/* V3 Studio — shared styles for plan, build, and publish screens. */

:root {
    --ink-900: #0b0f19;
    --ink-700: #1f2937;
    --ink-500: #4b5563;
    --ink-300: #9ca3af;
    --ink-100: #e5e7eb;
    --canvas: #fafafa;
    --surface: #ffffff;
    --accent: #4F46E5;
    --accent-soft: #eef2ff;
    --warn: #f59e0b;
    --danger: #dc2626;
    --success: #16a34a;
    --radius-lg: 16px;
    --radius-md: 12px;
    --radius-sm: 8px;
    --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06);
    --shadow-md: 0 4px 18px rgba(15, 23, 42, 0.08);
}

* { box-sizing: border-box; }

body.studio-body {
    margin: 0;
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
    background: var(--canvas);
    color: var(--ink-900);
    min-height: 100vh;
}
/* Build + plan screens run a fixed-viewport layout so each pane owns its
   own scrollbar instead of the whole page scrolling as one. Nav is fixed
   height (flex-shrink:0), main region takes remaining viewport. */
body.studio-body.no-page-scroll {
    height: 100vh;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}
body.studio-body.no-page-scroll > .studio-nav,
body.studio-body.no-page-scroll > .plan-nav { flex-shrink: 0; position: static; }
/* Step-2 (Preview) footer — Back / Save & Proceed buttons. Sticks
   to the viewport bottom inside the flex column; the build-layout
   above it claims the remaining space via flex: 1 1 auto. */
body.studio-body.no-page-scroll > .build-footer { flex-shrink: 0; }
body.studio-body.no-page-scroll > .build-layout,
body.studio-body.no-page-scroll > .plan-layout {
    flex: 1 1 auto;
    min-height: 0;
    height: auto;
}

a { color: var(--accent); text-decoration: none; }

.studio-nav {
    position: sticky;
    top: 0;
    /* Sits above the floating /plan TOC (z=40) so the avatar dropdown's
       bottom half (Your events / Profile / Org / Event hub / Sign out)
       isn't clipped by the TOC card on long viewports. The dropdown's
       inner z-index (70) is trapped in this nav's stacking context, so
       the nav itself has to outrank anything that floats over the
       content below — that means TOC, regen overlay (z=30), and other
       in-page floaters. Modals (z=9999) still cover the nav as expected. */
    z-index: 50;
    background: rgba(255, 255, 255, 0.92);
    backdrop-filter: blur(16px);
    border-bottom: 1px solid var(--ink-100);
    padding: 0 22px;
    /* three-column grid keeps stepper true-centred regardless of left/right widths */
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 24px;
    /* Fixed height — shared across /events, /studio/*, and /admin/platform/*
       so the three shells line up. Children size down to fit (see the
       .studio-nav .btn-publish-cta override below). */
    height: 64px;
}
.studio-nav > .studio-brand { justify-self: start; }
/* Brand + event-switcher cluster on the left of /build. Wraps the
   logo and the manage-event-switcher so the current event sits
   immediately next to the logo instead of being stranded in the
   right-hand nav cluster. `min-width: 0` lets the switcher's name
   span ellipsis cleanly when the event name is long. */
.studio-nav > .studio-brand-group {
    justify-self: start;
    display: inline-flex;
    align-items: center;
    gap: 14px;
    min-width: 0;
}
.studio-nav > .studio-nav-right { justify-self: end; }
.studio-brand {
    display: inline-flex;
    align-items: center;
    height: 36px;  /* bumped from 28 so the wordmark reads at nav scale */
}
.studio-brand img {
    height: 36px;
    width: auto;
    display: block;
    /* New PNG is already black-wordmark + red-dot on transparent — no
       invert needed. The old filter was for the previous white-logo PNG. */
}
.studio-nav-steps {
    display: flex;
    gap: 22px;
    font-size: 13px;
    color: var(--ink-500);
    flex-wrap: nowrap;
    justify-self: center;
}
.studio-nav-steps .step {
    position: relative;
    padding-bottom: 4px;
    text-decoration: none;
    color: inherit;
}
/* Done steps render as <a> — full clickable revisits. Muted colour
   matches the old span; subtle hover lift makes the affordance
   obvious on hover. */
.studio-nav-steps .step.done {
    color: var(--ink-700);
    cursor: pointer;
    transition: color 0.12s, border-color 0.12s;
    border-bottom: 1px solid transparent;
}
.studio-nav-steps .step.done:hover {
    color: var(--ink-900);
    border-bottom-color: var(--ink-300, #d4d4d8);
}
.studio-nav-steps .step.current { color: var(--ink-900); font-weight: 600; }
/* Pending = forward step the user hasn't reached yet. Inert, so a
   click into an empty Registrations / blocked Publish doesn't feel
   like the UI broke — the title= tooltip explains the gate. */
.studio-nav-steps .step.is-pending {
    color: var(--ink-400, #9ca3af);
    cursor: not-allowed;
}
.studio-nav-steps .step.current::after {
    content: '';
    position: absolute;
    left: 0; right: 0; bottom: -2px;
    height: 2px; border-radius: 2px;
    background: var(--accent);
}
.studio-nav-right { display: flex; align-items: center; gap: 12px; font-size: 13px; color: var(--ink-500); }
/* Marketing menu (Home / Pricing / Support) in the nav's centre slot —
   used on /v3/events, which has no step stepper there. Mirrors the
   public nav on /v3/ so the org can reach pricing + support without
   digging into the account dropdown. */
.studio-nav > .studio-nav-menu { justify-self: center; }
.studio-nav-menu {
    display: flex;
    align-items: center;
    gap: 26px;
}
.studio-nav-menu a {
    font-size: 14px;
    font-weight: 500;
    color: var(--ink-700, #475569);
    text-decoration: none;
}
.studio-nav-menu a:hover { color: var(--ink-900); }
@media (max-width: 760px) { .studio-nav-menu { display: none; } }
/* Admin entry — internal/platform-staff only. Special-cased as a
   distinct "internal tools" pill (accent border + shield glyph) so it
   never reads as a peer of the public menu items. */
.studio-nav-admin {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 600;
    color: #4f46e5;
    background: rgba(79, 70, 229, 0.08);
    border: 1px solid rgba(79, 70, 229, 0.22);
    border-radius: 999px;
    padding: 5px 11px;
    text-decoration: none;
    white-space: nowrap;
}
.studio-nav-admin:hover {
    background: rgba(79, 70, 229, 0.14);
    border-color: rgba(79, 70, 229, 0.40);
}
.studio-nav-admin svg { flex: 0 0 auto; }

/* ── Studio top-nav help pill ───────────────────────────────────────────
   Sits next to the dark Publish button. Light fill so it reads as a
   companion (not competing for attention) and so the eye still goes
   to Publish first. Hover lifts subtly. */
.studio-help-pill {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 7px 12px;
    background: #fff;
    color: var(--ink-700, #374151);
    border: 1px solid var(--ink-100, #e5e7eb);
    border-radius: 999px;
    font-size: 13px; font-weight: 500;
    text-decoration: none;
    transition: background 120ms ease, border-color 120ms ease,
                color 120ms ease, transform 120ms ease,
                box-shadow 120ms ease;
}
.studio-help-pill:hover {
    background: #f9fafb;
    color: var(--ink-900, #0b0f19);
    border-color: #d1d5db;
    transform: translateY(-1px);
    box-shadow: 0 4px 10px -4px rgba(15, 23, 42, 0.12);
}
.studio-help-pill:focus-visible {
    outline: none;
    border-color: #a5b4fc;
    box-shadow: 0 0 0 3px rgba(165, 180, 252, 0.35);
}
.studio-help-pill svg { color: var(--ink-500, #6b7280); flex-shrink: 0; }
.studio-help-pill:hover svg { color: var(--ink-900, #0b0f19); }
@media (max-width: 640px) {
    /* On narrow viewports, drop the label and keep just the icon
       so the right rail doesn't crowd the Publish button. */
    .studio-help-pill { padding: 7px 9px; }
    .studio-help-pill > span { display: none; }
}

/* ── Leave-confirm modal ───────────────────────────────────────────────── */
/* Same z-index logic as .publish-disclosure-overlay — sticky nav creates
   a stacking context that out-ranks z=100, so the leave-confirm scrim
   was also letting the nav's Back/Publish buttons read as clickable.
   9999 matches the documented modal convention. */
.leave-confirm-overlay {
    /* Same defensive pattern as .publish-disclosure-overlay above.
       Locks the floating-overlay properties past any later cascade. */
    position: fixed !important;
    top: 0 !important; right: 0 !important;
    bottom: 0 !important; left: 0 !important;
    inset: 0 !important;
    z-index: 9999 !important;
    display: flex !important;
    align-items: center !important; justify-content: center !important;
    background: rgba(11, 15, 25, 0.55);
    backdrop-filter: blur(4px);
    padding: 24px;
    margin: 0;
    width: auto; height: auto;
    transform: none;
    animation: leave-fade-in 0.15s ease-out;
}
@keyframes leave-fade-in { from { opacity: 0; } to { opacity: 1; } }
.leave-confirm-card {
    background: var(--surface);
    border-radius: var(--radius-lg);
    padding: 28px 28px 22px;
    max-width: 420px;
    width: 100%;
    box-shadow: 0 24px 60px rgba(15, 23, 42, 0.25);
    animation: leave-pop-in 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes leave-pop-in {
    from { opacity: 0; transform: translateY(6px) scale(0.98); }
    to { opacity: 1; transform: translateY(0) scale(1); }
}
.leave-confirm-card h3 {
    font-family: 'Instrument Serif', Georgia, serif;
    font-weight: 400;
    font-size: 26px;
    line-height: 1.15;
    letter-spacing: -0.02em;
    margin: 0 0 10px;
    color: var(--ink-900);
}
.leave-confirm-card p {
    font-size: 14px;
    color: var(--ink-500);
    line-height: 1.55;
    margin: 0 0 22px;
}
.leave-confirm-actions {
    display: flex;
    gap: 10px;
    justify-content: flex-end;
}
.leave-confirm-actions .btn-primary { padding: 10px 18px; font-size: 13px; }
.leave-confirm-actions .btn-ghost { padding: 10px 16px; font-size: 13px; }

/* ── Events list ───────────────────────────────────────────────────────── */
.events-main { max-width: 980px; margin: 0 auto; padding: 40px 32px 80px; }
.events-hero { margin-bottom: 32px; }
.events-hero h1 {
    font-family: 'Instrument Serif', Georgia, serif;
    font-weight: 400;
    font-size: 44px;
    letter-spacing: -0.02em;
    margin: 0 0 6px;
    color: var(--ink-900);
}
.events-hero p { color: var(--ink-500); font-size: 14px; margin: 0; }

/* ── Events toolbar (sort + view toggle) ──────────────────────────
   Sits above the list. Right-aligned so the eye tracks Title → list
   first, and the controls feel secondary. Single-row on desktop,
   stacks on narrow viewports. */
.events-toolbar {
    display: flex; align-items: center; justify-content: flex-end;
    gap: 12px; margin-bottom: 14px;
    flex-wrap: wrap;
}
.events-sort-label { display: inline-flex; align-items: center; gap: 8px; }
.events-sort-hint {
    font-size: 11px; font-weight: 600; letter-spacing: 0.5px;
    text-transform: uppercase; color: var(--ink-500);
}
.events-sort-select {
    appearance: none;
    background: #fff url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%236b7280' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat right 10px center;
    border: 1px solid var(--ink-100);
    border-radius: 8px;
    padding: 6px 28px 6px 10px;
    font-size: 13px; color: var(--ink-900);
    cursor: pointer;
    transition: border-color 0.12s;
}
.events-sort-select:hover { border-color: #c7d2fe; }
.events-sort-select:focus { outline: none; border-color: var(--accent, #4f46e5); }
/* Segmented view toggle — two buttons that share a border ring and
   a single active-fill that slides between them. Simpler than a pair
   of separate pills, and a familiar shape for list/card switchers. */
.events-view-toggle {
    display: inline-flex;
    border: 1px solid var(--ink-100);
    border-radius: 8px;
    background: #fff;
    padding: 2px;
}
.events-view-btn {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 5px 10px;
    font: inherit; font-size: 12.5px; font-weight: 500;
    color: var(--ink-500);
    background: transparent;
    border: 0;
    border-radius: 6px;
    cursor: pointer;
    transition: background 0.12s, color 0.12s;
}
.events-view-btn:hover { color: var(--ink-900); }
.events-view-btn.is-active {
    background: var(--ink-900);
    color: #fff;
}
.events-view-btn svg { opacity: 0.9; }

/* ── Cards (default) — full-width rows with bigger type + stats ─────
   List view was dropped; cards IS the compact list-like view. The
   banner markup below each card's <a> stays hidden here (tile-only)
   so the card reads as a single row. */
.events-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 12px; }
.events-list.view-cards { gap: 12px; }
.events-list.view-cards .event-card-link { padding: 18px 22px; }
.events-list.view-cards .event-stat span {
    display: block; font-size: 10px; text-transform: uppercase;
    letter-spacing: 0.08em;
}
/* Banner hidden in cards view — it's a tile-only affordance. */
.events-list.view-cards .event-card-banner { display: none; }

/* ── Tiles — grid of compact thumbnails, scan-friendly at scale ─────
   Every tile is a small card with title + meta + status. Stats live
   at the bottom as a one-line strip. 3 cols on desktop, 2 on tablet,
   1 on phone. The tile's <a> becomes flex-column so its footer sits
   at the bottom regardless of title length. */
.events-list.view-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 12px;
}
.events-list.view-tiles .event-card { display: flex; flex-direction: column; }
.events-list.view-tiles .event-card-link {
    display: flex; flex-direction: column;
    grid-template-columns: none;
    gap: 10px;
    padding: 14px 16px 12px;
    flex: 1;
}
.events-list.view-tiles .event-card-main h2 {
    font-size: 14.5px; margin: 0 0 6px;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.events-list.view-tiles .event-card-meta {
    font-size: 11px; gap: 8px;
    color: var(--ink-500);
}
.events-list.view-tiles .event-card-meta .pill {
    display: none;  /* Format pill noisy at tile size — keep meta dense. */
}
.events-list.view-tiles .event-card-stats {
    margin-top: auto;
    gap: 14px;
    padding-top: 10px;
    border-top: 1px dashed var(--ink-100);
}
.events-list.view-tiles .event-stat strong { font-size: 13px; }
.events-list.view-tiles .event-stat span {
    display: block; font-size: 9.5px; text-transform: uppercase;
    letter-spacing: 0.06em;
}
.events-list.view-tiles .event-card-status { margin-top: -2px; }
.events-list.view-tiles .event-card-actions { padding: 0 16px 12px; }

/* Tile banner — the top strip of each card in tile mode. Image when
   the event has a hero; deterministic gradient keyed off event.id
   when it doesn't (no stock photos, no fabricated URLs — pure CSS).
   Letter initial floats over the pattern so each tile still reads
   as "this event" while scanning. */
.event-card-banner {
    position: relative;
    height: 120px;
    border-radius: 12px 12px 0 0;
    overflow: hidden;
    background-size: cover;
    background-position: center;
    background-color: #e5e7eb;
}
.events-list.view-tiles .event-card-banner { display: block; }
.events-list.view-tiles .event-card { overflow: hidden; }
/* Subtle inset shadow on the bottom edge so the banner visually
   separates from the content without a hard line. */
.event-card-banner::after {
    content: "";
    position: absolute; inset: 0;
    box-shadow: inset 0 -18px 24px -20px rgba(11, 15, 25, 0.18);
}
.event-card-banner-initial {
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    font-family: 'Instrument Serif', Georgia, serif;
    font-size: 56px;
    color: rgba(255, 255, 255, 0.92);
    line-height: 1;
    text-shadow: 0 2px 16px rgba(11, 15, 25, 0.35);
    z-index: 1;
}
/* Eight deterministic gradient variants — event.id % 8 picks one.
   Palette mixes warm / cool / neutral pairs so a grid of adjacent
   tiles reads as intentional variety rather than random noise. */
.event-card-banner.is-pattern[data-seed="0"] { background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%); }
.event-card-banner.is-pattern[data-seed="1"] { background: linear-gradient(135deg, #0ea5e9 0%, #06b6d4 100%); }
.event-card-banner.is-pattern[data-seed="2"] { background: linear-gradient(135deg, #10b981 0%, #14b8a6 100%); }
.event-card-banner.is-pattern[data-seed="3"] { background: linear-gradient(135deg, #f59e0b 0%, #f97316 100%); }
.event-card-banner.is-pattern[data-seed="4"] { background: linear-gradient(135deg, #ec4899 0%, #f43f5e 100%); }
.event-card-banner.is-pattern[data-seed="5"] { background: linear-gradient(135deg, #6366f1 0%, #3b82f6 100%); }
.event-card-banner.is-pattern[data-seed="6"] { background: linear-gradient(135deg, #334155 0%, #0f172a 100%); }
.event-card-banner.is-pattern[data-seed="7"] { background: linear-gradient(135deg, #db2777 0%, #7c3aed 100%); }

.event-card {
    background: var(--surface);
    border: 1px solid var(--ink-100);
    border-radius: 12px;
    transition: border-color 0.12s, box-shadow 0.12s;
}
.event-card:hover { border-color: #c7d2fe; box-shadow: 0 4px 18px rgba(15, 23, 42, 0.06); }

.event-card-link {
    display: grid;
    grid-template-columns: 1fr auto auto;
    gap: 20px;
    align-items: center;
    padding: 18px 22px;
    text-decoration: none;
    color: inherit;
}
.event-card-main h2 {
    font-family: 'Inter', sans-serif;
    font-weight: 600;
    font-size: 16px;
    margin: 0 0 6px;
    color: var(--ink-900);
    letter-spacing: -0.01em;
}
.event-card-meta {
    display: flex; gap: 14px; flex-wrap: wrap; align-items: center;
    font-size: 12px; color: var(--ink-500);
}
.event-card-meta span { display: inline-flex; align-items: center; gap: 4px; }

.event-card-stats { display: flex; gap: 22px; align-items: center; }
.event-stat { text-align: center; }
.event-stat strong {
    display: block; font-size: 16px; font-weight: 600;
    color: var(--ink-900); font-variant-numeric: tabular-nums;
}
.event-stat span {
    display: block; font-size: 10px; text-transform: uppercase;
    letter-spacing: 0.08em; color: var(--ink-500);
}

.event-card-status { display: flex; align-items: center; }
.status-pill {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 4px 10px; border-radius: 999px;
    font-size: 12px; font-weight: 500;
}
.status-pill.status-live { background: #ecfdf5; color: #065f46; }
.status-pill.status-ended { background: #fef3c7; color: #92400e; }
.status-pill.status-draft { background: #f3f4f6; color: var(--ink-500); }
/* 2026-05-27 tester #7 — "In review" pill (indigo) sits between Live
   and Draft so an event awaiting admin approval reads distinctly,
   not as a plain Draft. */
.status-pill.status-in-review { background: #eef2ff; color: #3730a3; }
.status-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.status-live .status-dot { background: #10b981; box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.22); }
.status-ended .status-dot { background: #d97706; }
.status-in-review .status-dot { background: #6366f1; box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.22); }

.event-card-actions {
    padding: 0 22px 14px;
    display: flex; gap: 12px; align-items: center;
}

.events-empty {
    text-align: center;
    padding: 80px 32px;
    background: var(--surface);
    border: 1px dashed var(--ink-100);
    border-radius: 16px;
}
.events-empty-head {
    font-family: 'Instrument Serif', Georgia, serif;
    font-size: 32px; font-weight: 400;
    color: var(--ink-900);
    margin: 0 0 8px;
}
.events-empty-sub { color: var(--ink-500); font-size: 15px; margin: 0 0 24px; }

@media (max-width: 780px) {
    .event-card-link { grid-template-columns: 1fr; gap: 12px; }
    .event-card-stats { gap: 16px; }
}

/* ── Plan layout (chat left · summary right — each with own scroll) ────── */
.plan-layout {
    display: grid;
    /* 2 panes: chat · full-width questionnaire. Preview of the form
       lives on the /build step where builder + preview belong together.
       Plan is pure composition — one artifact at a time. Chat column
       width keyed off --plan-chat-w so the resizer can mutate it live,
       matching the /build screen's --chat-w pattern. */
    --plan-chat-w: 420px;
    --plan-chat-w-min: 280px;
    --plan-chat-w-max: 560px;
    grid-template-columns: var(--plan-chat-w) 1fr;
    height: calc(100vh - 56px);
    overflow: hidden;
    position: relative;  /* anchor for the .chat-resizer absolute element */
}
/* Chat-disabled variant — the /plan template now renders a static
   guidance panel in place of the chat (organisers were treating the
   chat as the primary input and ignoring the questionnaire). The
   guide column is a fixed-width sidebar without a draggable resizer,
   since there's nothing to resize anymore. */
.plan-layout.plan-layout-no-chat {
    --plan-chat-w: 420px;
    grid-template-columns: var(--plan-chat-w) 1fr;
}
@media (max-width: 900px) {
    .plan-layout.plan-layout-no-chat {
        grid-template-columns: 1fr;
        height: auto;
        overflow: visible;
    }
}
/* Tell the shared .chat-resizer which column width var to follow. Keeps
   one resizer CSS block working across /build and /plan without each
   layout hardcoding its own left-offset rule. */
.build-layout { --resizer-offset: var(--chat-w); }
.plan-layout  { --resizer-offset: var(--plan-chat-w); }
/* ── Plan guidance panel (replaces the chat on /plan) ─────────────────
   Static "step 2 of 5" overview that explains what the questionnaire
   on the right does. Same visual rhythm as the old chat sidebar
   (left-rule, fixed-width column, scrollable inner) so the page
   doesn't feel lopsided. Plain prose + numbered steps + a tip — no
   interactive controls, by design. */
.plan-guide {
    background: var(--surface);
    /* No right border — the column gap + the chat-resizer hover
       affordance carry the visual separation. The thin static line
       read as unnecessary chrome between the chat and the main pane. */
    height: 100%;
    overflow-y: auto;
    overflow-x: hidden;
    min-width: 0;
}
.plan-guide-inner {
    padding: 32px 28px 40px;
    max-width: 360px;
    margin: 0 auto;
}
.plan-guide-eyebrow {
    display: inline-block;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--accent, #4f46e5);
    background: rgba(79, 70, 229, 0.08);
    padding: 4px 10px;
    border-radius: 999px;
    margin-bottom: 14px;
}
.plan-guide-title {
    font-family: 'Instrument Serif', serif;
    font-size: 28px;
    font-weight: 400;
    color: var(--ink-900);
    line-height: 1.15;
    margin: 0 0 10px;
}
.plan-guide-sub {
    font-size: 14px;
    line-height: 1.55;
    color: var(--ink-600);
    margin: 0 0 28px;
}
.plan-guide-steps {
    list-style: none;
    margin: 0 0 24px;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 18px;
}
.plan-guide-steps li {
    display: flex;
    gap: 14px;
    align-items: flex-start;
}
.plan-guide-step-num {
    flex: 0 0 28px;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: #0b0f19;
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 600;
    margin-top: 1px;
}
.plan-guide-steps li > div {
    flex: 1; min-width: 0;
}
.plan-guide-steps strong {
    display: block;
    font-size: 14px;
    font-weight: 600;
    color: var(--ink-900);
    margin-bottom: 3px;
}
.plan-guide-steps p {
    margin: 0;
    font-size: 13px;
    line-height: 1.55;
    color: var(--ink-500);
}
.plan-guide-steps em {
    font-style: normal;
    font-weight: 600;
    color: var(--ink-700);
}
.plan-guide-tip {
    background: #fef9c3;
    border: 1px solid #fde68a;
    border-radius: 10px;
    padding: 12px 14px;
    margin-bottom: 24px;
}
.plan-guide-tip strong {
    display: block;
    font-size: 13px;
    color: #78350f;
    margin-bottom: 4px;
}
.plan-guide-tip p {
    margin: 0;
    font-size: 12.5px;
    line-height: 1.5;
    color: #92400e;
}
.plan-guide-tip em {
    font-style: normal;
    font-weight: 600;
}
.plan-guide-back {
    display: inline-block;
    font-size: 13px;
    color: var(--ink-500);
    text-decoration: none;
}
.plan-guide-back:hover {
    color: var(--ink-900);
    text-decoration: underline;
}

.plan-chat {
    background: var(--surface);
    border-right: 1px solid var(--ink-100);
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
    height: 100%;
    overflow: hidden;
}
.plan-layout > .plan-main {
    min-height: 0;
    overflow-y: auto;
    max-width: 100%;
}
.plan-chat-header {
    padding: 14px 18px;
    border-bottom: 1px solid var(--ink-100);
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-shrink: 0;
}
.plan-chat-body {
    flex: 1;
    overflow-y: auto;
    padding: 18px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/* ── Plan screen ───────────────────────────────────────────────────────── */
/* Flush-left layout: removed the historical `margin: 0 auto` centering
   so the questionnaire starts right after the chat divider instead of
   floating in the middle of the column with a fat empty gutter on its
   left. max-width still caps line length on huge monitors; padding is
   responsive (see breakpoints below). */
.plan-main { max-width: 880px; margin: 0; padding: 40px 40px 80px; }
.plan-hero { margin-bottom: 32px; }
.plan-tag {
    display: inline-block;
    padding: 4px 10px;
    border-radius: 999px;
    background: var(--accent-soft);
    color: var(--accent);
    font-size: 12px;
    font-weight: 600;
    margin-bottom: 16px;
    letter-spacing: 0.02em;
}
.plan-title {
    font-size: 36px;
    font-weight: 600;
    letter-spacing: -0.02em;
    line-height: 1.15;
    margin: 0 0 10px;
}
.plan-sub { color: var(--ink-500); font-size: 16px; line-height: 1.6; max-width: 640px; margin: 0 0 22px; }
.plan-prompt {
    border: 1px solid var(--ink-100);
    border-left: 4px solid var(--accent);
    background: var(--surface);
    border-radius: var(--radius-md);
    padding: 14px 18px;
    font-size: 14px;
    color: var(--ink-700);
}
.plan-prompt-label {
    font-size: 11px; color: var(--ink-500); text-transform: uppercase; letter-spacing: 0.1em;
}
.plan-prompt p { margin: 6px 0 0; line-height: 1.5; }

.plan-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 18px;
    margin-bottom: 28px;
}
.plan-card {
    background: var(--surface);
    border: 1px solid var(--ink-100);
    border-radius: var(--radius-lg);
    padding: 22px 22px 20px;
    box-shadow: var(--shadow-sm);
}
.plan-card h2 {
    font-size: 14px;
    font-weight: 600;
    color: var(--ink-500);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    margin: 0 0 14px;
}
.plan-card-wide { grid-column: span 2; }
.plan-card-hint { color: var(--ink-500); font-size: 13px; margin: -6px 0 12px; }

.plan-dl { display: grid; grid-template-columns: 120px 1fr; gap: 8px 18px; margin: 0; font-size: 14px; }
.plan-dl dt { color: var(--ink-500); }
.plan-dl dd { margin: 0; color: var(--ink-900); display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }

.pill {
    display: inline-block;
    padding: 3px 10px;
    border-radius: 999px;
    background: var(--accent-soft);
    color: var(--accent);
    font-size: 12px;
    font-weight: 500;
}
.pill.ghost { background: #f3f4f6; color: var(--ink-700); }
.pill.required { background: #fef3c7; color: #92400e; }

.plan-tickets { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.plan-tickets li {
    border: 1px solid var(--ink-100);
    border-radius: var(--radius-md);
    padding: 14px 16px;
    background: #fafbff;
}
.plan-ticket-head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.plan-ticket-name { font-weight: 600; }
.plan-ticket-price { color: var(--accent); font-weight: 600; }
.plan-ticket-desc { font-size: 13px; color: var(--ink-500); margin: 6px 0 0; line-height: 1.5; }
.plan-ticket-meta { display: flex; gap: 10px; font-size: 12px; color: var(--ink-500); margin-top: 8px; }

.plan-fields { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 10px; }
.plan-field {
    border: 1px solid var(--ink-100);
    border-radius: var(--radius-md);
    padding: 12px 14px;
    background: var(--surface);
}
.plan-field-head { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.plan-field-label { font-weight: 600; font-size: 14px; }
.plan-field-meta { display: flex; gap: 6px; margin-top: 6px; }
.plan-field-opts { font-size: 12px; color: var(--ink-500); margin: 8px 0 0; }

.plan-visual { display: flex; align-items: center; gap: 16px; }
.plan-swatch { width: 80px; height: 80px; border-radius: 16px; box-shadow: var(--shadow-sm); }
.plan-visual p { margin: 4px 0; font-size: 14px; color: var(--ink-700); }
.plan-visual code { background: #f3f4f6; padding: 2px 8px; border-radius: 6px; font-size: 12px; }

.plan-empty { color: var(--ink-500); font-size: 13px; margin: 0; }

.plan-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    padding-top: 8px;
}

/* ── Plan confirmation (simplified right panel) ────────────────────────── */
.plan-confirm {
    min-height: 0;
    overflow-y: auto;
    display: flex;
    justify-content: center;
    padding: 56px 32px 80px;
}
.plan-confirm-inner { max-width: 680px; width: 100%; }
.plan-confirm-title {
    font-family: 'Instrument Serif', Georgia, serif;
    font-weight: 400;
    font-size: 44px;
    line-height: 1.05;
    letter-spacing: -0.02em;
    color: var(--ink-900);
    margin: 10px 0 14px;
}
.plan-confirm-sub {
    font-size: 16px;
    color: var(--ink-500);
    line-height: 1.6;
    margin-bottom: 28px;
}
.plan-confirm-cta {
    display: flex;
    gap: 14px;
    align-items: center;
    flex-wrap: wrap;
    margin-bottom: 48px;
    padding-bottom: 32px;
    border-bottom: 1px solid var(--ink-100);
}
.plan-confirm-note {
    font-size: 13px;
    color: var(--ink-500);
    max-width: 300px;
    line-height: 1.45;
}
.btn-huge {
    padding: 16px 32px;
    font-size: 16px;
    letter-spacing: 0.005em;
    box-shadow: 0 8px 24px rgba(11, 15, 25, 0.12);
}
.btn-huge:hover { box-shadow: 0 12px 32px rgba(11, 15, 25, 0.16); transform: translateY(-1px); }

.plan-confirm-section { margin-bottom: 32px; }
.plan-confirm-section h3 {
    font-size: 11px;
    font-weight: 700;
    color: var(--ink-500);
    text-transform: uppercase;
    letter-spacing: 0.12em;
    margin-bottom: 12px;
    display: flex;
    align-items: center;
    gap: 8px;
}
.plan-confirm-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.plan-confirm-list li {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    background: var(--surface);
    border: 1px solid var(--ink-100);
    border-radius: 10px;
    font-size: 14px;
    color: var(--ink-900);
    flex-wrap: wrap;
}
.plan-confirm-list li strong { font-weight: 600; flex: 1; }
.plan-confirm-list li p {
    flex: 1 1 100%;
    margin: 4px 0 0;
    font-size: 13px;
    color: var(--ink-500);
    line-height: 1.45;
}
.plan-ro-price {
    font-weight: 600;
    color: var(--accent);
    font-variant-numeric: tabular-nums;
}

/* ── Plan hero CTA ─────────────────────────────────────────────────────── */
.plan-hero-actions {
    margin-top: 20px;
    display: flex;
    gap: 16px;
    align-items: center;
    flex-wrap: wrap;
}
.plan-hero-hint {
    font-size: 13px;
    color: var(--ink-500);
    max-width: 320px;
    line-height: 1.45;
}
.btn-large { padding: 13px 24px; font-size: 15px; letter-spacing: 0.01em; }

/* ── Plan h2 inline add button ─────────────────────────────────────────── */
.plan-card h2 {
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.plan-add-link {
    font-size: 11px;
    font-weight: 600;
    color: var(--accent);
    background: var(--accent-soft);
    border: none;
    padding: 3px 10px;
    border-radius: 999px;
    cursor: pointer;
    text-transform: none;
    letter-spacing: normal;
}
.plan-add-link:hover { filter: brightness(0.97); }

/* ── Editable event dl ─────────────────────────────────────────────────── */
.plan-dl-editable {
    grid-template-columns: 110px 1fr;
    align-items: center;
    gap: 10px 18px;
}
.plan-dl-editable dt { font-size: 12px; padding-top: 6px; }
.plan-input {
    width: 100%;
    border: 1px solid transparent;
    background: transparent;
    padding: 6px 8px;
    border-radius: 6px;
    font-family: inherit;
    font-size: 14px;
    color: var(--ink-900);
    transition: border-color 0.12s, background 0.12s;
}
.plan-input:hover { border-color: var(--ink-100); background: var(--surface); }
.plan-input:focus {
    outline: none;
    border-color: var(--accent);
    background: var(--surface);
    box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.12);
}
.plan-input-name { font-weight: 600; font-size: 15px; }
.plan-input-select { cursor: pointer; }
.plan-dates { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.plan-arrow { color: var(--ink-500); font-size: 12px; padding: 0 2px; }
.plan-add-end, .plan-remove-end {
    font-size: 12px; color: var(--ink-500);
    background: transparent; border: 1px dashed var(--ink-100);
    padding: 5px 10px; border-radius: 6px; cursor: pointer;
}
.plan-add-end:hover { color: var(--accent); border-color: var(--accent); }
.plan-remove-end {
    border-radius: 50%; width: 22px; height: 22px;
    padding: 0; display: inline-flex; align-items: center; justify-content: center;
    border-style: solid;
}
.plan-remove-end:hover { color: var(--danger); border-color: var(--danger); }

/* ── Editable tickets ──────────────────────────────────────────────────── */
.plan-tickets-editable li.plan-ticket-edit {
    display: flex; flex-direction: column; gap: 6px;
    padding: 12px 14px;
}
.plan-ticket-row { display: flex; gap: 8px; align-items: center; }
.plan-price-input {
    display: inline-flex; align-items: center;
    background: var(--surface); border: 1px solid var(--ink-100);
    border-radius: 6px; overflow: hidden; width: 112px;
    flex-shrink: 0;
}
.plan-price-symbol { padding: 0 6px 0 10px; color: var(--ink-500); font-size: 13px; }
.plan-price-input input {
    border: none; outline: none; width: 100%;
    padding: 7px 8px 7px 0;
    font-size: 13px; color: var(--ink-900);
    background: transparent;
    font-family: inherit;
}
.plan-ticket-edit .plan-input-desc {
    font-size: 13px; color: var(--ink-500);
}
.plan-row-delete {
    flex-shrink: 0;
    width: 26px; height: 26px;
    border: 1px solid var(--ink-100);
    background: var(--surface);
    color: var(--ink-500);
    border-radius: 50%;
    font-size: 16px;
    cursor: pointer;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: color 0.12s, border-color 0.12s;
}
.plan-row-delete:hover { color: var(--danger); border-color: var(--danger); }

/* ── Editable fields ───────────────────────────────────────────────────── */
.plan-fields-editable { display: flex; flex-direction: column; gap: 6px; }
.plan-field-edit {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border: 1px solid var(--ink-100);
    border-radius: 10px;
    background: var(--surface);
    transition: border-color 0.12s;
}
.plan-field-edit:hover { border-color: #c7d2fe; }
.plan-field-edit .plan-field-label-input {
    flex: 1; font-weight: 500; font-size: 14px;
}
.plan-field-edit .toggle { flex-shrink: 0; }
.plan-field-edit .plan-field-type {
    flex-shrink: 0;
    text-transform: lowercase;
    font-variant: all-small-caps;
    letter-spacing: 0.04em;
}

.btn-primary {
    background: var(--ink-900);
    color: #fff;
    border: none;
    padding: 12px 22px;
    border-radius: var(--radius-md);
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    transition: transform 0.08s, opacity 0.15s;
}
.btn-primary:hover { opacity: 0.94; }
.btn-primary:active { transform: scale(0.985); }
.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-ghost {
    color: var(--ink-500);
    background: transparent;
    border: 1px solid var(--ink-100);
    padding: 11px 18px;
    border-radius: var(--radius-md);
    font-size: 14px;
    cursor: pointer;
}
.btn-ghost:hover { background: #f3f4f6; }

/* ── Build screen ──────────────────────────────────────────────────────── */
.build-layout {
    display: grid;
    /* Chat column width comes from a custom property so the resizer can
       mutate it live (JS writes --chat-w on this element, CSS binds it
       to grid-template-columns). Defaults to 420px to match the v1
       reference width; min/max cap how far drag can go. */
    --chat-w: 420px;
    --chat-w-min: 300px;
    --chat-w-max: 640px;
    /* Tweaks-drawer width — same custom-property pattern as --chat-w
       so the right-edge resizer can mutate it live (JS writes
       --tweaks-w on this element). Default 420 matches the v1 width;
       360 min keeps dense form-field rows readable; 720 max stops the
       drawer from eating the entire preview. */
    --tweaks-w: 420px;
    --tweaks-w-min: 360px;
    --tweaks-w-max: 720px;
    grid-template-columns: var(--chat-w) 1fr;
    gap: 0;
    height: calc(100vh - 56px);    /* fixed, so each pane owns its own scroll */
    overflow: hidden;
    position: relative;             /* anchor for the drawer + resizer */
}

/* Draggable split between chat + preview. 6px hit-zone, 1px visible
   rule unless hovered/dragging. Sits centred on the grid gap so the
   click target spans both sides of the column border. */
.chat-resizer {
    position: absolute;
    top: 0;
    left: var(--resizer-offset, var(--chat-w, 420px));
    width: 10px;
    height: 100%;
    margin-left: -5px;
    z-index: 15;
    cursor: col-resize;
    user-select: none;
}
.chat-resizer::after {
    content: '';
    position: absolute;
    top: 0; bottom: 0;
    left: 4px; width: 2px;
    background: transparent;
    border-radius: 2px;
    transition: background 0.12s;
}
.chat-resizer:hover::after,
.chat-resizer.dragging::after {
    background: rgba(79, 70, 229, 0.45);
}
body.chat-resizing { cursor: col-resize; user-select: none; }
body.chat-resizing iframe { pointer-events: none; }  /* stop iframe from eating mousemove */

/* Tweaks-pane resizer — mirror of .chat-resizer but anchored to the
   LEFT edge of the Tweaks drawer (.build-pane.builder · position:
   absolute · right: 0). Dragging left widens the pane, dragging
   right narrows it. JS writes --tweaks-w on .build-layout; .builder's
   width: var(--tweaks-w, 420px) picks it up live. Only enabled at
   ≥1200 px (where Tweaks is inline) — see the media-query gate
   further down that hides the handle in the 1024-1199 drawer band. */
.builder-resizer {
    position: absolute;
    top: 0;
    right: var(--tweaks-w, 420px);
    width: 10px;
    height: 100%;
    margin-right: -5px;
    z-index: 25;                /* above .builder (z:20), below modals */
    cursor: col-resize;
    user-select: none;
}
.builder-resizer::after {
    content: '';
    position: absolute;
    top: 0; bottom: 0;
    right: 4px; width: 2px;
    background: transparent;
    border-radius: 2px;
    transition: background 0.12s;
}
.builder-resizer:hover::after,
.builder-resizer.dragging::after {
    background: rgba(79, 70, 229, 0.45);
}
/* When the Tweaks drawer is closed (data-advanced="off"), the pane is
   off-screen — hide the resizer so it doesn't sit on the right edge
   waiting for an invisible drawer. */
.build-layout[data-advanced="off"] .builder-resizer { display: none; }
body.tweaks-resizing { cursor: col-resize; user-select: none; }
body.tweaks-resizing iframe { pointer-events: none; }
.build-pane {
    border-right: 1px solid var(--ink-100);
    background: var(--surface);
    min-width: 0;
    min-height: 0;                  /* let grid child shrink so inner scroll works */
    overflow: hidden;
    display: flex;
    flex-direction: column;
}
.build-pane:last-child { border-right: none; }
/* Chat panel has no right border — the chat-resizer's hover affordance
   carries the divider role on demand. The static 1px line read as
   unnecessary chrome between the chat and the preview. The preview→
   tweaks divider keeps its border because the tweaks drawer slides
   over the preview and needs the hard edge for separation. */
.build-pane.chat { border-right: none; }

/* Advanced panel — slides in from the right over the preview pane.
   Floats instead of occupying a grid column so the preview iframe
   doesn't reflow when the drawer opens. Shadow on the left edge
   provides separation without a hard border. */
.build-pane.builder {
    position: absolute;
    top: 0;
    right: 0;
    height: 100%;
    width: var(--tweaks-w, 420px);
    max-width: 100%;
    z-index: 20;
    border-left: 1px solid var(--ink-100);
    border-right: none;
    box-shadow: -12px 0 32px -16px rgba(15, 23, 42, 0.18);
    transform: translateX(100%);
    transition: transform 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}
.build-layout[data-advanced="on"] .build-pane.builder {
    transform: translateX(0);
}
/* 2026-05-24 — the legacy peek state (drawer auto-opens for ~2s
   on every visit) has been retired. The toggle button now flashes
   in place via .btn-tweaks-flash; the drawer state stays untouched.
   The .build-layout.tweaks-peeking selector is preserved for any
   in-flight DOM that still carries the class, but the rule is now
   inert. */
.build-layout.tweaks-peeking .build-pane.builder {
    transition: transform 0.34s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Field-pulse for chat-guided uploads + picker openings.
   2026-05-22 systemic rule: when the chat opens the Tweaks panel
   at a specific field ("change the event logo"), the target gets
   a 2.4s pulse so the organiser's eye lands on it. The
   visual matches the existing tweaks-peek-pulse so the language
   stays coherent across the studio canvas. */
.tweaks-flash {
    animation: tweaks-flash-pulse 0.85s ease-out 3;
    border-radius: 8px;
}
@keyframes tweaks-flash-pulse {
    0%   { box-shadow: 0 0 0 0 rgba(139, 92, 246, 0.55); background-color: rgba(139, 92, 246, 0.12); }
    100% { box-shadow: 0 0 0 14px rgba(139, 92, 246, 0); background-color: rgba(139, 92, 246, 0); }
}
/* 2026-05-24 — `.btn-tweaks-flash` is applied by wireTweaksToggle()
   on build-page entry (when the drawer stayed closed). Replaces the
   prior `.tweaks-peeking` parent-class rule that paired the pulse
   with an auto-opened drawer — the drawer no longer opens, so the
   button's flash is the sole "your panel of fine controls is here"
   affordance. Two cycles of soft-indigo box-shadow halo around the
   pill, ~1.1s each, total ~2.2s. Respects prefers-reduced-motion. */
@keyframes btn-tweaks-flash-pulse {
    0%   { box-shadow: 0 0 0 0   rgba(139, 92, 246, 0.55); }
    25%  { box-shadow: 0 0 0 6px rgba(139, 92, 246, 0.32); }
    100% { box-shadow: 0 0 0 12px rgba(139, 92, 246, 0); }
}
#btn-toggle-advanced.btn-tweaks-flash {
    animation: btn-tweaks-flash-pulse 1.1s ease-out 2;
    /* Soft tint behind the pill so the pulse reads even when the
       indigo halo blends into adjacent UI. */
    background: rgba(139, 92, 246, 0.10);
    border-color: rgba(139, 92, 246, 0.45);
    color: #5b21b6;
    position: relative;
    z-index: 1;
}
@media (prefers-reduced-motion: reduce) {
    #btn-toggle-advanced.btn-tweaks-flash {
        animation: none;
        /* Keep the static tint so the affordance is still visible. */
    }
}

/* Toggle button label swap — two spans, one shown per state. */
.build-layout[data-advanced="on"] #btn-toggle-advanced .adv-toggle-open { display: none; }
.build-layout[data-advanced="off"] #btn-toggle-advanced .adv-toggle-close { display: none; }
.build-layout[data-advanced="on"] #btn-toggle-advanced {
    background: var(--ink-900);
    color: #fff;
    border-color: var(--ink-900);
}
/* 2026-05-29 user dip-test: Tweaks button blended into the toolbar
   alongside the Refresh ghost-button — organisers didn't notice it
   existed. Highlight the button in its CLOSED state (the call-to-
   action moment) with the accent fill, white text, and a subtle
   shadow so it reads as "click me to fine-tune." When the drawer
   opens (data-advanced="on") the rule above takes over with the
   neutral ink-900 fill — emphasis no longer needed because the
   panel itself is now visible. */
.build-layout[data-advanced="off"] #btn-toggle-advanced {
    background: var(--accent);
    color: #fff;
    border-color: var(--accent);
    box-shadow: 0 1px 2px rgba(79, 70, 229, 0.18),
                0 4px 14px -4px rgba(79, 70, 229, 0.35);
    font-weight: 600;
}
.build-layout[data-advanced="off"] #btn-toggle-advanced:hover {
    background: #4338ca;
    border-color: #4338ca;
}

.pane-header {
    padding: 14px 18px;
    border-bottom: 1px solid var(--ink-100);
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    flex-shrink: 0;
}
.pane-title { font-size: 13px; font-weight: 600; color: var(--ink-700); text-transform: uppercase; letter-spacing: 0.08em; }
.pane-body { flex: 1; overflow-y: auto; padding: 18px; min-height: 0; }

/* Chat pane — targets both id-based wrappers (#chat-messages on /build
   and #plan-chat-messages on /plan) + the legacy .chat-messages class,
   so messages always have breathing room. Earlier the /build wrapper
   only had .pane-body so bubbles stacked with zero gap. */
.chat-messages,
#chat-messages,
#plan-chat-messages {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding-bottom: 8px;
}
/* v1-inspired bubble layout: avatar circle + rounded pill beside it.
   The .chat-msg element is a flex row; addChatMsg / addPlanMsg build
   it as <div class="chat-msg {who}"><span class="chat-msg-avatar">..</span>
   <span class="chat-msg-bubble">text</span></div>. User messages flow
   right (row-reverse), AI messages flow left. Slight corner-trim on the
   bubble adjacent to the avatar mimics the v1 speech-bubble feel. */
.chat-msg {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    max-width: 94%;
    background: transparent;
    padding: 0;
    border-radius: 0;
    border: 0;
}
.chat-msg.user { align-self: flex-end; flex-direction: row-reverse; }
.chat-msg.ai   { align-self: flex-start; }

/* 2026-06-02 — chat-skeleton bubbles. Server-renders 3 placeholder
   pulses inside #chat-messages when there's no pre-rendered history
   yet (brand-new event), so the panel never paints blank between
   page-load and the loadChatHistory() fetch resolving. JS clears
   these on hydration. Layout mirrors a real bubble so the swap
   doesn't visibly reflow. */
.chat-msg.chat-skeleton {
    flex-direction: column;
    gap: 6px;
    padding: 10px 14px;
    background: #f3f4f6;
    border-radius: 12px;
    max-width: 220px;
    opacity: 0.6;
    animation: chat-skel-pulse 1.4s ease-in-out infinite;
}
.chat-msg.chat-skeleton.user {
    align-self: flex-end;
    background: #e5e7eb;
}
.chat-msg.chat-skeleton .chat-skeleton-line {
    display: block;
    height: 9px;
    border-radius: 5px;
    background: linear-gradient(90deg, #e5e7eb 0%, #f3f4f6 50%, #e5e7eb 100%);
    background-size: 200% 100%;
    animation: chat-skel-shimmer 1.6s linear infinite;
}
.chat-msg.chat-skeleton.user .chat-skeleton-line {
    background: linear-gradient(90deg, #d1d5db 0%, #e5e7eb 50%, #d1d5db 100%);
    background-size: 200% 100%;
}
@keyframes chat-skel-pulse {
    0%, 100% { opacity: 0.55; }
    50%      { opacity: 0.85; }
}
@keyframes chat-skel-shimmer {
    0%   { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}
/* Respect reduced-motion users — keep the bubbles visible but stop
   the shimmer so it isn't distracting / accessibility-hostile. */
@media (prefers-reduced-motion: reduce) {
    .chat-msg.chat-skeleton,
    .chat-msg.chat-skeleton .chat-skeleton-line {
        animation: none;
    }
}

.chat-msg-avatar {
    width: 32px; height: 32px;
    flex-shrink: 0;
    border-radius: 999px;
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 700;
    letter-spacing: 0.02em;
}
.chat-msg.user .chat-msg-avatar {
    background: var(--ink-900); color: #fff;
    font-size: 11px;
}
.chat-msg.ai .chat-msg-avatar {
    background: #eef0f6; color: var(--ink-900);
    font-size: 16px;                 /* Fallback letter "e" dominates the circle */
    font-weight: 700;
    border: 1px solid var(--ink-100);
}
/* Image-backed variant for the e2m AI avatar — the standalone "e" mark
   fills the circle edge-to-edge. object-fit: cover + 100% size means
   the mark crops to fill regardless of source aspect ratio, so the
   avatar reads as the brand icon at 32px rather than a small logo
   floating inside a mostly-empty tile. */
.chat-msg-avatar.chat-msg-avatar-logo {
    background: #fff;
    padding: 0;
    overflow: hidden;
}
.chat-msg-avatar.chat-msg-avatar-logo img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    /* Source PNGs often ship with a safety margin baked in; scaling up
       inside the clipped circle zooms past that whitespace so the mark
       reads at full strength at 32px. The parent's `overflow: hidden`
       keeps it pinned to the circle. */
    transform: scale(1.4);
}
/* User-photo variant — same hook as the logo above but WITHOUT the
   1.4x scale, since profile photos are already full-bleed faces
   (zooming in would crop heads). object-fit: cover keeps non-square
   photos centred + cropped to the circle. */
.chat-msg-avatar.chat-msg-avatar-photo {
    background: #fff;
    padding: 0;
    overflow: hidden;
    border-color: var(--ink-100);
}
.chat-msg-avatar.chat-msg-avatar-photo img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.chat-msg-bubble {
    padding: 12px 16px;
    border-radius: 20px;
    font-size: 14.5px;
    line-height: 1.55;
    white-space: pre-wrap;
    word-break: break-word;
    max-width: 100%;
}

/* Voice-channel badge — a small "🎤" chip rendered as a CSS
   pseudo-element on the bubble so voice turns read as visually
   distinct from typed chat. The Realtime persistence path stores
   assistant voice content with a literal "🎤 " prefix (see
   /voice-author/log); loadChatHistory strips that prefix and
   adds the .is-voice class instead, so the badge renders cleanly
   instead of as inline emoji-text. The 2026-05-18 organiser
   report flagged the literal-emoji rendering as confusing. */
.chat-msg.is-voice .chat-msg-bubble::before {
    content: '🎤';
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    margin-right: 8px;
    margin-left: -2px;
    background: rgba(99, 102, 241, 0.12);
    color: #4f46e5;
    border-radius: 999px;
    font-size: 12px;
    line-height: 1;
    vertical-align: middle;
    flex-shrink: 0;
}
.chat-msg.is-voice.user .chat-msg-bubble::before {
    background: rgba(255, 255, 255, 0.16);
    color: #fff;
}
.chat-msg.user .chat-msg-bubble {
    background: var(--ink-900);
    color: #fff;
    border-bottom-right-radius: 6px;
}
.chat-msg.ai .chat-msg-bubble {
    background: #f4f4f6;
    color: var(--ink-900);
    border-bottom-left-radius: 6px;
}
.chat-msg.ai.muted .chat-msg-bubble { font-style: italic; color: var(--ink-500); }

/* Awaiting-input affordance for clarification bubbles. Pulled in by
   the chat handler when ChatResult.awaiting_user_input is true —
   draws attention to the assistant's "could you rephrase?" / "do
   you want X or Y?" without the rotating ring that earlier read as
   a loading state.

   Now: a static gradient ring around the whole bubble (indigo →
   violet → blue) so the WHOLE box visibly says "this needs your
   input". Implementation pattern is the standard padded gradient
   border with mask-composite — the gradient fills a ::before
   layer, and a content-box mask carves out the interior so only
   the border edge paints. No rotation, no flicker; one short
   entry pulse to draw the eye, then the ring stays static.

   prefers-reduced-motion drops the entry pulse, keeps the static
   ring. Fallback for browsers without mask-composite (very rare):
   a solid indigo border via @supports negation. */
/* Awaiting-input bubble — banner-card pattern adapted from the Eva
   voice-builder UI (2026-05-22 user reference): a light-blue tinted
   card with a "Eva is asking — answer to continue:" header band at
   the top and a "● Agent is waiting…" strip with a pulsing dot at
   the bottom. The bubble's normal content sits between them as the
   card body. Stronger affordance than the prior subtle gradient
   ring — the user can't miss that input is expected.
   .chat-msg-bubble.is-awaiting-input owns the card chrome; the
   bubble's natural padding gives the card body its breathing room. */
.chat-msg.ai .chat-msg-bubble.is-awaiting-input {
    position: relative;
    background: rgba(99, 102, 241, 0.04);
    color: var(--ink-900);
    border: 1px solid rgba(99, 102, 241, 0.22);
    border-radius: 12px;
    /* Bubble's default padding is ~10-14px from the AI bubble rule
       above. Leave room for the inline header + footer bands which
       carry their own padding via negative margins below. */
    padding: 14px;
}
/* Top banner — "Eva is asking — answer to continue:". Negative
   horizontal + top margin makes the band span the full width of
   the bubble by absorbing the parent's padding. */
.chat-msg.ai .chat-msg-bubble.is-awaiting-input::before {
    content: '?  Eva is asking — answer to continue:';
    display: block;
    margin: -14px -14px 12px;
    padding: 9px 14px;
    background: rgba(99, 102, 241, 0.10);
    border-bottom: 1px solid rgba(99, 102, 241, 0.18);
    border-radius: 12px 12px 0 0;
    font-size: 12.5px;
    font-weight: 600;
    letter-spacing: 0.01em;
    color: #4f46e5;
}
/* Bottom strip — "● Agent is waiting…". Dot pulses gently to draw
   attention; the strip mirrors the top banner's tint so the card
   reads as a single unit. */
.chat-msg.ai .chat-msg-bubble.is-awaiting-input::after {
    content: '●  Agent is waiting…';
    display: block;
    margin: 12px -14px -14px;
    padding: 8px 14px;
    background: rgba(99, 102, 241, 0.10);
    border-top: 1px solid rgba(99, 102, 241, 0.18);
    border-radius: 0 0 12px 12px;
    font-size: 12px;
    font-weight: 500;
    color: #4f46e5;
    animation: chat-await-dot-pulse 1.4s ease-in-out infinite;
}
@keyframes chat-await-dot-pulse {
    0%, 100% { opacity: 0.78; }
    50%      { opacity: 1; }
}

/* Composer-side echo of the waiting state — pulses the textarea
   border in matching indigo so the organiser's eye lands on the
   reply input. Triggered by .is-awaiting-input on the .build-pane.chat
   pane (set in build.html's chat-form submit handler). */
.build-pane.chat.is-awaiting-input #chat-textarea {
    border-color: #6366f1 !important;
    box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.18) !important;
    animation: chat-await-textarea-pulse 1.6s ease-in-out infinite;
}
@keyframes chat-await-textarea-pulse {
    0%, 100% { box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.18); }
    50%      { box-shadow: 0 0 0 5px rgba(99, 102, 241, 0.30); }
}
@media (prefers-reduced-motion: reduce) {
    .chat-msg.ai .chat-msg-bubble.is-awaiting-input::after,
    .build-pane.chat.is-awaiting-input #chat-textarea {
        animation: none;
    }
}

/* ── Markdown rendering inside chat bubbles ────────────────────────
   The chat handler now passes the assistant's reply through a tiny
   markdown formatter (build.html : renderMarkdownInline) so **bold**,
   *italic*, lists, code, and links render properly instead of leaking
   raw asterisks. Below: the typography for those rendered elements,
   scoped to .chat-msg-bubble so no other surface inherits.

   Conservative palette — same ink colours as the bubble body so the
   visual hierarchy is "bold = emphasis", not "bold = different
   colour". Lists use compact margins so the bubble doesn't grow
   tall on a 3-bullet ask. */
.chat-msg-bubble strong { font-weight: 600; color: inherit; }
.chat-msg-bubble em { font-style: italic; }
.chat-msg-bubble code {
    font-family: ui-monospace, SFMono-Regular, Consolas, Menlo, monospace;
    font-size: 0.92em;
    background: rgba(15, 23, 42, 0.06);
    color: var(--ink-900);
    padding: 1px 5px;
    border-radius: 4px;
}
.chat-msg.user .chat-msg-bubble code {
    background: rgba(255, 255, 255, 0.18);
    color: #fff;
}
.chat-msg-bubble a {
    color: var(--accent, #4f46e5);
    text-decoration: underline;
    text-underline-offset: 2px;
}
.chat-msg-bubble ul,
.chat-msg-bubble ol {
    margin: 6px 0 6px 0;
    padding-left: 22px;
}
.chat-msg-bubble li { margin: 2px 0; }
.chat-msg-bubble p { margin: 0 0 8px; }
.chat-msg-bubble p:last-child { margin-bottom: 0; }

/* Comfy chat composer — generous padding, multiline by default, voice
   button for hands-free prompting. Shared by /plan and /build. */
/* Build-page chat composer — deliberately mirrors .plan-composer so the
   two pages feel like one continuous workspace. Same white card, same
   shadow, same gradient send pill, same voice-hero bar at the bottom.
   The textarea loses its own border (the card IS the container). */
.chat-input {
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 0;
    flex-shrink: 0;
    background: #fff;
    border: 1px solid rgba(11, 15, 25, 0.06);
    border-top: none;          /* card sits flush against the chat panel */
    border-radius: 0 0 14px 14px;
    box-shadow:
        0 0 0 6px rgba(15, 23, 42, 0.03),
        0 4px 16px rgba(15, 23, 42, 0.05),
        0 24px 56px rgba(139, 92, 246, 0.10);
    transition: border-color 0.18s, box-shadow 0.18s;
}
.chat-input:focus-within {
    border-color: rgba(139, 92, 246, 0.30);
    box-shadow:
        0 0 0 6px rgba(236, 72, 153, 0.08),
        0 4px 16px rgba(15, 23, 42, 0.06),
        0 24px 60px rgba(139, 92, 246, 0.18);
}
.chat-input textarea {
    width: 100%;
    border: 0;
    border-radius: 0;
    padding: 4px 2px;
    font-family: inherit;
    font-size: 13.5px;
    resize: none;
    min-height: 56px;
    max-height: min(40vh, 340px);
    overflow-y: auto;
    line-height: 1.55;
    background: transparent;
    color: #0f172a;
    outline: none;
}
.chat-input textarea::placeholder { color: #94a3b8; }
.chat-input textarea:focus {
    outline: none;
}
.chat-input-actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 8px;
    gap: 8px;
}
.chat-input-hint {
    font-size: 11.5px;
    color: #94a3b8;
    display: flex;
    align-items: center;
    gap: 8px;
}
.chat-input-hint kbd {
    background: #f3f4f6;
    border: 1px solid #e5e7eb;
    border-radius: 4px;
    padding: 1px 6px;
    font-family: inherit;
    font-size: 10.5px;
    color: #475569;
}
/* Subtle dot between the two shortcut hints — same colour as the
   hint text so it reads as a separator, not a bullet point. */
.chat-input-hint-sep { color: #cbd5e1; opacity: 0.9; }
.chat-input-buttons { display: flex; gap: 8px; align-items: center; }
/* Send button — gradient pill matching plan-composer-send */
.chat-input #chat-send {
    position: relative;
    border: none;
    background: linear-gradient(135deg, #ec4899 0%, #8b5cf6 100%);
    color: #fff;
    width: 32px;
    height: 32px;
    min-width: 32px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    cursor: pointer;
    box-shadow: 0 1px 2px rgba(139, 92, 246, 0.18);
    transition: transform 0.08s, box-shadow 0.18s, background 0.18s;
}
/* Countdown ring — overlays the send button while .is-countdown is
   set. SVG is 36×36 inset −2px around the 32×32 button so the ring
   draws OUTSIDE the gradient face; stroke is white-transparent so it
   reads on any background. .send-icon stays centered (sized + colored
   by the existing button rules). */
.chat-input #chat-send .send-countdown-ring {
    position: absolute;
    inset: -2px;
    width: 36px;
    height: 36px;
    pointer-events: none;
    transform: rotate(-90deg);  /* start the sweep at 12 o'clock */
    opacity: 0;
    transition: opacity 0.12s ease-out;
}
.chat-input #chat-send.is-countdown .send-countdown-ring {
    opacity: 1;
}
/* stroke-dashoffset 100 → 0 draws the progress arc clockwise.
   pathLength="100" on the circle normalises the dasharray to a 0-100
   space so the keyframe stays simple regardless of the actual r.
   Duration reads from --countdown-ms (set by the JS wirer, falling
   back to 1500ms — matches AUTO_SUBMIT_AFTER_MS) so the CSS
   animation and the JS setTimeout can never drift out of sync. */
.chat-input #chat-send.is-countdown .send-countdown-progress {
    animation: send-countdown-sweep var(--countdown-ms, 1500ms) linear forwards;
}
@keyframes send-countdown-sweep {
    from { stroke-dashoffset: 100; }
    to   { stroke-dashoffset: 0; }
}
/* Subtle nudge so the button "leans in" while the countdown runs —
   reinforces that an action is imminent without yelling. */
.chat-input #chat-send.is-countdown {
    box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.18),
                0 1px 2px rgba(139, 92, 246, 0.22);
}
.chat-input #chat-send:hover {
    background: linear-gradient(135deg, #db2777 0%, #7c3aed 100%);
    box-shadow: 0 1px 2px rgba(139, 92, 246, 0.22),
                0 8px 18px -6px rgba(236, 72, 153, 0.45);
}
.chat-input #chat-send:active { transform: scale(0.94); }
.chat-input #chat-send:disabled { opacity: 0.4; cursor: not-allowed; }
/* Mic button — sized + tinted to read as a peer of the send button.
   Idle: soft accent-tinted bg + accent icon (purple, matching the
   send gradient's terminal stop) so the pair feels like one
   coordinated control. Recording: takes on the send button's
   gradient with a red recording-cue ring pulsing around it. */
.chat-input .btn-voice {
    width: 32px;
    height: 32px;
    min-width: 32px;
    padding: 0;
    border-radius: 999px;
    background: rgba(139, 92, 246, 0.08);
    color: #8b5cf6;
    border: 1px solid rgba(139, 92, 246, 0.22);
    transition: background 0.18s, color 0.18s, border-color 0.18s, box-shadow 0.18s;
}
.chat-input .btn-voice:hover {
    background: rgba(139, 92, 246, 0.16);
    color: #7c3aed;
    border-color: #8b5cf6;
}
.chat-input .btn-voice.recording {
    background: linear-gradient(135deg, #ec4899 0%, #8b5cf6 100%);
    color: #fff;
    border-color: transparent;
    box-shadow: 0 0 0 4px rgba(220, 38, 38, 0.18);
    animation: mic-pulse 1.4s ease-in-out infinite;
}
@keyframes mic-pulse {
    0%, 100% { box-shadow: 0 0 0 4px rgba(220, 38, 38, 0.18); }
    50%      { box-shadow: 0 0 0 8px rgba(220, 38, 38, 0.30); }
}

/* 2026-05-24 — voice call mode.
   When .call-active is on the mic (set by wireVoice startCall) the
   button keeps the recording gradient but the ring stops pulsing
   between turns — the call-timer chip beside it carries the "still
   live" affordance via a pulsing red dot. The .thinking state fires
   while the AI reply is in flight: ring goes static + slightly dim
   so the user can tell the mic is paused. */
.chat-input .btn-voice.call-active {
    background: linear-gradient(135deg, #ec4899 0%, #8b5cf6 100%);
    color: #fff;
    border-color: transparent;
    box-shadow: 0 0 0 4px rgba(220, 38, 38, 0.22);
}
.chat-input .btn-voice.call-active.thinking {
    opacity: 0.7;
    animation: none;
    box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.20);
}

/* Call-timer chip — inline next to the mic. Renders as
   "● mm:ss" where the red dot pulses to mirror phone-call UI
   conventions. Hidden by default ([hidden] + display:none); the
   .is-visible class added by startCall flips it on. The
   tabular-nums font feature keeps the mm:ss width steady so the
   composer layout doesn't twitch every second. */
.chat-voice-timer {
    display: none;
    align-items: center;
    gap: 6px;
    margin-left: 4px;
    padding: 4px 10px;
    border-radius: 999px;
    background: rgba(220, 38, 38, 0.08);
    border: 1px solid rgba(220, 38, 38, 0.22);
    color: #b91c1c;
    font-size: 12px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    white-space: nowrap;
    user-select: none;
}
.chat-voice-timer.is-visible { display: inline-flex; }
.chat-voice-timer-dot {
    display: inline-block;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: #dc2626;
    box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.55);
    animation: call-timer-dot-pulse 1.4s ease-in-out infinite;
}
@keyframes call-timer-dot-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.0); opacity: 1; }
    50%      { box-shadow: 0 0 0 4px rgba(220, 38, 38, 0.25); opacity: 0.85; }
}
@media (prefers-reduced-motion: reduce) {
    .chat-voice-timer-dot { animation: none; }
    .chat-input .btn-voice.recording { animation: none; }
}

/* Voice dictation textarea states. .is-dictating runs while the
   recogniser is actively listening — soft red ring matches the mic
   button. .is-review parks after auto-stop (1.8s silence) so the
   organiser sees a "transcript ready, review + send" cue instead of
   the form auto-firing on noisy speech. .is-ready-pulse on the Send
   button draws the eye to the next step once. */
#chat-textarea.is-dictating,
#plan-chat-textarea.is-dictating {
    border-color: #fca5a5 !important;
    background: #fffafa !important;
    box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.10) !important;
}
#chat-textarea.is-review,
#plan-chat-textarea.is-review {
    border-color: #93c5fd !important;
    background: #f8fbff !important;
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.10) !important;
}
.is-ready-pulse {
    animation: send-ready 0.8s ease-out 3;
}
@keyframes send-ready {
    0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(79, 70, 229, 0); }
    50%      { transform: scale(1.06); box-shadow: 0 0 0 6px rgba(79, 70, 229, 0.22); }
}

/* Middle preview pane */
.preview-pane { background: #f4f5f7; align-items: stretch; }
.preview-toolbar {
    padding: 10px 14px;
    background: var(--surface);
    border-bottom: 1px solid var(--ink-100);
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-items: center;
    justify-content: flex-start;
}
.preview-toggles { display: flex; gap: 4px; background: #f3f4f6; border-radius: 10px; padding: 3px; }
.preview-toggles button {
    border: none;
    background: transparent;
    width: 32px;
    height: 28px;
    border-radius: 8px;
    color: var(--ink-500);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s, color 0.15s, box-shadow 0.15s;
}
.preview-toggles button:hover { color: var(--ink-900); }
.preview-toggles button.active { background: var(--surface); color: var(--ink-900); box-shadow: var(--shadow-sm); }
.preview-toggles button svg { display: block; }

.preview-mode-toggle { display: flex; gap: 4px; background: #f3f4f6; border-radius: 10px; padding: 3px; }
.preview-mode-toggle button { border: none; background: transparent; padding: 6px 12px; border-radius: 8px; font-size: 13px; cursor: pointer; color: var(--ink-500); }
.preview-mode-toggle button.active { background: var(--ink-900); color: #fff; }
.preview-mode-toggle button { white-space: nowrap; }
.preview-mode-toggle { margin-right: auto; }

/* Vertical / Horizontal ticket-layout toggle in the preview toolbar.
   Matches the device-toggle visual language (pill group, soft-grey
   container, subtle active card) so the toolbar reads as one set of
   coordinated controls. The "Change layout" label sits inline before
   the buttons because the toggle is the only one that names the
   property it controls — devices and modes are self-evident from the
   icons, layout needs the word. */
.preview-layout-toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}
.preview-layout-toggle .preview-layout-label {
    font-size: 12px;
    color: var(--ink-500);
    white-space: nowrap;
}
.preview-layout-toggle > button {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border: 0;
    background: #f3f4f6;
    padding: 6px 10px;
    border-radius: 8px;
    font-size: 12.5px;
    color: var(--ink-500);
    cursor: pointer;
    transition: background 0.12s, color 0.12s;
}
.preview-layout-toggle > button:hover { color: var(--ink-900); }
.preview-layout-toggle > button.active {
    background: var(--ink-900);
    color: #fff;
}
.preview-layout-toggle > button svg { display: block; }

.preview-stage {
    flex: 1;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 0;
    overflow: auto;
}
.preview-frame {
    background: var(--surface);
    border-radius: 0;
    box-shadow: none;
    overflow: hidden;
    width: 100%;
    max-width: 100%;
    transition: max-width 0.25s ease, width 0.25s ease;
}
.preview-frame.device-mobile { max-width: 392px; border-radius: 32px; box-shadow: 0 20px 60px rgba(15, 23, 42, 0.12); }
.preview-frame.device-tablet { max-width: 820px; border-radius: 24px; box-shadow: 0 20px 60px rgba(15, 23, 42, 0.12); }
/* Restore padding when a device frame is shown so the rounded card
   doesn't clip against the stage edge */
.preview-stage:has(.device-mobile),
.preview-stage:has(.device-tablet) { padding: 28px; }

.preview-iframe {
    /* Render the iframe at a slightly wider viewport and scale it back
       so organisers see more of the hero + reg form above the fold —
       the preview pane is narrower than a real desktop, and without a
       zoom compensation the landing looks crammed. */
    width: 108.7%;
    margin-left: -4.35%;
    min-height: 826px;
    transform: scale(0.92);
    transform-origin: top center;
    border: 0;
    display: block;
    background: #fff;
}
/* Mobile/tablet previews are already narrow phone-shaped simulations —
   no zoom-out, they'd shrink to unreadable. */
.preview-frame.device-mobile .preview-iframe,
.preview-frame.device-tablet .preview-iframe {
    width: 100%;
    margin-left: 0;
    min-height: 760px;
    transform: none;
}

/* Right builder pane */
.builder-section { margin-bottom: 18px; }
/* 2026-05-24 — Softer section headers. Was uppercase, 11px, very
   tight letter-spacing — read like a footer caption, not a section
   heading. Switched to sentence-case, 13px, normal letter-spacing,
   600 weight so the section title carries visual weight without
   shouting. The count-pill + add-button still sit on the right of
   the flex row. */
.builder-section-title {
    font-size: 13px;
    color: var(--ink-900);
    font-weight: 600;
    text-transform: none;
    letter-spacing: normal;
    margin: 0 0 10px;
    display: flex; justify-content: space-between; align-items: center;
    gap: 12px;
}
.builder-section-title .btn-add-inline {
    background: var(--accent-soft); color: var(--accent); border: none;
    padding: 4px 12px; border-radius: 999px; font-size: 12px; font-weight: 600; cursor: pointer;
    line-height: 1.4;
    flex-shrink: 0;
}
.builder-section-title .btn-add-inline:hover { filter: brightness(0.97); }

/* 2026-05-24 — Visual grouping inside Event Details. Each
   .meta-group renders as a subtle inset card so the four semantic
   bundles (identity / schedule / place / pricing) read as
   relationships. Lightweight chrome — soft bg + 1px border + a
   small label inside the corner. The label comes from
   `[data-group="…"]::before` so the markup stays clean. */
.meta-group {
    background: rgba(99, 102, 241, 0.025);
    border: 1px solid var(--ink-100);
    border-radius: 10px;
    padding: 10px 12px 4px;
    margin-bottom: 10px;
    position: relative;
}
.meta-group::before {
    content: attr(data-group);
    position: absolute;
    top: -7px;
    left: 12px;
    background: #fff;
    padding: 0 6px;
    font-size: 10px;
    font-weight: 600;
    color: var(--ink-500);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    line-height: 1;
}
.meta-group:last-child { margin-bottom: 0; }
.meta-group .event-meta-grid { margin: 0; }

/* Bottom "Add another" button — sits inside .landing-rows below the
   last row. Mirrors the top Add button in the section summary but
   styled as a dashed-outline "ghost" affordance so it reads as
   secondary. Saves the organiser from scrolling back to the section
   header after adding their fifth item (QA rows 12 / 14). */
.btn-add-row {
    display: block;
    width: 100%;
    margin-top: 8px;
    padding: 8px 12px;
    background: transparent;
    color: var(--accent);
    border: 1px dashed var(--ink-200, #e5e7eb);
    border-radius: var(--radius-md);
    font-size: 12.5px;
    font-weight: 500;
    cursor: pointer;
    text-align: center;
}
.btn-add-row:hover {
    background: var(--accent-soft);
    border-color: var(--accent);
    border-style: solid;
}

.builder-row {
    background: #fafbff;
    border: 1px solid var(--ink-100);
    border-radius: var(--radius-md);
    padding: 10px 12px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 8px;
}
.builder-row-head { display: flex; gap: 8px; align-items: center; }
.builder-row-head input.label {
    flex: 1; border: 1px solid transparent; background: transparent;
    font-size: 14px; font-weight: 500; padding: 6px 8px; border-radius: 6px;
}
.builder-row-head input.label:focus { background: var(--surface); border-color: var(--ink-100); outline: none; }
.builder-row-head select,
.builder-row-head input[type="number"],
.builder-row-head input[type="text"] {
    border: 1px solid var(--ink-100); background: var(--surface); border-radius: 6px;
    padding: 6px 8px; font-size: 13px;
}
.builder-row-head .mini-btn {
    background: transparent; border: none; color: var(--ink-500); cursor: pointer; padding: 4px 6px; border-radius: 6px;
}
.builder-row-head .mini-btn:hover { background: var(--ink-100); color: var(--danger); }

/* Landing-section rows (Speakers / Agenda / Sponsors). Same chrome
   as ticket / field rows but with a two-column grid body so the
   larger content set (photo url, bio, linkedin, etc.) doesn't
   overflow. .full spans both columns for long URLs / textareas. */
.landing-rows { display: flex; flex-direction: column; gap: 8px; }
.builder-row-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
}
.builder-row-grid > .full { grid-column: 1 / -1; }
.builder-row-grid input[type="text"],
.builder-row-grid input[type="url"],
.builder-row-grid input[type="datetime-local"],
.builder-row-grid textarea {
    width: 100%;
    padding: 7px 9px;
    font-size: 12.5px;
    border: 1px solid var(--ink-100);
    background: var(--surface);
    border-radius: 6px;
    color: var(--ink-900);
    font: inherit;
    font-size: 12.5px;
}
.builder-row-grid textarea { resize: vertical; min-height: 44px; }
.builder-row-grid input:focus,
.builder-row-grid textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.08);
}
.builder-row-thumb {
    margin-top: 4px;
    width: 56px; height: 56px;
    border-radius: 8px;
    overflow: hidden;
    background: var(--ink-100);
    border: 1px solid var(--ink-100);
}
.builder-row-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.btn-row-remove {
    background: transparent;
    border: 1px solid transparent;
    color: var(--ink-500);
    cursor: pointer;
    padding: 4px 9px;
    border-radius: 6px;
    font-size: 14px;
    line-height: 1;
}
.btn-row-remove:hover { background: #fef2f2; color: #991b1b; border-color: #fecaca; }

/* Inline "📎 Upload" trigger next to a URL field. Flex row with the
   input taking the remaining width; button sits flush against the
   input's right edge so the pair reads as a single control. */
.row-field-with-upload {
    display: flex;
    gap: 6px;
    align-items: stretch;
}
.row-field-with-upload input { flex: 1; min-width: 0; }
.btn-row-upload {
    flex-shrink: 0;
    padding: 7px 12px;
    font-size: 12px;
    font-weight: 600;
    color: var(--ink-900);
    background: #fff;
    border: 1px solid var(--ink-100);
    border-radius: 6px;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.12s, border-color 0.12s;
}
.btn-row-upload:hover { background: #eef2ff; border-color: #c7d2fe; color: #3730a3; }

/* 2026-05-24 — Shared upload-control wrapper for file-picker
   rows (Branding logo + hero banner). Provides consistent
   visual rhythm with the .row-field-with-upload pattern used by
   speaker photos / sponsor logos / resource files: thumb on
   the left, Upload button next, optional Remove + status hint.
   Inline styles in build.html have been replaced with this
   class so the four upload sites share one look. */
.upload-control {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}
.upload-control .upload-thumb {
    width: 40px;
    height: 40px;
    object-fit: contain;
    border: 1px solid var(--ink-100);
    border-radius: 8px;
    background: #fff;
    flex-shrink: 0;
}
.upload-control .upload-thumb.banner {
    /* hero-banner thumb is 2:1 to telegraph the aspect ratio */
    width: 80px;
}
.upload-control .btn-upload {
    flex-shrink: 0;
    padding: 7px 14px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--ink-900);
    background: #fff;
    border: 1px solid var(--ink-100);
    border-radius: 6px;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.12s, border-color 0.12s;
}
.upload-control .btn-upload:hover { background: #eef2ff; border-color: #c7d2fe; color: #3730a3; }
.upload-control .btn-upload-remove {
    flex-shrink: 0;
    background: transparent;
    border: 1px solid transparent;
    color: var(--ink-500);
    cursor: pointer;
    padding: 6px 10px;
    border-radius: 6px;
    font-size: 12px;
}
.upload-control .btn-upload-remove:hover { background: #fef2f2; color: #991b1b; border-color: #fecaca; }
.upload-control .upload-status {
    font-size: 11.5px;
    color: var(--ink-500);
    flex: 1 1 auto;
    min-width: 0;
}
.resource-row-hint {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 10.5px;
    color: var(--ink-500);
    background: #f9fafb;
    border: 1px dashed var(--ink-100);
    padding: 4px 8px;
    border-radius: 4px;
    word-break: break-all;
}

.builder-row-meta { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; font-size: 12px; color: var(--ink-500); }
.builder-row-meta label { display: flex; align-items: center; gap: 4px; cursor: pointer; }
.builder-row-meta input[type="date"] {
    border: 1px solid var(--ink-100); background: var(--surface);
    border-radius: 6px; padding: 4px 6px; font-size: 12px; color: var(--ink-700);
}
.builder-row-meta .options-input {
    flex: 1 1 100%; border: 1px solid var(--ink-100); background: var(--surface); border-radius: 6px;
    padding: 6px 8px; font-size: 12px; color: var(--ink-700);
}
/* Live-inventory pill next to the Limit cell — mirrors the "Only N
   left" badge the embed shows attendees, so organisers don't have to
   flip to the Registrations screen to see how fast a tier is filling. */
.tier-remaining {
    display: inline-flex; align-items: center;
    padding: 2px 7px; border-radius: 999px;
    font-size: 10.5px; font-weight: 600;
    background: var(--ink-100); color: var(--ink-700);
    letter-spacing: 0.03em;
}
.tier-remaining-low { background: #fef2f2; color: #b91c1c; }

.event-meta-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.event-meta-grid label { font-size: 11px; color: var(--ink-500); display: block; margin-bottom: 4px; text-transform: uppercase; letter-spacing: 0.08em; }
.event-meta-grid input,
.event-meta-grid select,
.event-meta-grid textarea {
    width: 100%; border: 1px solid var(--ink-100); background: var(--surface);
    border-radius: var(--radius-sm); padding: 8px 10px; font-size: 13px; font-family: inherit;
}
.event-meta-grid textarea { min-height: 58px; resize: vertical; }
.event-meta-grid .full { grid-column: span 2; }

.builder-footer {
    flex-shrink: 0; padding: 14px 18px; border-top: 1px solid var(--ink-100); background: var(--surface);
    display: flex; justify-content: space-between; align-items: center; gap: 8px;
}
.save-status { font-size: 12px; color: var(--ink-500); }
.save-status.saving { color: var(--warn); }
.save-status.saved { color: var(--success); }

/* ── Publish screen ────────────────────────────────────────────────────── */
.publish-main { max-width: 1100px; margin: 0 auto; padding: 40px 24px 80px; }
.publish-hero { margin-bottom: 28px; }
.publish-hero h1 { font-size: 32px; font-weight: 600; letter-spacing: -0.02em; margin: 0 0 6px; }
.publish-hero p { color: var(--ink-500); margin: 0; font-size: 15px; }
.publish-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.publish-card {
    background: var(--surface); border: 1px solid var(--ink-100);
    border-radius: var(--radius-lg); padding: 22px; box-shadow: var(--shadow-sm);
}
.publish-card h2 {
    font-size: 14px; font-weight: 600; color: var(--ink-500);
    text-transform: uppercase; letter-spacing: 0.08em; margin: 0 0 14px;
}
.publish-card-wide { grid-column: span 2; }

.payment-choices { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 14px; }
.payment-choice {
    border: 2px solid var(--ink-100); border-radius: var(--radius-md);
    padding: 14px 16px; cursor: pointer; transition: border 0.12s, background 0.12s;
    display: flex; flex-direction: column; gap: 6px;
}
.payment-choice.active { border-color: var(--ink-900); background: #fafafa; }
.payment-choice h3 { margin: 0; font-size: 15px; }
.payment-choice p { margin: 0; font-size: 13px; color: var(--ink-500); line-height: 1.45; }

/* Legacy .provider-row / .provider-pill rules retired — superseded by the
   .provider-pill-card grid on /v3/studio/{id}/payments. Removing them
   prevents display:flex leaking onto <tr class="provider-row"> in the
   admin payments table and mangling the row. */

.publish-previews { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 8px; }
.preview-tile { border: 1px solid var(--ink-100); border-radius: var(--radius-md); overflow: hidden; background: #f3f4f6; }
.preview-tile-head {
    padding: 8px 12px; background: var(--surface); border-bottom: 1px solid var(--ink-100);
    font-size: 12px; color: var(--ink-500); display: flex; justify-content: space-between; align-items: center;
}
.preview-tile iframe { width: 100%; height: 360px; border: 0; background: var(--surface); display: block; }

.embed-block {
    margin-top: 10px;
    background: #0b0f19; color: #e5e7eb; border-radius: var(--radius-md);
    padding: 12px 14px; font-family: 'SF Mono', Menlo, Consolas, monospace;
    font-size: 12px; overflow-x: auto; white-space: pre; position: relative;
}
.embed-block button {
    position: absolute; top: 8px; right: 8px; background: rgba(255, 255, 255, 0.12);
    color: #fff; border: none; padding: 4px 10px; border-radius: 6px; font-size: 11px; cursor: pointer;
}

.publish-footer { margin-top: 22px; display: flex; justify-content: space-between; align-items: center; }
.live-banner {
    background: #ecfdf5; border: 1px solid #a7f3d0; color: #065f46;
    padding: 14px 16px; border-radius: var(--radius-md); margin-bottom: 18px;
    display: flex; gap: 14px; align-items: center;
}
.live-banner a { color: inherit; text-decoration: underline; font-weight: 600; }

/* ── Plan compact prompt echo ─────────────────────────────────────────── */
.plan-prompt-compact {
    border: 1px solid var(--ink-100);
    border-radius: var(--radius-md);
    background: var(--surface);
    overflow: hidden;
}
.plan-prompt-compact summary {
    list-style: none;
    cursor: pointer;
    padding: 12px 16px;
    display: flex;
    align-items: center;
    gap: 12px;
    font-size: 13px;
}
.plan-prompt-compact summary::-webkit-details-marker { display: none; }
.plan-prompt-label {
    font-size: 10px;
    font-weight: 700;
    color: var(--accent);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    flex-shrink: 0;
}
.plan-prompt-snippet { color: var(--ink-700); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.plan-prompt-edit { font-size: 12px; color: var(--ink-500); text-decoration: none; }
.plan-prompt-edit:hover { color: var(--accent); text-decoration: underline; }
.plan-prompt-compact p { padding: 0 16px 14px; font-size: 13px; line-height: 1.55; color: var(--ink-700); margin: 0; }

/* ── Count pill + collapsibles ─────────────────────────────────────────── */
.count-pill {
    display: inline-block;
    margin-left: 6px;
    min-width: 20px;
    padding: 1px 7px;
    background: var(--ink-100);
    color: var(--ink-700);
    border-radius: 999px;
    font-size: 10px;
    font-weight: 700;
    text-align: center;
    letter-spacing: normal;
    text-transform: none;
}
.builder-section-collapsible { padding: 0; background: transparent; border: none; }
.builder-section-collapsible > summary {
    cursor: pointer;
    list-style: none;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 0 10px;
    margin: 0 0 10px;
    position: relative;
}
.builder-section-collapsible > summary::-webkit-details-marker { display: none; }
.builder-section-collapsible > summary::after {
    content: '▾';
    color: var(--ink-500);
    font-size: 10px;
    transition: transform 0.2s;
    margin-left: 8px;
}
.builder-section-collapsible[open] > summary::after { transform: rotate(180deg); }
.builder-section-collapsible .collapsible-hint { font-size: 11px; color: var(--ink-500); text-transform: none; letter-spacing: normal; font-weight: 400; }

/* ── Grouped field rows (Form fields panel) ───────────────────────────── */
.field-group { border: none; padding: 0; margin-bottom: 14px; }
.field-group > summary {
    list-style: none;
    cursor: pointer;
    padding: 8px 10px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    gap: 10px;
    background: #f9fafb;
    border: 1px solid transparent;
    transition: background 0.15s;
    margin-bottom: 6px;
}
.field-group > summary::-webkit-details-marker { display: none; }
.field-group > summary:hover { background: var(--ink-100); }
.field-group[open] > summary { background: var(--surface); border-color: var(--ink-100); }
.field-group > summary::before {
    content: '▸';
    color: var(--ink-500);
    font-size: 10px;
    transition: transform 0.15s;
    flex-shrink: 0;
}
.field-group[open] > summary::before { transform: rotate(90deg); }
.fg-title { font-size: 13px; font-weight: 600; color: var(--ink-900); }
.fg-hint { font-size: 11px; color: var(--ink-500); flex: 1; }
.fg-count {
    background: var(--ink-100); color: var(--ink-700);
    font-size: 10px; font-weight: 700;
    padding: 1px 7px; border-radius: 999px;
}
/* Rename affordance on custom section headers. Only renders for
   organiser-defined sections — the preset keys (contact/professional/
   …) stay read-only so their semantics survive. Opacity 0.6 by default
   so it fades into the summary row, lifts to full opacity on hover.
   The click handler swaps .fg-title for a bare <input> styled below,
   so committing is just Enter/blur — no save button needed. */
.fg-rename {
    background: transparent; border: 0; cursor: pointer;
    font-size: 12px; color: var(--ink-500);
    padding: 2px 6px; border-radius: 4px;
    opacity: 0.6; transition: opacity 120ms, background 120ms;
}
.field-group > summary:hover .fg-rename { opacity: 1; }
.fg-rename:hover { background: var(--ink-100); color: var(--ink-900); }
.fg-title-input {
    font-size: 13px; font-weight: 600; color: var(--ink-900);
    padding: 2px 6px; border: 1px solid var(--accent, #4f46e5);
    border-radius: 5px; outline: none;
    min-width: 120px; max-width: 240px;
    background: #fff;
}

.field-row {
    border: 1px solid var(--ink-100);
    border-radius: 10px;
    background: var(--surface);
    margin: 4px 0 6px 20px;
    overflow: hidden;
    transition: box-shadow 0.15s, border-color 0.15s;
}
.field-row:hover { border-color: #c7d2fe; }
.field-row.expanded { box-shadow: 0 4px 12px rgba(15, 23, 42, 0.06); border-color: #c7d2fe; }

.field-row-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    cursor: default;
}
.field-expand {
    width: 18px; height: 18px; flex-shrink: 0;
    border: none; background: transparent; color: var(--ink-500);
    font-size: 11px; cursor: pointer; padding: 0;
    transition: transform 0.15s;
}
.field-row.expanded .field-expand { transform: rotate(90deg); }
.field-row-label {
    flex: 1;
    font-size: 13px; font-weight: 500; color: var(--ink-900);
    cursor: pointer;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.field-row-type {
    font-size: 11px; color: var(--ink-500);
    padding: 2px 8px; border-radius: 999px;
    background: #f3f4f6;
    cursor: pointer;
    flex-shrink: 0;
}
.field-row .toggle { margin: 0; flex-shrink: 0; }
.field-row .mini-btn { flex-shrink: 0; }
/* ↑ / ↓ reorder handles — lighter touch than the ✕ delete so the eye
   reads Remove as the scariest action. Disabled-look when the field is
   already at the section's extremes (handled by JS guarding the swap). */
.field-row .field-up, .field-row .field-down {
    font-size: 13px;
    line-height: 1;
    opacity: 0.55;
    transition: opacity 0.15s, background 0.15s;
}
.field-row .field-up:hover, .field-row .field-down:hover {
    opacity: 1;
    background: var(--ink-100);
    color: var(--ink-900);
}
.field-row .field-remove:hover { background: #fef2f2; color: var(--danger, #dc2626); }

/* Duplicate-field warning. Soft amber so it reads as "heads up" rather
   than "error" — duplicates are usually a UX mistake the organiser can
   accept or fix in one click, not a hard validation failure. Lives at
   the top of #fields-list and mirrors the visual weight of a save
   status pill so it doesn't dominate the editor. */
.dup-warning {
    margin: 0 0 10px;
    padding: 10px 12px;
    border: 1px solid #fcd9a3;
    background: #fff8ec;
    border-radius: 8px;
    font-size: 12.5px;
    color: #7c4a03;
}
.dup-warning-head {
    display: flex; align-items: center; gap: 6px;
    font-weight: 600; margin-bottom: 6px;
    color: #92400e;
}
.dup-warning-icon { font-size: 13px; }
.dup-warning-row {
    display: flex; align-items: center; justify-content: space-between;
    gap: 10px; padding: 4px 0;
    border-top: 1px dashed #f3d9a8;
}
.dup-warning-row:first-of-type { border-top: none; }
.dup-warning-text { flex: 1; line-height: 1.4; }
.dup-warning-actions { display: flex; gap: 6px; flex-shrink: 0; }
.dup-action {
    font-size: 11.5px; font-weight: 500;
    padding: 4px 10px; border-radius: 6px;
    border: 1px solid transparent; cursor: pointer;
    transition: background 0.12s, border-color 0.12s;
}
.dup-action.dup-remove {
    background: #fff; border-color: #fcd9a3; color: #7c4a03;
}
.dup-action.dup-remove:hover { background: #fef3c7; border-color: #f59e0b; }
.dup-action.dup-keep {
    background: transparent; border-color: transparent; color: #92400e;
}
.dup-action.dup-keep:hover { background: #fef3c7; }

/* Inline chip next to the field label — stays visible after "Keep
   both" dismisses the banner so the duplicate row is never silently
   accepted. Same palette as the banner. */
.field-row-dup {
    font-size: 10.5px; font-weight: 600;
    padding: 2px 7px; border-radius: 999px;
    background: #fff3d6; color: #92400e;
    border: 1px solid #fcd9a3;
    flex-shrink: 0;
    letter-spacing: 0.01em;
}
.field-row-head.is-duplicate .field-row-label { color: #7c4a03; }

/* Drag-handle column. The handle is the only legal drag-start surface
   on the row — outside it the body/inputs work normally. cursor: grab
   tells the eye it's draggable; on active drag the body cursor flips
   to grabbing via [draggable].is-dragging below. */
.field-row .field-drag {
    width: 16px; height: 18px; flex-shrink: 0;
    border: none; background: transparent; padding: 0;
    color: var(--ink-300, #cbd5e1);
    font-size: 14px; line-height: 1; letter-spacing: -2px;
    cursor: grab;
    opacity: 0.7;
    transition: color 0.15s, opacity 0.15s;
    user-select: none;
}
.field-row:hover .field-drag { color: var(--ink-500); opacity: 1; }
.field-row .field-drag:active { cursor: grabbing; }

/* In-flight drag visual — source row dims, target row gets a coloured
   line on the side it'll drop to so the user can predict the result
   without releasing. The 2px insets keep the indicator off the section
   border so it reads as a per-row affordance. */
.field-row.is-dragging {
    opacity: 0.45;
    box-shadow: 0 6px 18px rgba(15, 23, 42, 0.12);
    cursor: grabbing;
}
.field-row.drop-above { box-shadow: inset 0 3px 0 0 #4f46e5; }
.field-row.drop-below { box-shadow: inset 0 -3px 0 0 #4f46e5; }
.field-row.drop-above.drop-below { box-shadow: inset 0 3px 0 0 #4f46e5; } /* belt+braces */

.field-row-body {
    display: none;
    padding: 8px 14px 14px 36px;
    border-top: 1px solid #f3f4f6;
    gap: 10px;
    grid-template-columns: 1fr 1fr;
}
.field-row.expanded .field-row-body { display: grid; }
.field-row-body label {
    font-size: 11px;
    color: var(--ink-500);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.field-row-body input,
.field-row-body select {
    border: 1px solid var(--ink-100);
    border-radius: 6px;
    padding: 7px 10px;
    font-size: 13px;
    color: var(--ink-900);
    background: var(--surface);
    font-family: inherit;
    font-weight: 400;
    text-transform: none;
    letter-spacing: normal;
}
.field-row-body .field-body-full { grid-column: 1 / -1; }
.field-row-body .field-hint {
    font-size: 11px;
    color: var(--ink-500);
    text-transform: none;
    letter-spacing: normal;
    font-weight: 400;
    margin-top: 2px;
    line-height: 1.4;
}
.field-row-body .field-hint em { font-style: italic; color: var(--ink-700); }
.field-row-body .cond-editor {
    background: #fafafa;
    border: 1px dashed var(--ink-100);
    border-radius: 8px;
    padding: 10px 12px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.field-row-body .cond-editor[data-has-rule="1"] {
    background: #fffbeb;
    border-color: #fde68a;
    border-style: solid;
}
.field-row-body .cond-head {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.field-row-body .cond-label {
    font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em;
    font-weight: 600; color: var(--ink-500);
}
.field-row-body .cond-clear {
    background: none; border: none; cursor: pointer;
    font-size: 11px; color: var(--ink-500); text-transform: uppercase;
    letter-spacing: 0.06em; padding: 2px 6px; border-radius: 4px;
}
.field-row-body .cond-clear:hover { background: #fef2f2; color: var(--danger, #dc2626); }
.field-row-body .cond-row {
    display: grid;
    grid-template-columns: 1.6fr 1fr 1.4fr;
    gap: 8px;
}
@media (max-width: 680px) {
    .field-row-body .cond-row { grid-template-columns: 1fr; }
}
.field-row-body .scope-toggle .scope-row {
    display: flex; align-items: center; gap: 8px;
    text-transform: none; letter-spacing: normal;
    font-weight: 400; font-size: 13px; color: var(--ink-700);
}
.field-row-body .scope-toggle .scope-row strong {
    font-weight: 600; color: var(--ink-900);
}
.field-row-body .scope-toggle input[type="checkbox"] {
    width: auto; margin: 0;
}

/* ── Options chips editor ──────────────────────────────────────────────
   Replaces the old comma-separated input. Each option is a chip: click
   the label to edit in place, press ✕ to delete, or type in the dashed
   "+ Add option" slot at the end. Used by select/radio/multi-select. */
.opts-wrap .opts-label-row {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: 10px; margin-bottom: 6px;
}
.opts-wrap .opts-label-text {
    font-size: 11px;
    color: var(--ink-500);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}
.opts-wrap .opts-hint {
    font-size: 11px;
    color: var(--ink-500);
    font-weight: 400;
    text-transform: none;
    letter-spacing: normal;
}
.opts-chips {
    display: flex; flex-wrap: wrap; gap: 6px;
    padding: 8px;
    border: 1px solid var(--ink-100);
    border-radius: 8px;
    background: var(--surface);
    min-height: 44px;
    align-items: center;
}
.opts-chips .opts-parent-switch {
    flex: 0 0 100%;
    display: flex; align-items: center; gap: 8px;
    padding: 4px 6px 8px;
    border-bottom: 1px dashed var(--ink-100);
    margin-bottom: 4px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--ink-500);
}
.opts-chips .opts-parent-switch select {
    font-size: 12px; padding: 4px 8px;
    border: 1px solid var(--ink-100);
    border-radius: 6px;
    background: #fff; color: var(--ink-900);
    text-transform: none; letter-spacing: normal;
    font-weight: 500;
}
.opts-chips .opts-parent-empty {
    font-style: italic; color: #92400e;
    text-transform: none; letter-spacing: normal;
}
.opts-chip-badge {
    margin-left: 6px;
    padding: 1px 6px;
    border-radius: 10px;
    background: #f3f4f6;
    color: var(--ink-500);
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}
.opts-chip {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 4px 4px 4px 10px;
    background: #eef2ff;
    border: 1px solid #c7d2fe;
    color: var(--ink-900);
    border-radius: 999px;
    font-size: 12px; font-weight: 500;
    font-family: inherit;
    transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
    max-width: 100%;
}
.opts-chip:hover { border-color: var(--accent); }
.opts-chip.editing {
    background: #fff;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}
.opts-chip-label {
    cursor: text;
    padding: 2px 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    max-width: 200px;
}
.opts-chip > input {
    border: none !important;
    outline: none !important;
    background: transparent !important;
    padding: 2px 2px !important;
    font-size: 12px !important;
    font-weight: 500 !important;
    color: var(--ink-900) !important;
    min-width: 60px;
    width: 140px;
    font-family: inherit;
    text-transform: none;
    letter-spacing: normal;
}
.opts-chip-x {
    display: inline-flex; align-items: center; justify-content: center;
    width: 20px; height: 20px; border-radius: 50%;
    background: transparent;
    color: var(--ink-500);
    border: none;
    cursor: pointer;
    font-size: 11px;
    line-height: 1;
    padding: 0;
    flex-shrink: 0;
    transition: background 0.12s, color 0.12s;
}
.opts-chip-x:hover { background: rgba(239, 68, 68, 0.12); color: var(--danger, #dc2626); }
.opts-chip-add {
    background: transparent;
    border: 1px dashed #cbd5e1;
    color: var(--ink-500);
    padding: 2px 8px;
}
.opts-chip-add:hover { border-color: var(--accent); }
.opts-chip-add > input {
    width: 130px;
    font-weight: 400 !important;
    color: var(--ink-700) !important;
}
.opts-chip-add > input::placeholder { color: var(--ink-500); }

/* ── Required toggle ───────────────────────────────────────────────────── */
.toggle { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; font-size: 12px; color: var(--ink-700); }
.toggle input { display: none; }
.toggle-switch {
    width: 30px; height: 18px; background: #cbd5e1; border-radius: 999px;
    position: relative; transition: background 0.15s;
    flex-shrink: 0;
}
.toggle-switch::after {
    content: ''; position: absolute;
    top: 2px; left: 2px;
    width: 14px; height: 14px;
    background: #fff; border-radius: 50%;
    transition: transform 0.15s;
    box-shadow: 0 1px 2px rgba(0,0,0,0.15);
}
.toggle input:checked + .toggle-switch { background: var(--accent); }
.toggle input:checked + .toggle-switch::after { transform: translateX(12px); }
.toggle-label { user-select: none; }

/* ── Price input + section select ──────────────────────────────────────── */
.price-input {
    display: inline-flex; align-items: center;
    background: var(--surface); border: 1px solid var(--ink-100);
    border-radius: 6px; overflow: hidden; width: 110px;
}
.price-symbol { padding: 0 6px 0 8px; color: var(--ink-500); font-size: 13px; }
.price-input input { border: none !important; outline: none; width: 100%; padding: 6px 8px 6px 0 !important; }
.section-select { white-space: nowrap; }

/* ── Chat chips + typing dots ──────────────────────────────────────────── */
.chat-msg.compact .chat-msg-bubble { padding: 8px 12px; font-size: 13px; }
.chat-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 4px; }
.chat-chip {
    border: 1px solid var(--ink-100);
    background: var(--surface);
    color: var(--ink-700);
    font-size: 12px; padding: 6px 12px;
    border-radius: 999px; cursor: pointer;
    transition: border 0.15s, background 0.15s;
    font-family: inherit;
}
.chat-chip:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
/* 2026-05-22 evening — multi-select clarification chips.
   Tap to toggle. Staged chips render with the accent fill so the
   organiser can see what they've picked before hitting Send. */
.chat-chip-multi.is-staged {
    border-color: var(--accent);
    color: var(--surface);
    background: var(--accent);
}
.chat-chip-multi.is-staged:hover { background: var(--accent); color: var(--surface); }
.chat-chips[data-input-type="multi_select"]::after {
    content: "tap chips to stage · Send to submit";
    display: block;
    font-size: 11px;
    color: var(--ink-500);
    margin-top: 6px;
    padding-left: 4px;
}
/* 2026-05-24 — chips embedded INSIDE an awaiting-input bubble card.
   Sits between the question body and the "● Agent is waiting…" footer
   band. Top padding + separator line read as a card section divider;
   chips themselves keep their pill shape but render in a stacked
   column on narrow widths so each chip is comfortably tappable in the
   chat pane. The chat-chips-inline class is set on the wrap only when
   renderChips was called with a targetBubble — the legacy tail-of-pane
   chips keep the original .chat-chips style. */
.chat-msg.ai .chat-msg-bubble.is-awaiting-input .chat-chips-inline {
    margin: 12px -2px 0;
    padding-top: 12px;
    border-top: 1px dashed rgba(99, 102, 241, 0.22);
    gap: 6px;
}
.chat-msg.ai .chat-msg-bubble.is-awaiting-input .chat-chips-inline .chat-chip {
    background: rgba(99, 102, 241, 0.06);
    border-color: rgba(99, 102, 241, 0.28);
    color: #4338ca;
    width: 100%;
    text-align: left;
    border-radius: 8px;
    padding: 9px 12px;
    font-size: 13px;
}
.chat-msg.ai .chat-msg-bubble.is-awaiting-input .chat-chips-inline .chat-chip:hover {
    background: rgba(99, 102, 241, 0.14);
    border-color: rgba(99, 102, 241, 0.45);
    color: #3730a3;
}
/* Multi-select staged styling needs higher specificity than the
   generic chat-chip rules above to keep the accent fill visible
   inside the awaiting-input card. */
.chat-msg.ai .chat-msg-bubble.is-awaiting-input .chat-chips-inline .chat-chip-multi.is-staged {
    background: var(--accent);
    color: var(--surface);
    border-color: var(--accent);
}

/* 2026-05-24 — explicit checkbox icon on multi_select chips so the
   affordance reads as "you can pick more than one". `.chip-checkbox`
   sits left of `.chip-label`; the icon glyph (☐ / ☑) is swapped by
   the click handler in renderChips. */
.chat-chip .chip-checkbox {
    display: inline-block;
    margin-right: 8px;
    font-size: 14px;
    line-height: 1;
    vertical-align: -1px;
    color: var(--ink-500);
}
.chat-chip-multi.is-staged .chip-checkbox {
    color: var(--surface);
}
.chat-chip .chip-label {
    display: inline-block;
}
/* 2026-05-24 — single_select chips get a subtle → arrow suffix so
   it reads as "tap to submit" rather than "pick one of many". */
.chat-chip:not(.chat-chip-multi) .chip-arrow {
    display: inline-block;
    margin-left: 6px;
    color: var(--ink-400);
    transition: transform 0.15s, color 0.15s;
}
.chat-chip:not(.chat-chip-multi):hover .chip-arrow {
    color: var(--accent);
    transform: translateX(2px);
}
/* 2026-05-24 — live picked-count hint that REPLACES the CSS ::after
   pseudo-element when the chip group is multi_select. The CSS-only
   variant couldn't reflect picked-count; this one does. The legacy
   ::after stays in place as a fallback when JS hasn't appended a
   .chat-chips-hint yet. */
.chat-chips-hint {
    width: 100%;
    font-size: 11px;
    color: var(--ink-500);
    margin-top: 6px;
    padding-left: 4px;
}
/* Suppress the CSS-only hint when the JS-generated one is present
   (avoids two stacked "tap chips" lines). */
.chat-chips[data-input-type="multi_select"]:has(.chat-chips-hint)::after {
    display: none;
}

.chat-msg.typing .chat-msg-bubble {
    letter-spacing: 6px; font-size: 18px; padding: 8px 14px; animation: typing-pulse 1s infinite;
}
@keyframes typing-pulse {
    0%, 100% { opacity: 0.4; }
    50% { opacity: 1; }
}

.chat-input button {
    border: none; background: var(--ink-900); color: #fff;
    padding: 0 12px; border-radius: var(--radius-md);
    font-weight: 500; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    min-width: 44px;
}

/* Add-field flash highlight — applies to tickets (.builder-row) AND
   form field rows (.field-row). Fires on add-button clicks + after any
   chat-driven edit via flashRowsInList() in build.html. Triple-pulse
   so a collapsed-then-opened section's expansion animation doesn't eat
   the flash before the organizer sees it. */
.builder-row.flash-new,
.field-row.flash-new { animation: flash-glow 2s ease-out; }
@keyframes flash-glow {
    0%   { box-shadow: 0 0 0 0   rgba(79, 70, 229, 0.55); background: #eef2ff; }
    25%  { box-shadow: 0 0 0 10px rgba(79, 70, 229, 0.32); background: #e0e7ff; }
    55%  { box-shadow: 0 0 0 3px  rgba(79, 70, 229, 0.22); background: #eef2ff; }
    80%  { box-shadow: 0 0 0 12px rgba(79, 70, 229, 0.12); background: #f3f4ff; }
    100% { box-shadow: 0 0 0 0   rgba(79, 70, 229, 0);    background: transparent; }
}

/* ─── Plan live form preview (right pane) ────────────────────────────────
   Mirrors the /v3/embed render so the organizer sees exactly what
   attendees will see. Reloads on every chat reply; on Continue the
   final state is baked in and /build takes over. */
.plan-preview {
    background: var(--ink-900);
    border-left: 1px solid var(--ink-100);
    display: flex;
    flex-direction: column;
    height: 100%;
    min-width: 0;
    min-height: 0;
}
.plan-preview-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 20px;
    color: #fff;
    background: var(--ink-900);
    border-bottom: 1px solid rgba(255,255,255,0.08);
    flex-shrink: 0;
}
.plan-preview-title {
    font-size: 14px;
    font-weight: 600;
    letter-spacing: -0.01em;
}
.plan-preview-hint {
    font-size: 12px;
    color: rgba(255,255,255,0.55);
}
.plan-preview-stage {
    flex: 1;
    min-height: 0;
    overflow: hidden;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 20px;
    background:
        radial-gradient(circle at 30% 20%, rgba(79, 70, 229, 0.12), transparent 60%),
        var(--ink-900);
}
.plan-preview-frame {
    width: 100%;
    max-width: 520px;
    height: 100%;
    background: #fff;
    border-radius: 14px;
    overflow: hidden;
    box-shadow: 0 22px 60px rgba(0,0,0,0.35), 0 1px 0 rgba(255,255,255,0.04);
}
.plan-preview-iframe {
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
}
.plan-preview-foot {
    padding: 10px 20px 14px;
    font-size: 11.5px;
    color: rgba(255,255,255,0.55);
    text-align: center;
    flex-shrink: 0;
}

/* ─── /publish embed-hero card ───────────────────────────────────────────
   The embed snippet is the primary artifact — this card leads the
   Publish screen. Hosted landing URL is demoted to a secondary card
   below ("Hosted landing page — most organizers embed above instead"). */
.embed-hero-card {
    order: -1;  /* pin to top of publish-grid by default */
    border: 1px solid var(--ink-100);
    box-shadow: var(--shadow-md);
    padding: 28px;
}

/* When a paid event hasn't set up payments yet, the payment card leapfrogs
   the embed-hero card — "do this first" visual priority. order:-2 beats
   embed-hero's -1. The amber outline + soft glow makes it unmissable. */
.publish-card.gate-first {
    order: -2;
    border: 2px solid #f59e0b;
    box-shadow: 0 0 0 6px rgba(245, 158, 11, 0.08), var(--shadow-md);
    background: linear-gradient(180deg, #fffbeb, #fff);
}

/* Pay-required validation banner — renders full-width under the
   manage header on every manage page when the event has paid tickets
   but no verified payment config (suppressed on /payments itself).
   Sits outside .manage-layout so it spans the whole viewport; we
   apply horizontal margins here to match the header's padding. */
.pay-required-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    background: #fffbeb;
    border: 1.5px solid #fcd34d;
    border-radius: 14px;
    padding: 16px 20px;
    margin: 16px 22px 0;
    box-shadow: 0 4px 16px -8px rgba(245, 158, 11, 0.25);
}
@media (max-width: 780px) {
    .pay-required-banner { margin: 12px 16px 0; flex-direction: column; align-items: stretch; }
}
.pay-required-banner-left {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    flex: 1;
    min-width: 0;
}
.pay-required-dot {
    width: 10px; height: 10px;
    border-radius: 999px;
    background: #f59e0b;
    flex-shrink: 0;
    margin-top: 7px;
    box-shadow: 0 0 0 4px rgba(245, 158, 11, 0.2);
    animation: pay-required-pulse 2s ease-in-out infinite;
}
@keyframes pay-required-pulse {
    0%, 100% { box-shadow: 0 0 0 4px rgba(245, 158, 11, 0.20); }
    50%      { box-shadow: 0 0 0 8px rgba(245, 158, 11, 0.10); }
}
.pay-required-banner strong {
    display: block;
    color: #92400e;
    font-size: 15px;
    font-weight: 700;
    margin-bottom: 2px;
}
.pay-required-hint {
    display: block;
    color: #78350f;
    font-size: 13px;
    line-height: 1.5;
}
.pay-required-cta {
    flex-shrink: 0;
    padding: 10px 18px !important;
    font-size: 13px !important;
    font-weight: 600;
    white-space: nowrap;
    background: #f59e0b !important;
    border-color: #f59e0b !important;
}
.pay-required-cta:hover { background: #d97706 !important; border-color: #d97706 !important; }

/* Sandbox-live chip — compact pill docked right-top of the manage
   content area. Status indicator ("you're in test mode"), not an
   emergency alert: amber fill + pulsing dot + short "Test mode / Fix
   →" copy, long-form consequence in the title= tooltip. The whole
   pill is the link so the full surface is clickable. Amber (not red)
   because being in sandbox is a heads-up worth noticing, not a broken
   state — the old red banner read as "something is on fire" and ate
   half the viewport on /emails and /payments. */
.sandbox-live-chip-row {
    display: flex;
    justify-content: flex-end;
    margin: 0 22px 10px;
}
.sandbox-live-chip {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    background: #fffbeb;
    border: 1px solid #fde68a;
    border-radius: 999px;
    padding: 4px 10px 4px 9px;
    font-size: 11.5px;
    font-weight: 500;
    color: #78350f;
    text-decoration: none;
    /* Default state = muted (lives here after the 4s timeout).
       Dimmed so it reads as ambient status, not an alert. */
    opacity: 0.55;
    transition: background 180ms, border-color 180ms, opacity 600ms ease;
}
/* `.is-fresh` is set by the partial's JS on render and stripped after
   4s. Full-opacity window = the notice moment; after that the chip
   fades into passive awareness without re-flowing the layout. */
.sandbox-live-chip.is-fresh { opacity: 1; }
/* Hover always restores full opacity so the affordance survives the
   mute — organiser can still click through to fix without waiting. */
.sandbox-live-chip:hover { background: #fef3c7; border-color: #fcd34d; opacity: 1; }
.sandbox-live-dot {
    width: 7px; height: 7px;
    border-radius: 50%;
    background: #f59e0b;
    flex-shrink: 0;
    animation: sandbox-live-pulse 1.8s ease-in-out infinite;
}
@keyframes sandbox-live-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.45; }
}
.sandbox-live-label { color: #92400e; }
.sandbox-live-fix {
    color: #92400e;
    font-weight: 600;
    padding-left: 6px;
    border-left: 1px solid #fde68a;
    margin-left: 2px;
}
@media (max-width: 780px) {
    .sandbox-live-chip-row { margin: 0 16px 8px; }
}
.embed-hero-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 20px;
    margin-bottom: 18px;
}
.embed-hero-head h2 {
    font-family: 'Instrument Serif', Georgia, serif;
    font-weight: 400;
    font-size: 28px;
    letter-spacing: -0.01em;
    margin: 0;
    color: var(--ink-900);
}
.embed-block-hero {
    background: var(--ink-900);
    color: #e7e9f0;
    font-family: ui-monospace, SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace;
    font-size: 12.5px;
    line-height: 1.55;
    padding: 16px 18px;
    border-radius: 10px;
    white-space: pre-wrap;
    word-break: break-word;
    margin-bottom: 18px;
}
.embed-preview-row {
    border-top: 1px solid var(--ink-100);
    padding-top: 18px;
}
.publish-previews-single { grid-template-columns: 1fr !important; max-width: none; }

/* ─── Embed delivery-mode tabs — Inline vs Button popup ─────────────────
   Two large radio-style tabs above the snippet block on Publish. Clicking
   one switches which snippet + preview shows (and which is copied). Kept
   visually chunky so the choice reads as a real product decision, not a
   stylistic toggle. */
.embed-mode-tabs {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
    margin-bottom: 18px;
}
.embed-mode-tab {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 14px 16px;
    background: var(--surface);
    border: 1.5px solid var(--ink-100);
    border-radius: 12px;
    text-align: left;
    cursor: pointer;
    font: inherit;
    color: var(--ink-700);
    transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}
.embed-mode-tab:hover {
    border-color: var(--accent);
    background: var(--accent-soft);
    color: var(--ink-900);
}
.embed-mode-tab.active {
    border-color: var(--accent);
    background: var(--accent-soft);
    color: var(--ink-900);
    box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.12);
}
.embed-mode-glyph {
    display: inline-flex;
    align-items: center; justify-content: center;
    width: 36px; height: 36px; border-radius: 10px;
    background: var(--surface);
    border: 1px solid var(--ink-100);
    color: var(--ink-700);
    flex-shrink: 0;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.embed-mode-tab.active .embed-mode-glyph {
    background: #fff;
    border-color: var(--accent);
    color: var(--accent);
}
.embed-mode-label {
    display: flex; flex-direction: column; gap: 2px; min-width: 0;
}
.embed-mode-label strong {
    font-size: 14px;
    font-weight: 600;
    color: var(--ink-900);
    letter-spacing: -0.005em;
}
.embed-mode-label small {
    font-size: 12.5px;
    color: var(--ink-500);
    font-weight: 400;
}
.embed-mode-desc {
    font-size: 13px;
    color: var(--ink-500);
    line-height: 1.55;
    margin: 12px 0 18px;
}
.embed-mode-desc code {
    background: #f3f4f6;
    border: 1px solid var(--ink-100);
    border-radius: 4px;
    padding: 1px 5px;
    font-size: 11.5px;
    color: var(--ink-900);
    font-family: ui-monospace, SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace;
}

/* "Try it on this page" demo row in the popup panel. Live button wired
   to the same widget.js that ships in the snippet — so the organizer
   actually sees the popup modal before they paste the code. */
.embed-try {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 18px 20px;
    background: linear-gradient(180deg, #fafaff, #fff);
    border: 1px dashed #c7d2fe;
    border-radius: 12px;
    margin-top: 6px;
}
.embed-try-label {
    font-size: 12px;
    font-weight: 600;
    color: var(--ink-700);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.embed-try-link { margin-left: auto; }

@media (max-width: 640px) {
    .embed-mode-tabs { grid-template-columns: 1fr; }
    .embed-try { flex-wrap: wrap; }
    .embed-try-link { margin-left: 0; width: 100%; }
}

/* ─── /build "ready to embed" strip ──────────────────────────────────────
   Sits between the nav and the 3-pane build layout. Compact, declarative.
   The actual embed snippet + copy action live on /v3/studio/{id}/publish. */
.form-ready-strip {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 10px 22px;
    background: linear-gradient(90deg, var(--accent-soft), rgba(238, 242, 255, 0.4));
    border-bottom: 1px solid var(--ink-100);
    font-size: 13px;
    color: var(--ink-700);
    flex-shrink: 0;
}
.frs-dot {
    width: 8px; height: 8px;
    border-radius: 999px;
    background: var(--accent);
    box-shadow: 0 0 0 4px rgba(79, 70, 229, 0.18);
    flex-shrink: 0;
}
.frs-title { font-weight: 600; color: var(--ink-900); }
.frs-hint { color: var(--ink-500); }

body.studio-body.no-page-scroll > .form-ready-strip,
body.studio-body.no-page-scroll > .email-verify-banner,
body.studio-body.no-page-scroll > .llm-degraded-banner,
body.studio-body.no-page-scroll > .unpublished-banner { flex-shrink: 0; }

/* ─── Plan questionnaire — Claude-Design-inspired chip layout ────────────
   Used by the right pane of /v3/studio/{id}/plan (plan.html).
   Single-select: clicking a chip fills it; clicking again falls back to
   "Decide for me". Multi-select: toggle any number; escape via "Other…".
   Freeform: plain text input, blank = use default.                       */
.plan-questions {
    background: var(--canvas);
    min-height: 0;
    min-width: 0;
    /* Explicit full-row height — without this, the grid row auto-sizes
       to content, the flex column has no upper bound, and the inner
       scroll container never triggers its scrollbar. Mirrors .plan-chat. */
    height: 100%;
    overflow: hidden;
    border-right: 1px solid var(--ink-100);
    display: flex;
    flex-direction: column;
}
.plan-questions-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 40px 40px 40px;
}
.plan-questions-inner { max-width: 560px; margin: 0 auto; }

/* ── Event-requirements checklist on /plan ────────────────────────
   Systemic ask layer: a grouped checklist that surfaces what's
   still needed before the event can practically go live. Sits below
   the Claude-Design questionnaire so the organiser confirms inline
   before clicking Continue. Rows auto-save via PATCH — no submit,
   no navigation loss. */
.plan-req {
    margin: 40px 0 8px;
    padding: 20px 22px 16px;
    background: #fff;
    border: 1px solid var(--ink-100);
    border-radius: 14px;
    box-shadow: 0 14px 32px -26px rgba(11, 15, 25, 0.15);
}
.plan-req[data-ready="true"] {
    border-color: #a7f3d0;
    box-shadow: 0 14px 32px -26px rgba(16, 185, 129, 0.25);
}
.plan-req-header { margin-bottom: 14px; }
.plan-req-header h2 {
    font-family: 'Instrument Serif', Georgia, serif;
    font-weight: 400;
    font-size: 22px;
    margin: 0 0 4px;
    color: var(--ink-900);
    letter-spacing: -0.005em;
}
.plan-req-progress {
    margin: 0;
    font-size: 13px;
    color: var(--ink-500);
}
.plan-req-group { margin-top: 18px; }
.plan-req-group:first-of-type { margin-top: 6px; }
.plan-req-group-label {
    font-size: 10.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-500);
    margin-bottom: 10px;
}
.plan-req-row {
    padding: 12px 14px;
    margin-bottom: 10px;
    background: #fafafa;
    border: 1px solid var(--ink-100);
    border-radius: 10px;
    transition: border-color 0.12s, background 0.12s;
}
.plan-req-row.status-satisfied {
    background: #f7fffb;
    border-color: #a7f3d0;
}
.plan-req-row.status-missing_required {
    background: #fffbf5;
    border-color: #fde68a;
}
.plan-req-row-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 10px; flex-wrap: wrap;
    margin-bottom: 6px;
}
.plan-req-label {
    font-size: 13.5px; font-weight: 600;
    color: var(--ink-900);
    display: inline-flex; align-items: center; gap: 8px;
}
.plan-req-req {
    font-size: 9.5px; font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    padding: 2px 6px;
    border-radius: 3px;
    background: #fef2f2; color: #991b1b;
}
.plan-req-status-badge {
    font-size: 10.5px; font-weight: 700;
    letter-spacing: 0.04em;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--ink-100);
    color: var(--ink-500);
}
.status-satisfied .plan-req-status-badge { background: #ecfdf5; color: #047857; }
.status-missing_required .plan-req-status-badge { background: #fef3c7; color: #b45309; }
.plan-req-input {
    width: 100%;
    padding: 9px 11px;
    border: 1px solid var(--ink-100);
    border-radius: 8px;
    font: inherit;
    font-size: 13.5px;
    color: var(--ink-900);
    background: #fff;
}
.plan-req-input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}
.plan-req-why {
    margin-top: 6px;
    font-size: 11.5px;
    color: var(--ink-500);
    line-height: 1.5;
}
.plan-req-error {
    margin-top: 6px;
    font-size: 11.5px;
    color: #991b1b;
}
/* Invalid-input visual — fires when the type-aware validator
   (URL etc.) rejects the current value. Red border + soft red
   ring so the row reads as 'fix me' without the row-level
   shake animation reserved for the Continue-pressed case. */
.plan-req-row.is-invalid .plan-req-input {
    border-color: #fecaca;
    background: #fff7f7;
}
.plan-req-row.is-invalid .plan-req-input:focus {
    border-color: #dc2626;
    box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.15);
}

/* Continue-blocked: shake the offending row + flash a red border.
   Triggered when the organiser hits Continue with required fields
   still empty. Honours prefers-reduced-motion by staying still and
   just swapping border colour. */
@keyframes plan-req-shake {
    0%, 100% { transform: translateX(0); }
    20% { transform: translateX(-6px); }
    40% { transform: translateX(5px); }
    60% { transform: translateX(-3px); }
    80% { transform: translateX(2px); }
}
.plan-req-shake {
    animation: plan-req-shake 0.45s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
    border-color: #f87171 !important;
    background: #fef2f2 !important;
}
@media (prefers-reduced-motion: reduce) {
    .plan-req-shake { animation: none; }
}

/* Soft attention pulse for rows whose VALUE just changed via a
   voice edit. Distinct from .plan-req-shake (which means "invalid /
   missing") — this is a positive cue that says "we just updated
   this field for you, look here". 1.6s total: a quick brighten,
   gentle border glow, fade back to baseline. Indigo accent so it
   reads as a system update rather than a warning.

   Triggered by refreshPlanPanelFromServer() in plan.html on every
   successful voice edit. Removes itself on animation end so a
   second flash on the same row replays cleanly. */
@keyframes plan-req-flash {
    0%   { background: #eef2ff; border-color: #6366f1;
           box-shadow: 0 0 0 3px rgba(99,102,241,0.12); }
    60%  { background: #f5f3ff; border-color: #a5b4fc;
           box-shadow: 0 0 0 6px rgba(99,102,241,0.06); }
    100% { background: inherit;  border-color: inherit;
           box-shadow: none; }
}
.plan-req-flash {
    animation: plan-req-flash 1.6s ease-out both;
}
@media (prefers-reduced-motion: reduce) {
    .plan-req-flash {
        animation: none;
        background: #eef2ff !important;
        border-color: #6366f1 !important;
        transition: background 1.6s ease-out, border-color 1.6s ease-out;
    }
}

/* Inline summary above the footer that names every still-missing
   required field. Replaces any prior note — the gate handler
   guarantees this element renders at most once at any moment. */
.plan-req-block-note {
    margin: 14px auto 0;
    max-width: 560px;
    padding: 10px 14px;
    background: #fef2f2;
    border: 1px solid #fecaca;
    border-radius: 8px;
    color: #991b1b;
    font-size: 12.5px;
    font-weight: 500;
    line-height: 1.5;
}

/* ── Floating TOC on /plan ─────────────────────────────────────
   Sticky right-hand mini-nav. Each item is a dot + label; the dot
   fills as you scroll into the matching section (driven by the
   IntersectionObserver in plan.html). Hidden under 1100px because
   the questions column already fills the screen at narrower
   widths and the TOC would crowd it. */
.plan-toc {
    position: fixed;
    top: 50%;
    /* Align with the questions column's right edge instead of being
       pinned 24px from the viewport edge. The plan layout's main
       column maxes out at ~720px and is centred; on a 1440px screen
       the right edge sits around 1080px. clamp() keeps the panel
       hugging that edge across viewport widths so the composition
       reads as a paired column, not a floating outlier. */
    right: clamp(24px, calc((100vw - 720px) / 2 - 232px), 80px);
    transform: translateY(-50%);
    z-index: 40;
    max-height: 70vh;
    overflow-y: auto;
    background: rgba(255, 255, 255, 0.95);
    backdrop-filter: blur(10px);
    border: 1px solid var(--ink-100);
    border-radius: 14px;
    padding: 14px 14px 12px;
    box-shadow:
        0 1px 2px rgba(15, 23, 42, 0.04),
        0 18px 36px -22px rgba(11, 15, 25, 0.18);
    width: 220px;
    pointer-events: auto;
}
/* Heading row — small uppercase label + a subtle progress bar
   underneath that fills as the active section advances down the
   list. Gives the panel a clear identity ('jump to') and a sense
   of progress without taking real estate. */
.plan-toc-head {
    display: flex; flex-direction: column; gap: 6px;
    padding: 0 4px 10px;
    margin-bottom: 8px;
    border-bottom: 1px solid var(--ink-100);
}
.plan-toc-title {
    font-size: 10.5px; font-weight: 700;
    letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--ink-500);
}
.plan-toc-progress {
    height: 3px; border-radius: 999px;
    background: rgba(11, 15, 25, 0.06);
    position: relative;
    overflow: hidden;
}
.plan-toc-progress::after {
    content: '';
    position: absolute; left: 0; top: 0; bottom: 0;
    width: var(--plan-toc-progress, 0%);
    background: linear-gradient(90deg, #818cf8 0%, #4f46e5 100%);
    border-radius: 999px;
    transition: width 220ms ease;
}
.plan-toc-list {
    list-style: none;
    padding: 0; margin: 0;
    display: flex; flex-direction: column;
    gap: 2px;
}
.plan-toc-item a {
    display: flex; align-items: center; gap: 10px;
    padding: 6px 6px;
    color: var(--ink-500);
    text-decoration: none;
    font-size: 12px;
    line-height: 1.35;
    border-radius: 6px;
    transition: background 0.12s, color 0.12s;
}
.plan-toc-item a:hover {
    color: var(--ink-900);
    background: var(--ink-100);
}
.plan-toc-dot {
    width: 8px; height: 8px;
    border-radius: 999px;
    background: transparent;
    border: 1.5px solid var(--ink-200, #d4d4d8);
    flex-shrink: 0;
    transition: background 0.12s, border-color 0.12s, transform 0.12s;
}
.plan-toc-item a.is-active {
    color: var(--ink-900);
    font-weight: 600;
    background: rgba(79, 70, 229, 0.06);
}
.plan-toc-item a.is-active .plan-toc-dot {
    background: var(--accent);
    border-color: var(--accent);
    transform: scale(1.15);
}
.plan-toc-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1;
    min-width: 0;
}
@media (max-width: 1100px) {
    .plan-toc { display: none; }
}


.plan-q-header { margin-bottom: 36px; }
.plan-q-title {
    font-family: 'Instrument Serif', Georgia, serif;
    font-weight: 400;
    font-size: 40px;
    line-height: 1.1;
    letter-spacing: -0.01em;
    color: var(--ink-900);
    margin: 0 0 12px;
}
.plan-q-sub {
    font-size: 15px; color: var(--ink-500); line-height: 1.55;
    max-width: 560px; margin: 0;
}
.plan-q-sub em { color: var(--ink-700); font-style: italic; }

.plan-q-section {
    padding: 24px 0;
    border-top: 1px solid var(--ink-100);
}
.plan-q-section h2 {
    font-size: 17px;
    font-weight: 600;
    color: var(--ink-900);
    margin: 0 0 6px;
    letter-spacing: -0.01em;
}
.plan-q-hint {
    font-size: 13px; color: var(--ink-500); margin: 0 0 14px; line-height: 1.5;
}

.plan-q-chips {
    display: flex; flex-wrap: wrap; gap: 8px;
}
.plan-chip {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 8px 14px;
    background: var(--surface);
    border: 1px solid var(--ink-100);
    border-radius: 999px;
    font-family: inherit;
    font-size: 13px;
    color: var(--ink-700);
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s, color 0.15s, box-shadow 0.15s;
}
.plan-chip:hover {
    border-color: var(--accent);
    color: var(--accent);
    background: var(--accent-soft);
}
.plan-chip.selected {
    border-color: var(--ink-900);
    background: var(--ink-900);
    color: #fff;
    font-weight: 500;
}
.plan-chip.selected:hover {
    border-color: var(--ink-900);
    background: var(--ink-900);
    color: #fff;
}
.plan-chip-escape {
    border-style: dashed;
    color: var(--ink-500);
}
.plan-chip-escape.selected {
    border-style: solid;
    background: var(--ink-900);
    color: #fff;
}
.plan-chip-other {
    color: var(--ink-500);
    border-style: dashed;
}

.plan-q-freeform { margin-top: 4px; }
.plan-q-input {
    width: 100%;
    padding: 10px 14px;
    border: 1px solid var(--ink-100);
    border-radius: 10px;
    font-family: inherit;
    font-size: 14px;
    color: var(--ink-900);
    background: var(--surface);
    transition: border-color 0.15s, box-shadow 0.15s;
}
.plan-q-input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

/* Action bar pinned outside the questionnaire scroll area so it can
   never overlap or bleed under a chip row. Full width of the right
   pane, with the inner row re-centred to the 560px questionnaire grid. */
.plan-q-footer {
    flex: 0 0 auto;
    background: var(--canvas);
    border-top: 1px solid var(--ink-100);
    box-shadow: 0 -8px 20px -14px rgba(15, 23, 42, 0.12);
    /* Mirror the plan-layout-no-chat 2-column grid so the footer content
       sits over the questions column, not the full viewport width. */
    display: grid;
    grid-template-columns: 360px 1fr;
    padding: 0;
}
.plan-q-footer-inner {
    grid-column: 2;
    /* Match plan-questions-scroll padding + plan-questions-inner max-width
       so the left/right edges of the footer content line up exactly with
       the content box above. 1056px = 960px inner + 48px padding each side. */
    padding: 16px 48px;
    max-width: 1056px;
    margin: 0 auto;
    width: 100%;
    display: flex;
    align-items: center;
    gap: 10px;
    box-sizing: border-box;
}
/* Counter row — "X of Y total fields left" sits on the far left as
   plain text (the at-a-glance progress reading), followed by the
   "X of Y required fields left" green pill (the actual go/no-go
   signal). Skip + Proceed live on the far right via margin-left:auto
   on the skip button. Matches the 2026-06-10 design ref where total-
   fields is a quiet counter and required-fields is the highlighted
   pill. */
.plan-q-progress {
    font-size: 13px;
    font-weight: 500;
    color: var(--ink-700, #475569);
    line-height: 1.4;
    white-space: nowrap;
    padding: 0;
    background: transparent;
    border: 0;
    border-radius: 0;
}
.plan-q-required-count {
    font-size: 12px;
    font-weight: 600;
    padding: 3px 9px;
    border-radius: 999px;
    background: #ecfdf5;
    color: #047857;
    border: 1px solid #a7f3d0;
    line-height: 1.4;
    white-space: nowrap;
}
.plan-q-required-count + #btn-skip-build,
.plan-q-progress + #btn-skip-build {
    margin-left: auto;
}
.plan-q-progress:empty,
.plan-q-required-count:empty { display: none; }
/* Continue button — visibly inactive when the required-fields gate
   would block the click. The click handler still fires (so the existing
   shake-and-scroll feedback still works as a backup), but the button
   reads as not-yet-actionable so the organiser doesn't bounce off
   thinking the button is broken. */
.btn-gradient-forward.is-blocked {
    opacity: 0.55;
    cursor: not-allowed;
    box-shadow: none;
    filter: saturate(0.7);
}
.btn-gradient-forward.is-blocked:hover {
    transform: none;
    box-shadow: none;
}

/* Preview frame flash — fires after every chat submit so the organizer's
   eye follows the edit into the iframe even when only the landing page
   changed (no specific builder row to highlight). */
.preview-frame { transition: box-shadow 0.2s ease-out; }
.preview-frame.flash { animation: flash-preview 1.6s ease-out; }
@keyframes flash-preview {
    0%   { box-shadow: 0 0 0 0 rgba(79, 70, 229, 0.55), 0 12px 40px rgba(79, 70, 229, 0.25); }
    30%  { box-shadow: 0 0 0 6px rgba(79, 70, 229, 0.35), 0 18px 48px rgba(79, 70, 229, 0.28); }
    100% { box-shadow: 0 0 0 0 rgba(79, 70, 229, 0), 0 4px 12px rgba(15, 23, 42, 0.08); }
}

.btn-link {
    background: transparent; border: none; color: var(--accent);
    font-size: 12px; cursor: pointer; padding: 4px 8px; border-radius: 4px;
    font-family: inherit;
}
.btn-link:hover { background: var(--accent-soft); }
.mini-link { background: transparent; border: none; color: var(--accent); font-size: 12px; cursor: pointer; padding: 0 4px; text-decoration: none; }
.mini-link:hover { text-decoration: underline; }

/* ── Publish improvements ──────────────────────────────────────────────── */
.payment-choice { position: relative; }
.payment-choice.recommended { border-color: var(--accent); background: linear-gradient(180deg, #fafbff, #fff); }
.payment-choice.recommended.active { border-color: var(--ink-900); }
.payment-choice h3 { margin-top: 4px; }
.choice-badge {
    display: inline-block;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    padding: 3px 8px;
    border-radius: 999px;
    background: var(--accent-soft);
    color: var(--accent);
    width: fit-content;
    margin-bottom: 4px;
}
.choice-badge.warning { background: #fef3c7; color: #92400e; }

/* Legacy .provider-label rule retired — the label text was moved into the
   .provider-pill-card grid on /v3/studio/{id}/payments. Retained here as
   a comment so future greps find the history. The new .provider-label
   used by /admin/platform/payments (input.provider-input.provider-label)
   is defined in the admin block later in this stylesheet. */

.publish-note { font-size: 12px; color: var(--ink-500); margin: 12px 0 0; }

.embed-header {
    display: flex; justify-content: space-between; align-items: center;
    margin: 14px 0 6px; font-size: 12px; color: var(--ink-500);
    text-transform: uppercase; letter-spacing: 0.06em; font-weight: 600;
}
.embed-header button { text-transform: none; letter-spacing: normal; font-size: 12px; font-weight: 500; }
.embed-block button { display: none; }  /* relocated to embed-header */

.btn-publish-cta {
    padding: 14px 24px;
    font-size: 15px;
    letter-spacing: 0.01em;
    background: linear-gradient(135deg, #ec4899 0%, #8b5cf6 100%);
    box-shadow:
        0 1px 2px rgba(139, 92, 246, 0.18),
        0 8px 24px -6px rgba(236, 72, 153, 0.35);
}
.btn-publish-cta:hover {
    opacity: 1;
    background: linear-gradient(135deg, #db2777 0%, #7c3aed 100%);
    box-shadow:
        0 1px 2px rgba(139, 92, 246, 0.22),
        0 12px 30px -6px rgba(236, 72, 153, 0.45);
}
/* Inside the manage header the Publish CTA shrinks so the nav stays
   flush with the 64px shared height used on /events and /platform. */
.studio-nav .btn-publish-cta { padding: 9px 20px; font-size: 14px; }
.spinner-inline {
    display: inline-block;
    width: 12px; height: 12px;
    border: 2px solid rgba(255,255,255,0.4);
    border-top-color: #fff;
    border-radius: 50%;
    margin-right: 6px;
    animation: spin 0.7s linear infinite;
    vertical-align: -1px;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ── Live banner + share card ──────────────────────────────────────────── */
.live-banner {
    background: var(--surface);
    border: 1px solid var(--ink-100);
    color: var(--ink-700);
    padding: 18px 22px;
    border-radius: var(--radius-lg);
    margin-bottom: 22px;
    box-shadow: var(--shadow-sm);
    transition: background 0.35s, border-color 0.35s, box-shadow 0.35s;
}
.live-banner.is-live {
    background: linear-gradient(135deg, #ecfdf5 0%, #f0fdf4 60%, #fff 100%);
    border-color: #a7f3d0;
    color: #065f46;
    box-shadow: 0 4px 24px rgba(22, 163, 74, 0.08);
}
.live-banner.just-published { animation: banner-in 0.5s cubic-bezier(0.16, 1, 0.3, 1); }
@keyframes banner-in {
    from { opacity: 0; transform: translateY(-8px); }
    to { opacity: 1; transform: translateY(0); }
}
.live-banner-head { display: flex; gap: 12px; align-items: center; margin-bottom: 14px; }
.live-banner-head strong.live-title { font-size: 17px; display: block; color: inherit; }
.live-sub { font-size: 13px; color: var(--ink-500); display: block; margin-top: 2px; }
.live-banner.is-live .live-sub { color: #047857; }
.live-dot {
    width: 10px; height: 10px; border-radius: 50%;
    background: var(--ink-300);
    box-shadow: 0 0 0 4px rgba(156, 163, 175, 0.18);
    flex-shrink: 0;
    transition: background 0.3s, box-shadow 0.3s;
}
.live-banner.is-live .live-dot {
    background: #10b981;
    box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.22);
    animation: live-pulse 1.8s ease-in-out infinite;
}
@keyframes live-pulse {
    0%, 100% { box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.22); }
    50% { box-shadow: 0 0 0 10px rgba(16, 185, 129, 0); }
}
.share-grid { display: grid; gap: 10px; }
.share-url-row {
    display: flex; gap: 8px; align-items: center;
    background: var(--surface); border: 1px solid var(--ink-100);
    border-radius: var(--radius-md); padding: 4px 4px 4px 14px;
    transition: border-color 0.3s;
}
.live-banner.is-live .share-url-row { border-color: #d1fae5; }
.share-url-row input {
    flex: 1; border: none; background: transparent;
    font-size: 13px; font-family: 'SF Mono', Menlo, monospace;
    color: var(--ink-700); outline: none;
}
.share-url-row .btn-primary { padding: 8px 16px; font-size: 13px; }
.share-buttons { display: flex; flex-wrap: wrap; gap: 8px; }
.share-btn {
    display: inline-flex; align-items: center; gap: 7px;
    border: 1px solid #d1fae5; background: var(--surface);
    padding: 8px 14px; border-radius: 999px;
    font-size: 13px; font-weight: 500; color: var(--ink-700);
    text-decoration: none; transition: transform 0.08s, border 0.15s, color 0.15s;
}
.share-btn:hover { transform: translateY(-1px); border-color: #a7f3d0; color: var(--ink-900); }
.share-btn svg { flex-shrink: 0; }
.share-whatsapp { color: #25D366; }
.share-whatsapp:hover { color: #1ebc59; border-color: #25D366; }
.share-linkedin { color: #0a66c2; }
.share-linkedin:hover { color: #0a66c2; border-color: #0a66c2; }
.share-x { color: #0b0f19; }
.share-x:hover { border-color: #0b0f19; }

/* ── Confetti ──────────────────────────────────────────────────────────── */
.confetti {
    position: fixed;
    top: -20px;
    width: 8px; height: 8px;
    border-radius: 2px;
    animation: confetti-fall var(--cf-dur, 2.4s) var(--cf-delay, 0s) linear forwards;
    z-index: 9999;
    pointer-events: none;
}
.confetti.is-strip { width: 4px; height: 16px; border-radius: 1px; }
.confetti.is-circle { border-radius: 50%; }
@keyframes confetti-fall {
    0%   { transform: translateX(0) translateY(0) rotateZ(0) rotateX(0) rotateY(0); opacity: 1; }
    75%  { opacity: 1; }
    100% { transform: translateX(var(--cf-drift, 0px)) translateY(110vh) rotateZ(calc(var(--cf-spin, 1) * 780deg)) rotateX(360deg) rotateY(180deg); opacity: 0.5; }
}

/* ── Responsive ladder ────────────────────────────────────────────────
   Five tiers, ordered widest-first. Each tier ONLY adjusts what the
   previous one didn't get right at this width — no full restyles.

     ≥1340  → flagship desktop (defaults above)
     ≤1340  → 1280px laptops; slim chat + TOC, smaller hero title
     ≤1200  → older laptops; chat tightens further, side cards stack
     ≤1024  → iPad landscape; drop the TOC, widen form to fill,
              tighter form padding
     ≤900   → iPad portrait / small tablet; chat collapses to a 36px
              rail (icon-only); main goes full width
     ≤780   → phones; chat hidden, single column, full-bleed form */
@media (max-width: 1340px) {
    .plan-layout { --plan-chat-w-max: 340px; --plan-chat-w-min: 260px; }
    .plan-layout { --plan-chat-w: min(var(--plan-chat-w), 340px); }
    .plan-toc { width: 220px; }
    .plan-q-section { padding: 20px 0; }
    .plan-main { padding: 32px 28px 80px; }
    .plan-q-title { font-size: 32px; }
}
@media (min-width: 1100px) and (max-width: 1199px) {
    /* Inline layout in the 1100-1199 band (chat drawer removed at this
       range). Cap the chat column tightly so the preview still gets
       ≥800 px of preview width. */
    .build-layout { --chat-w-max: 300px; }
    .build-layout { --chat-w: min(var(--chat-w), 300px); }
}
@media (max-width: 1200px) {
    .build-layout { --chat-w-max: 380px; }
    .build-layout { --chat-w: min(var(--chat-w), 380px); }
    .plan-layout { --plan-chat-w-max: 320px; --plan-chat-w-min: 240px; }
    .plan-layout { --plan-chat-w: min(var(--plan-chat-w), 320px); }
    .plan-toc { width: 200px; }
    .plan-main { padding: 28px 24px 72px; }
    .plan-q-title { font-size: 30px; }
    .plan-grid { grid-template-columns: 1fr; }
    .plan-card-wide { grid-column: span 1; }
    .publish-grid { grid-template-columns: 1fr; }
    .publish-card-wide { grid-column: span 1; }
    .publish-previews { grid-template-columns: 1fr; }
}
@media (max-width: 1024px) {
    /* iPad landscape (1024×768). The TOC is the first to go — its
       value-density drops fast at this width and reclaiming 200px
       lets the questions read like a native form again. Chat stays
       so the AI loop still works on tablets without pulling out a
       laptop. */
    .plan-toc { display: none; }
    .plan-layout { --plan-chat-w-max: 300px; --plan-chat-w-min: 220px; }
    .plan-layout { --plan-chat-w: min(var(--plan-chat-w), 300px); }
    .plan-main { padding: 24px 20px 64px; max-width: none; }
    .plan-q-title { font-size: 28px; }
    .plan-q-section { padding: 16px 0; }
}
@media (max-width: 1023px) {
    /* Tweaks drawer hidden below the 1024-1199 chat-drawer band — no
       slide-in pattern exists at this width yet and the absolute-
       positioned drawer would just overlap the cramped preview. The
       chat-drawer band (1024-1199) keeps the builder available as a
       right-edge slide-in; >=1200 keeps it inline. This rule used to
       sit at max-width:1200 which broke the 1024-1199 drawer (the
       display:none won over the drawer's transform), 2026-05-31. */
    .build-pane.builder { display: none; }
}
@media (max-width: 900px) {
    /* iPad portrait (768×1024) + small Android tablets. Chat compresses
       to an icon rail; tap to expand inline. The full-bleed form is
       the priority — organisers on tablets are usually triaging an
       event mid-meeting, not chatting with the AI. */
    .plan-layout { --plan-chat-w: 0px; }
    .plan-layout { grid-template-columns: 0 1fr; }
    .plan-chat { display: none; }
    .chat-resizer { display: none; }
    .plan-main { padding: 20px 18px 60px; }
}
@media (max-width: 780px) {
    /* Phone — single column, no chat sidebar, full-bleed form.
       (Already most of the heavy lifting; this stanza just keeps the
       legacy build screen + studio nav in step.) */
    .build-layout { grid-template-columns: 1fr; }
    .build-pane.chat { display: none; }
    .plan-layout { grid-template-columns: 1fr; }
    .plan-chat { display: none; }
    .studio-nav-steps { display: none; }
    .plan-main { padding: 18px 14px 52px; }
    .plan-q-title { font-size: 24px; }
    .plan-q-section { padding: 14px 0; }
    /* Footer Continue/Skip — pin bottom on phones so it's reachable
       without thumb-stretching past a long form. */
    .plan-q-footer { grid-template-columns: 1fr; }
    .plan-q-footer-inner { grid-column: 1; max-width: none; padding: 12px 14px; gap: 8px; }
}

/* ─── Registrations screen (Phase 8 — who's registered) ──────────────── */
.registrations-main { max-width: 1200px; margin: 0 auto; padding: 40px 24px 80px; }
.registrations-hero { margin-bottom: 28px; }
.registrations-hero h1 {
    font-family: 'Instrument Serif', Georgia, serif;
    font-weight: 400;
    font-size: 42px;
    letter-spacing: -0.02em;
    margin: 0 0 6px;
}
.registrations-hero p { color: var(--ink-500); margin: 0; font-size: 15px; }

.metric-grid {
    display: grid;
    /* auto-fit packs as many ~180px-min tiles per row as the viewport
       allows. With 5 tiles on /registrations (and 7 on /analytics)
       this fills one row on desktop widths (≥1080) and gracefully
       collapses to 2-col on narrow screens. Replaces the previous
       hard `repeat(4, 1fr)` which forced /registrations to wrap to
       4 + 1 even on wide monitors — Sid's 2026-06-02 feedback. */
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 14px;
    margin-bottom: 28px;
}
.metric-card {
    background: var(--surface);
    border: 1px solid var(--ink-100);
    border-radius: 14px;
    padding: 18px 20px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    box-shadow: var(--shadow-sm);
}
.metric-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-500);
    font-weight: 600;
}
.metric-value {
    font-family: 'Instrument Serif', Georgia, serif;
    font-size: 32px;
    font-weight: 400;
    letter-spacing: -0.01em;
    color: var(--ink-900);
    line-height: 1.1;
}
.metric-sub { font-size: 12px; color: var(--ink-500); }
/* No explicit media-query — `auto-fit` + minmax(180px, 1fr) handles
   the narrow-viewport collapse for us. Below ~380 px the single tile
   simply takes full width. */

.registrations-filters {
    display: flex;
    align-items: flex-end;
    gap: 16px;
    margin-bottom: 18px;
    padding: 14px 18px;
    background: var(--surface);
    border: 1px solid var(--ink-100);
    border-radius: 12px;
}
.registrations-filters .filter-group {
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.registrations-filters label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-500);
    font-weight: 600;
}
.registrations-filters select {
    border: 1px solid var(--ink-100);
    border-radius: 8px;
    padding: 8px 12px;
    font-size: 14px;
    font-family: inherit;
    background: var(--surface);
    min-width: 180px;
    cursor: pointer;
}
.registrations-filters select:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}
.registrations-filters .filter-spacer { flex: 1; }

.registrations-table-wrap {
    background: var(--surface);
    border: 1px solid var(--ink-100);
    border-radius: 14px;
    overflow: hidden;
}
.registrations-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
}
.registrations-table th {
    text-align: left;
    padding: 12px 16px;
    background: #f9fafb;
    color: var(--ink-500);
    font-weight: 600;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    border-bottom: 1px solid var(--ink-100);
}
.registrations-table th.num { text-align: right; }
.registrations-table td {
    padding: 14px 16px;
    border-bottom: 1px solid #f3f4f6;
    color: var(--ink-700);
    vertical-align: middle;
}
.registrations-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.registrations-table td.mono { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 13px; color: var(--ink-500); }
.registrations-table td.ellipsis {
    max-width: 260px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.registrations-table .reg-name { font-weight: 600; color: var(--ink-900); }
.registrations-table tr:last-child td { border-bottom: 0; }

.reg-status {
    display: inline-block;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.reg-status-confirmed,
.reg-status-paid { background: #d1fae5; color: #065f46; }
.reg-status-pending { background: #fef3c7; color: #92400e; }
.reg-status-pending_payment { background: #ffedd5; color: #9a3412; }
.reg-status-cancelled { background: #fee2e2; color: #991b1b; }
.reg-status-refunded { background: #e0e7ff; color: #3730a3; }

/* In-cart partial registrations — distinct from sandbox so the
   organiser can scan the table and tell "no money has changed
   hands yet" apart from a sandbox simulation. Slightly cooler
   tint than sandbox amber. */
.reg-row-partial td { background: #fff7ed; }
.registrations-table .reg-row-partial td s {
    color: var(--ink-500);
}

/* Sandbox rows come from /admin/platform/payments' per-provider
   sandbox toggle. Visually dim them and tag each row so the organiser
   never confuses a simulated order for a real one; the row-level
   metrics strip above already excludes them from revenue + paid count. */
.reg-row-sandbox td { background: #fffbeb; }
.reg-sandbox-tag {
    display: inline-block;
    margin-left: 6px;
    padding: 2px 8px;
    border-radius: 999px;
    background: #fef3c7;
    color: #92400e;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}
.registrations-table .reg-row-sandbox td s {
    color: var(--ink-500);
}

.registrations-note {
    text-align: center;
    color: var(--ink-500);
    font-size: 13px;
    padding: 16px;
    border-top: 1px solid var(--ink-100);
    margin: 0;
}

.registrations-empty {
    text-align: center;
    padding: 60px 24px;
    background: var(--surface);
    border: 1px dashed var(--ink-100);
    border-radius: 14px;
}
.registrations-empty h2 {
    font-family: 'Instrument Serif', Georgia, serif;
    font-weight: 400;
    font-size: 28px;
    margin: 0 0 8px;
    color: var(--ink-900);
}
.registrations-empty p {
    color: var(--ink-500);
    font-size: 15px;
    margin: 0 0 20px;
    max-width: 460px;
    margin-left: auto;
    margin-right: auto;
}

/* Per-row action button (Refund, etc.) — quiet by default, accent on hover */
.row-actions { text-align: right; }
.btn-row-action {
    border: 1px solid var(--ink-100);
    background: var(--surface);
    color: var(--ink-700);
    padding: 6px 14px;
    border-radius: 8px;
    font-size: 12px;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s, color 0.15s;
}
.btn-row-action:hover {
    border-color: var(--accent);
    color: var(--accent);
    background: var(--accent-soft);
}

/* ─── Refund policy row on /publish ────────────────────────────────── */
.refund-policy-row {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-top: 20px;
    padding: 14px 16px;
    background: #f9fafb;
    border: 1px solid var(--ink-100);
    border-radius: 10px;
}
.refund-policy-head { display: flex; flex-direction: column; gap: 2px; }
.refund-policy-label { font-size: 13px; font-weight: 600; color: var(--ink-900); }
.refund-policy-hint { font-size: 12px; color: var(--ink-500); }
.refund-policy-select {
    margin-left: auto;
    min-width: 240px;
    padding: 8px 12px;
    border: 1px solid var(--ink-100);
    border-radius: 8px;
    font-size: 13px;
    font-family: inherit;
    background: var(--surface);
    cursor: pointer;
}
.refund-policy-select:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}
.refund-policy-status { font-size: 12px; color: var(--ink-500); min-width: 90px; }
.refund-policy-status.saving { color: var(--ink-500); }
.refund-policy-status.ok     { color: #16a34a; }
.refund-policy-status.fail   { color: #dc2626; }

/* ─── Refund confirm drawer (slides from right on /registrations) ──── */
.refund-backdrop {
    position: fixed; inset: 0;
    background: rgba(15, 23, 42, 0.45);
    backdrop-filter: blur(2px);
    z-index: 90;
    opacity: 0;
    transition: opacity 0.18s ease-out;
}
.refund-backdrop.open { opacity: 1; }
.refund-drawer {
    position: fixed;
    top: 0; right: 0; bottom: 0;
    width: 440px;
    max-width: 100vw;
    background: var(--surface);
    box-shadow: -16px 0 48px rgba(15, 23, 42, 0.15);
    z-index: 100;
    display: flex;
    flex-direction: column;
    transform: translateX(100%);
    transition: transform 0.22s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.refund-drawer.open { transform: translateX(0); }
.refund-drawer-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 20px 24px;
    border-bottom: 1px solid var(--ink-100);
}
.refund-drawer-head h3 {
    font-family: 'Instrument Serif', Georgia, serif;
    font-weight: 400;
    font-size: 24px;
    letter-spacing: -0.01em;
    margin: 0;
    color: var(--ink-900);
}
.refund-drawer-body {
    flex: 1;
    overflow-y: auto;
    padding: 24px;
    display: flex;
    flex-direction: column;
    gap: 24px;
}
.refund-order-summary {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 14px 16px;
    background: #f9fafb;
    border: 1px solid var(--ink-100);
    border-radius: 10px;
}
.refund-field { display: flex; justify-content: space-between; align-items: center; gap: 16px; }
.refund-field-label { font-size: 12px; color: var(--ink-500); font-weight: 600; letter-spacing: 0.02em; text-transform: uppercase; }
.refund-field-value { font-size: 14px; color: var(--ink-900); font-weight: 500; }
.refund-field-value.mono { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 13px; }
.refund-amount-row label,
.refund-reason-row label {
    display: block;
    font-size: 13px;
    font-weight: 600;
    color: var(--ink-900);
    margin-bottom: 8px;
}
.refund-amount-input {
    display: flex;
    align-items: center;
    border: 1.5px solid var(--ink-100);
    border-radius: 10px;
    overflow: hidden;
    background: var(--surface);
    transition: border-color 0.15s;
}
.refund-amount-input:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}
.refund-amount-currency {
    padding: 10px 14px;
    background: #f3f4f6;
    color: var(--ink-500);
    font-size: 13px;
    font-weight: 600;
    border-right: 1px solid var(--ink-100);
}
.refund-amount-input input {
    flex: 1;
    border: 0;
    padding: 10px 14px;
    font-family: inherit;
    font-size: 16px;
    font-variant-numeric: tabular-nums;
    background: transparent;
    outline: none;
    color: var(--ink-900);
}
.refund-amount-hint {
    font-size: 12px;
    color: var(--ink-500);
    margin: 8px 0 0;
    line-height: 1.5;
}
.refund-reason-row textarea {
    width: 100%;
    border: 1.5px solid var(--ink-100);
    border-radius: 10px;
    padding: 10px 14px;
    font-family: inherit;
    font-size: 14px;
    line-height: 1.5;
    resize: vertical;
    min-height: 80px;
    background: var(--surface);
    color: var(--ink-900);
    transition: border-color 0.15s, box-shadow 0.15s;
}
.refund-reason-row textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}
.refund-error {
    padding: 10px 14px;
    background: #fef2f2;
    border: 1px solid #fecaca;
    border-radius: 8px;
    color: #991b1b;
    font-size: 13px;
    line-height: 1.5;
}
.refund-drawer-foot {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    padding: 16px 24px;
    border-top: 1px solid var(--ink-100);
    background: #f9fafb;
}

/* ─── Organizer identity card on /publish ───────────────────────────── */
#organizer-identity-card details { display: block; }
#organizer-identity-card summary {
    display: block;
    list-style: none;
    cursor: pointer;
    padding: 0;
}
#organizer-identity-card summary::-webkit-details-marker { display: none; }
#organizer-identity-card summary::marker { display: none; }
.organizer-identity-head {
    position: relative;
    padding-right: 28px;
}
.organizer-identity-head::after {
    content: "›";
    position: absolute;
    right: 0;
    top: 50%;
    transform: translateY(-50%) rotate(90deg);
    font-size: 24px;
    color: var(--ink-300);
    transition: transform 0.15s;
}
#organizer-identity-card details[open] .organizer-identity-head::after {
    transform: translateY(-50%) rotate(270deg);
}
.organizer-identity-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px;
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid var(--ink-100);
}
.organizer-identity-grid label {
    display: flex;
    flex-direction: column;
    gap: 6px;
    font-size: 13px;
    font-weight: 600;
    color: var(--ink-900);
}
.organizer-identity-grid label.field-full { grid-column: 1 / -1; }
.organizer-identity-grid label.field-checkbox {
    flex-direction: row;
    align-items: flex-start;
    gap: 10px;
    font-weight: 400;
    font-size: 13px;
    color: var(--ink-700);
    line-height: 1.5;
    padding: 10px 14px;
    background: #f9fafb;
    border-radius: 8px;
}
.organizer-identity-grid label.field-checkbox input { margin-top: 2px; }
.organizer-identity-grid input[type="text"],
.organizer-identity-grid input[type="email"],
.organizer-identity-grid input[type="tel"],
.organizer-identity-grid input[type="url"],
.organizer-identity-grid input[type="number"],
.organizer-identity-grid select,
.organizer-identity-grid textarea {
    border: 1.5px solid var(--ink-100);
    border-radius: 8px;
    padding: 10px 12px;
    font-family: inherit;
    font-size: 14px;
    background: var(--surface);
    color: var(--ink-900);
    transition: border-color 0.15s, box-shadow 0.15s;
    -webkit-appearance: none;
    appearance: none;
}
.organizer-identity-grid select {
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
    background-repeat: no-repeat;
    background-position: right 12px center;
    padding-right: 34px;
}
.organizer-identity-grid input:focus,
.organizer-identity-grid textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}
.organizer-identity-grid textarea { resize: vertical; min-height: 60px; line-height: 1.5; }
.organizer-save-status {
    display: inline-block;
    margin-top: 12px;
    font-size: 12px;
    color: var(--ink-500);
    min-height: 18px;
}
.organizer-save-status.saving { color: var(--ink-500); }
.organizer-save-status.ok { color: #16a34a; }
.organizer-save-status.fail { color: #dc2626; }

/* ─── Compact payment status strip on /publish ────────────────────────
   Replaces the big all-in-one yellow setup card. Full config lives on
   /v3/studio/{id}/payments — this row is just the status readout + a
   clear CTA to go manage it. */
.payment-status-strip {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    padding: 20px 22px !important;
}
.payment-status-strip.unverified {
    border: 2px solid #f59e0b;
    box-shadow: 0 0 0 6px rgba(245, 158, 11, 0.08), var(--shadow-md);
    background: linear-gradient(180deg, #fffbeb, #fff);
}
.payment-status-left {
    display: flex;
    align-items: center;
    gap: 16px;
    flex: 1;
    min-width: 0;
}
.payment-status-title {
    display: block;
    font-size: 15px;
    font-weight: 700;
    color: var(--ink-900);
    margin-bottom: 2px;
}
.payment-status-desc {
    display: block;
    font-size: 13px;
    color: var(--ink-500);
    line-height: 1.45;
}
.payment-status-cta {
    flex-shrink: 0;
    padding: 10px 18px !important;
    font-size: 13px !important;
    font-weight: 600;
    white-space: nowrap;
}

/* ─── Dedicated /payments screen ──────────────────────────────────────── */
.payments-main {
    max-width: 920px;
    margin: 0 auto;
    padding: 40px 24px 80px;
}
.payments-hero { margin-bottom: 36px; }
.payments-hero h1 {
    font-family: 'Instrument Serif', Georgia, serif;
    font-weight: 400;
    font-size: 48px;
    letter-spacing: -0.02em;
    margin: 0 0 8px;
    color: var(--ink-900);
}
.payments-sub {
    font-size: 15px;
    color: var(--ink-500);
    line-height: 1.55;
    margin: 0 0 18px;
    max-width: 620px;
}
.payments-status {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 14px 18px;
    background: var(--surface);
    border: 1px solid var(--ink-100);
    border-radius: 12px;
    margin-top: 12px;
}
.payments-status-badge {
    font-weight: 700;
    font-size: 13px;
    padding: 6px 12px;
    border-radius: 999px;
    white-space: nowrap;
}
.payments-status-badge.status-ok {
    background: #d1fae5;
    color: #065f46;
}
.payments-status-badge.status-todo {
    background: #fee2e2;
    color: #991b1b;
}
.payments-status-desc {
    color: var(--ink-500);
    font-size: 13px;
    line-height: 1.5;
}

/* Tabs that split the Payments page into Setup vs Receipts — keeps the
   screen one-viewport-scannable. Sits between the hero status strip
   and the section cards. Only renders for paid events (free events
   have one surface, so the tab bar would be noise). */
.payments-tabs {
    display: flex;
    gap: 4px;
    margin-bottom: 18px;
    border-bottom: 1px solid var(--ink-100);
    padding-bottom: 0;
}
.payments-tab {
    background: transparent;
    border: 0;
    border-bottom: 2px solid transparent;
    padding: 10px 18px 12px;
    font-family: inherit;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--ink-500);
    cursor: pointer;
    margin-bottom: -1px;
    transition: color 0.12s, border-color 0.12s;
}
.payments-tab:hover { color: var(--ink-900); }
.payments-tab.active {
    color: var(--ink-900);
    border-bottom-color: var(--accent);
}
.payments-tab-panel { display: block; }
.payments-tab-panel[hidden] { display: none; }

.payments-section {
    background: var(--surface);
    border: 1px solid var(--ink-100);
    border-radius: 14px;
    padding: 28px;
    margin-bottom: 18px;
    box-shadow: var(--shadow-sm);
}
.payments-section-head { margin-bottom: 18px; }
.payments-section-head h2 {
    font-size: 20px;
    font-weight: 700;
    letter-spacing: -0.01em;
    margin: 0 0 6px;
    color: var(--ink-900);
}
.payments-section-head p {
    font-size: 13.5px;
    color: var(--ink-500);
    line-height: 1.5;
    margin: 0;
}

/* Fee illustration — appears inside the "How should payments flow?"
   section when e2m_managed is the selected mode. Two stacked blocks:
   a headline fee list and a worked example table so the organizer can
   see the percentage AND the net payout on a concrete ticket price. */
.e2m-fees-panel {
    margin-top: 20px;
    border: 1px solid var(--ink-100);
    border-radius: 12px;
    background: linear-gradient(180deg, #fafbff, #fff);
    padding: 20px 22px;
}
.e2m-fees-head {
    margin-bottom: 14px;
}
.e2m-fees-head h3 {
    font-size: 15px;
    font-weight: 700;
    color: var(--ink-900);
    margin: 0 0 4px;
    letter-spacing: -0.005em;
}
.e2m-fees-head p {
    font-size: 12.5px;
    color: var(--ink-500);
    line-height: 1.5;
    margin: 0;
}
.e2m-fees-list {
    list-style: none;
    padding: 0;
    margin: 0 0 18px;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px 14px;
}
.e2m-fees-list li {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    padding: 8px 0;
    border-bottom: 1px solid #eef0f6;
    font-size: 13px;
}
.e2m-fees-list li:last-child,
.e2m-fees-list li:nth-last-child(2):nth-child(odd) {
    border-bottom: none;
}
.e2m-fee-label { color: var(--ink-500); }
.e2m-fee-value { color: var(--ink-900); font-weight: 600; text-align: right; }

.e2m-fees-example {
    background: #fff;
    border: 1px solid var(--ink-100);
    border-radius: 10px;
    padding: 14px 16px;
}
.e2m-fees-example-title {
    font-size: 11px;
    font-weight: 700;
    color: var(--ink-500);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin-bottom: 8px;
}
.e2m-fees-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13.5px;
}
.e2m-fees-table td {
    padding: 6px 0;
    color: var(--ink-700);
}
.e2m-fees-table td:last-child {
    text-align: right;
    font-variant-numeric: tabular-nums;
    color: var(--ink-900);
}
.e2m-fees-table tr.neg td:last-child { color: #b91c1c; }
.e2m-fees-table tr.net td {
    border-top: 1px solid var(--ink-100);
    padding-top: 10px;
    font-size: 14px;
    color: var(--ink-900);
}
.e2m-fees-note {
    margin: 12px 0 0;
    font-size: 12px;
    color: var(--ink-500);
    line-height: 1.55;
}
@media (max-width: 640px) {
    .e2m-fees-list { grid-template-columns: 1fr; }
}

/* Provider pill grid — wider cards with an extra subtitle line */
.provider-pill-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 10px;
}
.provider-pill-card {
    display: flex;
    flex-direction: column;
    gap: 4px;
    align-items: flex-start;
    border: 1.5px solid var(--ink-100);
    background: var(--surface);
    padding: 14px 18px;
    border-radius: 12px;
    cursor: pointer;
    font-family: inherit;
    text-align: left;
    transition: border-color 0.15s, background 0.15s, transform 0.08s;
}
.provider-pill-card:hover {
    border-color: var(--accent);
    background: var(--accent-soft);
}
.provider-pill-card.active {
    border-color: var(--ink-900);
    background: var(--ink-900);
    color: #fff;
}
.provider-pill-card.active .provider-pill-card-sub { color: rgba(255, 255, 255, 0.7); }
.provider-pill-card-name { font-weight: 600; font-size: 15px; }
.provider-pill-card-sub { font-size: 12px; color: var(--ink-500); }

/* Brand logo slot on each provider card. Fixed height so the grid
   rows line up even with different aspect ratios across the SVGs.
   White-out when the card is active (dark bg) — done via a filter
   because the SVGs have baked-in brand colors we can't theme. */
.provider-pill-logo {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    height: 22px;
    margin-bottom: 8px;
}
.provider-pill-logo svg { max-height: 100%; display: block; }
.provider-pill-card.active .provider-pill-logo svg {
    filter: brightness(0) invert(1);
}

/* Credentials form — was unstyled so labels rendered inline with
   inputs. Grid of two fields per row (single col on narrow widths),
   label stacked above each input. Matches Tito/Stripe's settings feel. */
.byo-creds-fields {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 14px 18px;
    margin-bottom: 16px;
}
.byo-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.byo-field-label {
    font-size: 11.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--ink-500);
}
.byo-field input,
.byo-field textarea {
    padding: 10px 12px;
    border: 1px solid var(--ink-100);
    border-radius: 8px;
    font-family: ui-monospace, SFMono-Regular, Consolas, Menlo, monospace;
    font-size: 13px;
    color: var(--ink-900);
    background: var(--surface);
    width: 100%;
    box-sizing: border-box;
}
.byo-field input:focus,
.byo-field textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.12);
}
.byo-field textarea { font-family: inherit; resize: vertical; min-height: 72px; }

.byo-creds-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}
.byo-test-status {
    font-size: 12px;
    color: var(--ink-500);
}
.byo-test-status.ok { color: #16a34a; }
.byo-test-status.fail { color: #dc2626; }
.byo-test-status.testing { color: var(--ink-500); }

/* Standalone refund-policy row (no longer inside the payment card) */
.refund-policy-standalone {
    display: flex;
    align-items: center;
    gap: 16px;
}
.refund-policy-select-lg {
    flex: 1;
    max-width: 420px;
    padding: 12px 14px;
    border: 1.5px solid var(--ink-100);
    border-radius: 10px;
    font-family: inherit;
    font-size: 14px;
    background: var(--surface);
    cursor: pointer;
}
.refund-policy-select-lg:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

.payments-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 28px;
    padding-top: 20px;
    border-top: 1px solid var(--ink-100);
}

/* ─── Custom domain card on /publish ─────────────────────────────────── */
#custom-domain-card details { display: block; }
#custom-domain-card summary {
    display: block;
    list-style: none;
    cursor: pointer;
}
#custom-domain-card summary::-webkit-details-marker,
#custom-domain-card summary::marker { display: none; }
.custom-domain-body {
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid var(--ink-100);
    display: flex;
    flex-direction: column;
    gap: 16px;
}
.custom-domain-body label.field-full {
    display: flex;
    flex-direction: column;
    gap: 6px;
    font-size: 13px;
    font-weight: 600;
    color: var(--ink-900);
}
.custom-domain-body input {
    border: 1.5px solid var(--ink-100);
    border-radius: 8px;
    padding: 10px 14px;
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: 14px;
    background: var(--surface);
    color: var(--ink-900);
    transition: border-color 0.15s, box-shadow 0.15s;
}
.custom-domain-body input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}
.custom-domain-actions {
    display: flex;
    align-items: center;
    gap: 10px;
}
.cd-status {
    font-size: 12px;
    color: var(--ink-500);
    flex: 1;
}
.cd-status.saving { color: var(--ink-500); }
.cd-status.ok { color: #16a34a; font-weight: 600; }
.cd-status.fail { color: #dc2626; }

.custom-domain-instructions {
    background: #f9fafb;
    border: 1px solid var(--ink-100);
    border-radius: 10px;
    padding: 16px 18px;
    font-size: 13px;
    color: var(--ink-700);
    line-height: 1.55;
}
.custom-domain-instructions strong { color: var(--ink-900); }
.custom-domain-instructions p { margin: 8px 0; }
.dns-table {
    width: 100%;
    border-collapse: collapse;
    margin: 10px 0;
    background: var(--surface);
    border: 1px solid var(--ink-100);
    border-radius: 8px;
    overflow: hidden;
}
.dns-table th {
    text-align: left;
    padding: 8px 12px;
    background: #f3f4f6;
    color: var(--ink-500);
    font-weight: 600;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    border-bottom: 1px solid var(--ink-100);
}
.dns-table td {
    padding: 10px 12px;
    font-size: 13px;
    color: var(--ink-900);
}
.dns-table td.mono {
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
}
.cd-note {
    color: var(--ink-500);
    font-size: 12.5px;
    margin-top: 10px !important;
}

/* ─── Stripe Connect one-click panel ─────────────────────────────────── */
.stripe-connect-panel {
    background: linear-gradient(135deg, #f7fafc 0%, #eef2ff 100%);
    border: 1px solid #c7d2fe;
    border-radius: 12px;
    padding: 18px 20px;
    margin-bottom: 14px;
}
.stripe-connect-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    margin-bottom: 10px;
}
.stripe-connect-head strong {
    display: block;
    font-size: 15px;
    font-weight: 700;
    color: var(--ink-900);
    margin-bottom: 4px;
}
.stripe-connect-hint {
    display: block;
    font-size: 13px;
    color: var(--ink-500);
    line-height: 1.5;
    max-width: 420px;
}
.btn-stripe-connect {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 11px 20px;
    background: #635BFF;  /* Stripe's brand purple */
    color: #fff;
    border: 0;
    border-radius: 10px;
    font-family: inherit;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    box-shadow: 0 4px 14px -4px rgba(99, 91, 255, 0.5);
    transition: transform 0.08s, box-shadow 0.15s;
    white-space: nowrap;
    flex-shrink: 0;
}
.btn-stripe-connect:hover {
    transform: translateY(-1px);
    box-shadow: 0 6px 18px -4px rgba(99, 91, 255, 0.55);
    background: #554BEE;
}
.btn-stripe-connect:active { transform: translateY(0); }
.btn-stripe-connect:disabled { opacity: 0.6; cursor: wait; }
.stripe-connect-toggle {
    text-align: right;
    margin-top: 4px;
}

/* Post-OAuth success banner */
.stripe-success-banner {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 14px 20px;
    background: linear-gradient(180deg, #ecfdf5, #f0fdf4);
    border: 1px solid #86efac;
    border-radius: 12px;
    margin-bottom: 20px;
    box-shadow: 0 4px 16px -8px rgba(22, 163, 74, 0.25);
    opacity: 0;
    transform: translateY(-6px);
    transition: opacity 0.25s ease-out, transform 0.25s ease-out;
}
/* display:flex above outranks the UA's [hidden] { display: none } because
   class beats attribute specificity. Without this rule the hidden banner
   still reserves its padding + margin + border, leaving a ~60px empty
   band between "Share & embed" and the embed card. */
.stripe-success-banner[hidden] { display: none !important; }
.stripe-success-banner.visible {
    opacity: 1;
    transform: translateY(0);
}
.stripe-success-check {
    width: 32px; height: 32px;
    border-radius: 999px;
    background: #16a34a;
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 16px;
    flex-shrink: 0;
}
.stripe-success-banner strong {
    display: block;
    color: #065f46;
    font-size: 14px;
    font-weight: 700;
    margin-bottom: 2px;
}
.stripe-success-banner span:not(.stripe-success-check) {
    display: block;
    color: #065f46;
    font-size: 13px;
}
.stripe-success-banner > div { flex: 1; }
.stripe-success-banner .mini-btn {
    background: transparent;
    border: 0;
    color: #065f46;
    cursor: pointer;
    padding: 4px 8px;
    border-radius: 6px;
    font-size: 14px;
}
.stripe-success-banner .mini-btn:hover { background: rgba(22, 163, 74, 0.1); }

/* ═══════════════════════════════════════════════════════════════════════
   Manage shell — post-create event workspace with left sidebar.
   Replaces the linear step-bar on /publish (and, in follow-ups,
   /payments + /registrations). Expandable: "Coming soon" group signals
   roadmap items (Check-in, Emails, Analytics, Team).
   ═══════════════════════════════════════════════════════════════════════ */

/* Header on manage pages: event name + status badge + primary Publish CTA.
   Reuses .studio-nav but re-slots the middle column for event meta. */
.manage-nav {
    grid-template-columns: auto 1fr auto;
}
.manage-event-meta {
    justify-self: start;
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
}
.manage-event-name {
    font-size: 15px;
    font-weight: 600;
    color: var(--ink-900);
    letter-spacing: -0.01em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 420px;
}
.manage-event-status {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 600;
    padding: 4px 10px;
    border-radius: 999px;
    border: 1px solid var(--ink-100);
    background: var(--surface);
    color: var(--ink-500);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    white-space: nowrap;
}
.manage-event-status .manage-status-dot {
    width: 7px;
    height: 7px;
    border-radius: 999px;
    background: var(--ink-300, #d4d4d8);
}
.manage-event-status.live {
    color: #065f46;
    background: #ecfdf5;
    border-color: #a7f3d0;
}
.manage-event-status.live .manage-status-dot {
    background: #16a34a;
    box-shadow: 0 0 0 3px rgba(22, 163, 74, 0.2);
    animation: managePulse 1.8s infinite;
}
@keyframes managePulse {
    0%, 100% { box-shadow: 0 0 0 3px rgba(22, 163, 74, 0.2); }
    50%      { box-shadow: 0 0 0 5px rgba(22, 163, 74, 0.05); }
}

/* ── Manage-header event switcher ─────────────────────────────────────
   The event-name span across every /registrations, /payments,
   /publish, /domain page is now a trigger button for the switcher
   dropdown. Collapsed state keeps the old visual weight (bold name,
   hover affordance) but adds a subtle chevron so the affordance is
   discoverable. Expanded state anchors a card below the button with
   a search input + the user's events. */
.manage-event-switcher { position: relative; display: inline-flex; align-items: center; }
.manage-event-switcher-trigger {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: transparent;
    border: 1px solid transparent;
    padding: 4px 8px;
    border-radius: 8px;
    cursor: pointer;
    font: inherit;
    color: inherit;
    max-width: 100%;
}
.manage-event-switcher-trigger:hover,
.manage-event-switcher.open .manage-event-switcher-trigger {
    background: var(--ink-50, #f4f4f5);
    border-color: var(--ink-100);
}
.manage-event-switcher-trigger:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.manage-event-switcher-chev {
    color: var(--ink-500);
    font-size: 10px;
    line-height: 1;
    transition: transform 0.15s ease;
}
.manage-event-switcher.open .manage-event-switcher-chev { transform: rotate(180deg); }
.manage-event-switcher-menu[hidden] { display: none; }
.manage-event-switcher-menu {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 80;
    min-width: 320px;
    max-width: 420px;
    background: #fff;
    border: 1px solid var(--ink-100);
    border-radius: 12px;
    box-shadow: 0 20px 40px -18px rgba(0, 0, 0, 0.18), 0 4px 12px -6px rgba(0, 0, 0, 0.06);
    overflow: hidden;
    display: flex;
    flex-direction: column;
}
/* 2026-06-10 — search row redesign: lighter neutral border (no
   accent ring on focus), magnifier icon parked on the right edge,
   no separator line below (the list spaces itself). */
.manage-event-switcher-search-wrap {
    position: relative;
    padding: 10px 12px 6px;
    background: #fff;
}
.manage-event-switcher-search {
    width: 100%;
    padding: 10px 36px 10px 14px;
    font-size: 13.5px;
    border: 1px solid var(--ink-100);
    border-radius: 10px;
    background: #fff;
    color: var(--ink-900);
}
.manage-event-switcher-search::placeholder { color: var(--ink-500); }
.manage-event-switcher-search:focus {
    outline: none;
    border-color: rgba(15, 23, 42, 0.25);
    box-shadow: none;
}
.manage-event-switcher-search-icon {
    position: absolute;
    right: 24px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--ink-500);
    pointer-events: none;
}
.manage-event-switcher-list {
    max-height: 360px;
    overflow-y: auto;
    padding: 6px;
}
.manage-event-switcher-state {
    padding: 14px 10px;
    font-size: 13px;
    color: var(--ink-500);
    text-align: center;
}
.manage-event-switcher-state.error { color: #b91c1c; }
.manage-event-switcher-row {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 10px 12px;
    background: transparent;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    text-align: left;
    font: inherit;
}
.manage-event-switcher-row:hover { background: var(--ink-50, #f4f4f5); }
/* 2026-06-10 — current row no longer gets the indigo wash. The
   surrounding studio header already says "you're on Event X", so
   double-marking it inside the dropdown read as noise. Kept aria-
   selected for screen readers. */
.manage-event-switcher-row.is-current { background: transparent; }
.manage-event-switcher-row-name {
    flex: 1;
    min-width: 0;
    font-size: 13.5px;
    font-weight: 500;
    color: var(--ink-900);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Pill restyle — softer mint for "New", neutral grey for "Draft",
   sentence-case (not ALL CAPS), no letter-spacing. Matches the
   2026-06-10 design ref. */
.switcher-pill {
    font-size: 11px;
    font-weight: 600;
    padding: 2px 9px;
    border-radius: 999px;
    text-transform: none;
    letter-spacing: 0;
    flex-shrink: 0;
}
.switcher-pill.live  { background: #d1fae5; color: #047857; }
.switcher-pill.draft { background: #f1f5f9; color: #475569; }

/* Footer — two stacked action buttons (View all events / New
   registration). Replaces the single "All events →" link. The
   primary CTA uses the same gradient as the home "Create
   Registration" + plan "Proceed to Step 2" so the brand accent is
   consistent across the wizard. */
.manage-event-switcher-foot {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 10px 12px 12px;
    border-top: 1px solid var(--ink-100);
    background: #fff;
}
.manage-event-switcher-foot-secondary,
.manage-event-switcher-foot-primary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    padding: 11px 14px;
    border-radius: 12px;
    font-size: 13.5px;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    transition: transform 120ms ease, box-shadow 120ms ease, border-color 120ms ease;
}
.manage-event-switcher-foot-secondary {
    background: #fff;
    color: var(--ink-900, #0f172a);
    border: 1px solid rgba(15, 23, 42, 0.12);
}
.manage-event-switcher-foot-secondary:hover {
    border-color: rgba(15, 23, 42, 0.24);
    background: #f8fafc;
}
.manage-event-switcher-foot-primary {
    background: linear-gradient(135deg, #7c3aed 0%, #db2777 100%);
    color: #fff;
    border: 0;
    box-shadow: 0 10px 24px -10px rgba(124, 58, 237, 0.55);
}
.manage-event-switcher-foot-primary:hover {
    transform: translateY(-1px);
    box-shadow: 0 14px 32px -12px rgba(124, 58, 237, 0.7);
}
.manage-event-switcher-foot-primary:active { transform: translateY(0); }

/* Inside the trigger the event name reuses its old look — max-width
   shrinks to leave room for the chevron. */
.manage-event-switcher-trigger .manage-event-name { max-width: 360px; }
.manage-nav-right {
    justify-self: end;
    display: flex;
    align-items: center;
    gap: 14px;
}

/* Two-column layout: sidebar + main content. Sidebar sticks.
   `minmax(0, 1fr)` instead of bare `1fr` so a wide grandchild (a long
   <table>, the .plans-grid 240px-min cards, an unbroken text run) can
   never push the main column past its share — that was making the
   anonymous /v3/plans view balloon to ~624px on a 398px viewport,
   which let auto-fit place 2 plan cards per row and chopped the right
   card off the screen. With `minmax(0, …)` the column shrinks to
   viewport and overflowing children stay inside their own scroll
   wrapper instead of stretching the page. */
.manage-layout {
    display: grid;
    grid-template-columns: 240px minmax(0, 1fr);
    gap: 0;
    min-height: calc(100vh - 64px);
    align-items: start;
}
/* Anonymous-visitor variant — public /v3/plans renders the account
   shell without a sidebar (since the account nav is meaningless until
   sign-up). Make main span the full grid so there's no 240px void on
   the left. min-width: 0 belt-and-braces against the same overflow
   path on viewports where the @media below has already collapsed the
   layout to a single column. */
.manage-main-full {
    grid-column: 1 / -1;
    max-width: 1280px;
    margin: 0 auto;
    width: 100%;
    min-width: 0;
}
.manage-sidebar {
    position: sticky;
    top: 64px;
    align-self: start;
    height: calc(100vh - 64px);
    overflow-y: auto;
    border-right: 1px solid var(--ink-100);
    padding: 22px 14px 40px;
    background: var(--surface);

    /* 2026-06-02 — chunky native scrollbar (recently surfaced on
       Windows after the sidebar grew taller with the new icons +
       group nesting) reads as ugly chrome inside an otherwise calm
       rail. Slim it down and only paint the thumb when the user is
       interacting with the sidebar. Two engines:
         • Firefox: scrollbar-width:thin + transparent track colour.
         • Chromium/WebKit: ::-webkit-scrollbar styled below.
       Fall-back: any engine without either feature gets the native
       bar — accessible, ugly, fine. */
    scrollbar-width: thin;
    scrollbar-color: transparent transparent;
}
.manage-sidebar:hover,
.manage-sidebar:focus-within {
    /* Reveal the thumb only when the user is actually engaged with
       the sidebar. Track stays transparent so the rail keeps reading
       as one surface, not two. */
    scrollbar-color: rgba(15, 23, 42, 0.22) transparent;
}
.manage-sidebar::-webkit-scrollbar {
    /* 6px is the sweet-spot — readable as a scroll affordance, narrow
       enough to disappear into the rail when there's nothing to drag. */
    width: 6px;
}
.manage-sidebar::-webkit-scrollbar-track {
    background: transparent;
}
.manage-sidebar::-webkit-scrollbar-thumb {
    /* Transparent until hover. Setting it to transparent (rather than
       0-width) keeps the thumb hit-target alive so a power user who
       knows where to click can still drag without hovering first. */
    background: transparent;
    border-radius: 4px;
    transition: background 160ms ease;
}
.manage-sidebar:hover::-webkit-scrollbar-thumb,
.manage-sidebar:focus-within::-webkit-scrollbar-thumb {
    background: rgba(15, 23, 42, 0.22);
}
.manage-sidebar::-webkit-scrollbar-thumb:hover {
    background: rgba(15, 23, 42, 0.38);
}
.manage-nav-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.manage-nav-label {
    font-size: 11px;
    font-weight: 600;
    color: var(--ink-500);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    padding: 10px 12px 6px;
}
.manage-nav-label-sub { margin-top: 18px; }

/* ── Collapsible nav groups (2026-06-03) ──────────────────────────
   All four sidebar groups (Set up / Attendees / Money / Account) now
   use <details> + <summary>, so the visual treatment lands in one
   place. Per Sid's screenshot: each group reads as a soft grey pill-
   shaped bar with the uppercase label on the left and a chevron on
   the right that rotates when open. Click anywhere on the summary
   toggles. The native disclosure triangle is hidden. */
.manage-nav-group {
    margin-top: 14px;
}
.manage-nav-group:first-of-type {
    margin-top: 0;
}
.manage-nav-group-summary {
    /* Override the bare-label padding inherited from .manage-nav-label
       so the bar reads as a tappable element, not floating text. */
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 9px 12px 9px 14px;
    margin: 0 0 4px;
    background: var(--ink-50, #f3f4f6);
    border-radius: 8px;
    cursor: pointer;
    list-style: none;
    user-select: none;
    transition: background 0.12s;
}
.manage-nav-group-summary:hover {
    background: var(--ink-100, #e5e7eb);
}
.manage-nav-group-summary::-webkit-details-marker,
.manage-nav-group-summary::marker {
    display: none;
}
.manage-nav-group-summary::after {
    /* Custom chevron via CSS-painted SVG so we don't ship an extra
       inline icon per group. Rotates 180° when the parent <details>
       is open. */
    content: '';
    width: 12px;
    height: 12px;
    flex-shrink: 0;
    background-color: var(--ink-500, #4b5563);
    mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>") center/12px no-repeat;
    -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>") center/12px no-repeat;
    transition: transform 0.18s ease;
}
.manage-nav-group[open] > .manage-nav-group-summary::after {
    transform: rotate(180deg);
}
/* The Money summary embeds an inline warning chip. Keep it from
   pushing the chevron when present — flex justify still wins, but
   we don't want the label group to wrap if the chip is wide. */
.manage-nav-group-summary > * { min-width: 0; }

.manage-nav-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 8px 12px;
    font-size: 13.5px;
    font-weight: 500;
    color: var(--ink-700);
    border-radius: 8px;
    text-decoration: none;
    transition: background 0.12s, color 0.12s;
    cursor: pointer;
    line-height: 1.3;
}

/* 2026-06-02 — sidebar icons (manage + account). Inline SVG with
   fill="currentColor", so the icon recolours with text on hover /
   active without per-state CSS. .manage-nav-text gets flex:1 so
   end-of-row badges (NEW pill, Max plan badge, warn dot) still hug
   the right edge — justify-content:space-between stays a no-op
   because the text claims all the leftover space. Items without an
   icon (back link, locked rows handled via ::after) render unchanged
   — they have no .manage-nav-icon child to mind. */
.manage-nav-icon {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--ink-500, #626772);
}
/* Reserve the same 20px slot for items that don't have an icon yet
   (account sidebar's My tickets / Plans / Billing history — design
   hasn't shipped icons for those). Keeps the text column aligned
   across the whole sidebar so the rhythm reads as deliberate, not
   misaligned. Remove the class once each item gets a real icon. */
.manage-nav-icon-placeholder {
    visibility: hidden;
    pointer-events: none;
}
.manage-nav-icon svg {
    width: 20px;
    height: 20px;
    display: block;
}
.manage-nav-text {
    flex: 1;
    min-width: 0;
}
.manage-nav-item:hover .manage-nav-icon { color: var(--ink-900); }
.manage-nav-item.active .manage-nav-icon { color: #fff; }
.manage-nav-item.locked .manage-nav-icon,
.manage-nav-item.is-locked .manage-nav-icon { color: var(--ink-300, #b8b8bf); }

.manage-nav-item:hover {
    /* Soft accent-tinted hover — light lavender wash that hints at
       the gradient direction of the active state without competing
       with it. Replaces the neutral #f5f5f7 grey for a more on-brand
       feel across the manage shell. */
    background: linear-gradient(135deg, rgba(236, 72, 153, 0.07) 0%, rgba(139, 92, 246, 0.07) 100%);
    color: var(--ink-900);
}
.manage-nav-item.active {
    /* Pink → purple brand gradient on the current section. Matches
       the .btn-gradient-forward CTA used in the top nav + footer +
       upgrade nudge, so the active sidebar item reads as part of
       the same brand system. Drop shadow tells the eye this is the
       currently-selected surface. */
    background: linear-gradient(135deg, #ec4899 0%, #8b5cf6 100%);
    color: #fff;
    box-shadow: 0 6px 18px -8px rgba(139, 92, 246, 0.45);
}
.manage-nav-item.active:hover {
    background: linear-gradient(135deg, #db2777 0%, #7c3aed 100%);
}

.manage-nav-item.locked {
    color: var(--ink-300, #b8b8bf);
    cursor: default;
    font-weight: 500;
}

/* ── /v3/studio/{id}/publish — Share & embed (new layout) ───────────
   Preview-first hierarchy: the hosted landing iframe is the hero at
   the top of the page, a mode switcher (Popup / Inline / Hosted URL)
   sits beneath, and only the snippet/URL row for the active mode is
   shown. Scoped under .share-preview-stage / .share-mode-* so the
   legacy .embed-mode-* classes (still used elsewhere) stay untouched. */
.share-preview-stage {
    background: #fff;
    border: 1px solid var(--ink-100);
    border-radius: 14px;
    overflow: hidden;
    box-shadow: 0 18px 42px -28px rgba(11, 15, 25, 0.25);
    margin-bottom: 18px;
}
.share-preview-chrome {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    background: #f6f6f8;
    border-bottom: 1px solid var(--ink-100);
    font-size: 12px;
    color: var(--ink-500);
}
.spc-dots { display: inline-flex; gap: 6px; flex-shrink: 0; }
.spc-dot { width: 11px; height: 11px; border-radius: 50%; background: #d1d5db; }
.spc-dot-r { background: #ff5f57; }
.spc-dot-y { background: #ffbd2e; }
.spc-dot-g { background: #28c840; }
.spc-url {
    flex: 1; min-width: 0;
    padding: 6px 12px; background: #fff; border: 1px solid var(--ink-100);
    border-radius: 8px;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12px; color: var(--ink-700);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.spc-open {
    font-size: 12px; font-weight: 500; color: var(--ink-500);
    text-decoration: none; padding: 4px 8px; border-radius: 6px;
    flex-shrink: 0;
}
.spc-open:hover { background: #eceef2; color: var(--ink-900); }
.share-preview-frame {
    position: relative;
    /* Aspect-ratio box sized so the scaled 1440×900 desktop iframe below
       fits the container width exactly. Height = containerWidth * 900/1440. */
    aspect-ratio: 1440 / 900;
    max-height: 560px;
    background: #fff;
    overflow: hidden;
}
.share-preview-frame iframe {
    /* Render the landing page at a real desktop viewport so the hero
       renders in its intended split layout, then scale the whole thing
       down to fit the preview box. JS sets --share-preview-scale based
       on container width; CSS fallback uses 0.62 which matches the
       ~896px content column at desktop. */
    width: 1440px;
    height: 900px;
    border: 0;
    display: block;
    transform-origin: 0 0;
    transform: scale(var(--share-preview-scale, 0.62));
}
/* Floating Register-popup button — overlays the preview iframe at
   bottom-right. Demonstrates what the popup embed feels like on a
   third-party site. data-visible="false" hides in inline / URL mode. */
.share-preview-pop {
    position: absolute;
    right: 22px; bottom: 22px;
    display: flex; align-items: center; gap: 10px;
    transition: opacity 0.2s, transform 0.2s;
}
.share-preview-pop[data-visible="false"] {
    opacity: 0; transform: translateY(8px); pointer-events: none;
}
.spp-hint {
    background: var(--ink-900); color: #fff;
    font-size: 11px; font-weight: 600; letter-spacing: 0.3px;
    padding: 5px 9px; border-radius: 6px;
    position: relative;
    box-shadow: 0 8px 20px -10px rgba(0,0,0,0.35);
}
.spp-hint::after {
    content: ''; position: absolute; right: -4px; top: 50%;
    transform: translateY(-50%) rotate(45deg);
    width: 8px; height: 8px; background: var(--ink-900);
}
.spp-btn {
    /* Uses the same widget.js .e2m-btn styles as the real embed, so if
       the organiser restyles .e2m-btn later this demo updates too. */
    box-shadow: 0 18px 36px -12px rgba(11, 15, 25, 0.45);
    animation: spp-nudge 2.8s ease-in-out infinite;
}
@keyframes spp-nudge {
    0%, 70%, 100% { transform: translateY(0); }
    80%           { transform: translateY(-4px); }
    90%           { transform: translateY(-1px); }
}

/* Mode switcher — compact segmented pills. The default pill gets a
   tiny "Default" badge so "popup is what attendees see out of the
   box" reads without a tooltip. */
.share-modes {
    display: inline-flex;
    gap: 6px;
    padding: 5px;
    background: #f3f0ea;
    border-radius: 12px;
    margin-bottom: 16px;
    flex-wrap: wrap;
}
.share-mode-pill {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 7px 14px;
    font: inherit; font-size: 13px; font-weight: 500;
    color: var(--ink-700);
    background: transparent;
    border: 0;
    border-radius: 8px;
    cursor: pointer;
    transition: background 0.12s, color 0.12s, box-shadow 0.12s;
}
.share-mode-pill:hover { color: var(--ink-900); }
.share-mode-pill.active {
    background: #fff;
    color: var(--ink-900);
    font-weight: 600;
    box-shadow: 0 2px 6px rgba(11, 15, 25, 0.08);
}
.share-mode-pill svg { opacity: 0.8; }
.share-mode-default {
    font-size: 10px; font-weight: 700; letter-spacing: 0.5px;
    text-transform: uppercase;
    padding: 2px 6px; border-radius: 999px;
    background: #e0e7ff; color: #3730a3;
    margin-left: 2px;
}
.share-mode-pill.active .share-mode-default { background: #eef2ff; }

.share-mode-panel {
    background: #fff;
    border: 1px solid var(--ink-100);
    border-radius: 12px;
    padding: 16px 18px;
}
.share-mode-panel-head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 16px; margin-bottom: 12px;
}
.share-mode-panel-head strong {
    display: block; font-size: 14px; font-weight: 600; color: var(--ink-900);
}
.share-mode-panel-head span {
    display: block; font-size: 12.5px; color: var(--ink-500); margin-top: 2px;
}
.share-mode-panel-head code {
    background: #f3f4f6; border: 1px solid var(--ink-100);
    border-radius: 4px; padding: 0 5px;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 11.5px; color: var(--ink-900);
}

.share-snippet {
    /* Light code block — previously dark (var(--ink-900)), which made
       it visually merge with the adjacent dark "Copy snippet" and
       "Try the popup" CTA buttons. Light-grey surface + indigo
       syntax-ish text reads as "code to copy" without competing with
       the button row. Monospace + subtle border keeps it recognisable
       as a code affordance. */
    background: #f8fafc;
    color: #1e293b;
    border: 1px solid var(--ink-100);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12.5px; line-height: 1.55;
    padding: 14px 16px;
    border-radius: 10px;
    white-space: pre-wrap; word-break: break-word;
    margin: 0 0 14px;
    overflow: auto;
}

.share-mode-foot {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    font-size: 12.5px; color: var(--ink-500);
}
.share-mode-demo { flex-shrink: 0; }
.share-mode-foot-chips { gap: 8px; }
.share-mode-foot-label {
    font-size: 11.5px; font-weight: 600; color: var(--ink-500);
    text-transform: uppercase; letter-spacing: 0.4px;
    margin-right: 2px;
}

.share-url-row {
    display: flex; align-items: stretch; gap: 8px;
    margin-bottom: 12px;
}
.share-url-row input {
    flex: 1; min-width: 0;
    padding: 9px 12px; border: 1.5px solid var(--ink-100);
    border-radius: 8px;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 13px; color: var(--ink-900);
    background: #fafafa;
}
.share-url-row input:focus { outline: none; border-color: var(--accent); background: #fff; }
.share-url-row .share-preview-link { align-self: center; flex-shrink: 0; }

/* Off-screen live signal — only exists so legacy post-publish JS can
   flip class names + text without touching the new preview/snippet
   layout. Hidden from layout entirely. */
.share-live-signal {
    position: absolute; left: -9999px; width: 1px; height: 1px;
    overflow: hidden;
}

@media (max-width: 720px) {
    .share-preview-frame { max-height: 420px; }
    .spc-url { font-size: 11px; }
    .share-preview-pop { right: 14px; bottom: 14px; }
    .spp-hint { display: none; }
    .share-mode-panel-head { flex-direction: column; }
    .share-mode-panel-head .btn-primary { align-self: flex-start; }
}

/* "← Back" entry pinned above the nav groups. Lighter weight + a
   hairline under it so it reads as a navigational escape hatch, not
   a same-level menu item. */
.manage-nav-item.manage-nav-back {
    color: var(--ink-500);
    font-weight: 500;
    margin-bottom: 12px;
    padding: 6px 12px;
    border-bottom: 1px solid var(--ink-100);
    border-radius: 8px 8px 0 0;
}
.manage-nav-item.manage-nav-back:hover { color: var(--ink-900); background: transparent; }
.manage-nav-item.locked::after {
    content: 'soon';
    font-size: 9.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    background: var(--ink-100);
    color: var(--ink-500);
    padding: 2px 6px;
    border-radius: 4px;
}
.manage-nav-item.locked:hover { background: transparent; color: var(--ink-300, #b8b8bf); }

/* Warning dot for the Payments item when a paid event lacks a verified
   payment config — same signal the pay-required banner carries. */
.manage-nav-warn {
    width: 8px;
    height: 8px;
    border-radius: 999px;
    background: #f59e0b;
    box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.18);
    flex-shrink: 0;
}
.manage-nav-item.needs-attention { color: var(--ink-900); font-weight: 600; }
/* When the current-page ink-900 background coincides with needs-attention
   (you're on the Payments screen AND payments still need wiring), the two
   rules stacked before made the label the same colour as the background —
   a dark bar with an invisible "Payments" word, per the BIT Mesra screenshot.
   This combined selector wins over both and restores the white label. */
.manage-nav-item.active.needs-attention { color: #fff; }

/* Main content area — same max-width feel as old .publish-main, but sits
   inside the layout grid so it breathes with the sidebar. */
.manage-main {
    max-width: 960px;
    width: 100%;
    margin: 0 auto;
    padding: 40px 32px 80px;
    box-sizing: border-box;
}
/* Wide variant for data/list manage pages (Registrations, Emails, AI
   Voice Support, Analytics, Payments, Payouts). Drops the 960px
   readability cap so wide tables fill the content column instead of
   leaving dead space on the right — matches the admin-platform pages
   (.manage-main-admin). Defined after .manage-main so the equal-
   specificity max-width wins. Sid 2026-06-02 moved Payouts in here
   too — the verification checklist + sales tiles read as data, not
   prose, and lost the rest of the studio's visual rhythm at 960. */
.manage-main-wide { max-width: none; }
.manage-section-head { margin-bottom: 24px; }
.manage-section-head h1 {
    font-family: 'Instrument Serif', Georgia, serif;
    font-weight: 400;
    font-size: 36px;
    letter-spacing: -0.01em;
    margin: 0 0 6px;
    color: var(--ink-900);
}
.manage-section-head p {
    color: var(--ink-500);
    font-size: 14.5px;
    margin: 0;
}

/* /publish: tighten top rhythm so "Share & embed" lands closer to
   actionable content and avoids a large dead band above the first card.
   Numbers halved vs the initial pass — user shot showed >200px of empty
   space between the page nav and the first actionable element. */
.publish-manage-main { padding-top: 16px; padding-bottom: 48px; }
.publish-manage-main .manage-section-head { margin-bottom: 10px; }
.publish-manage-main .manage-section-head h1 { font-size: 32px; }
.manage-main .pay-required-banner {
    margin: 0 0 12px;
    position: sticky;
    top: 8px;
    z-index: 20;
}
.publish-manage-main .stripe-success-banner { margin-bottom: 12px; }

/* Publish disclosure — modal shown pre-publish on paid + e2m_managed
   events. Backdrop + centered card, tight on numbers. Esc / backdrop
   click / Cancel all resolve as "no, don't publish" in the JS wiring.

   z-index sits at 9999 to match the convention documented on
   .studio-nav above — the sticky nav (z=50) creates its own stacking
   context, and at z=200 the overlay was being out-ranked, letting the
   Back + Publish buttons in the nav poke through the scrim and read
   as clickable while a blocking modal was open. 9999 puts the overlay
   above every in-page floater (TOC z=40, regen overlay z=30, refund
   drawer z=100) AND clears the nav's stacking trap. */
.publish-disclosure-overlay {
    /* Defensive !important on the props that govern "is this floating
       above the page". Two prior bugs traced back to this: the sticky
       nav's stacking context out-ranking us, and other stylesheets
       redefining .publish-disclosure-overlay with position: relative.
       The !important here can't be over-ridden by another stylesheet
       loading later in the cascade. Other props (padding, transition,
       backdrop blur) stay overridable for theming. */
    position: fixed !important;
    top: 0 !important; right: 0 !important;
    bottom: 0 !important; left: 0 !important;
    inset: 0 !important;
    z-index: 9999 !important;
    display: flex !important;
    align-items: center !important; justify-content: center !important;
    background: rgba(15, 23, 42, 0.55);
    -webkit-backdrop-filter: blur(3px);
    backdrop-filter: blur(3px);
    padding: 20px;
    margin: 0;
    width: auto; height: auto;
    transform: none;
    opacity: 0;
    transition: opacity 0.18s ease;
}
.publish-disclosure-overlay.open { opacity: 1; }
.publish-disclosure-overlay[hidden] { display: none !important; }
.publish-disclosure-modal {
    background: #fff;
    border-radius: 16px;
    padding: 28px 28px 22px;
    max-width: 460px;
    width: 100%;
    box-shadow: 0 28px 80px -20px rgba(15, 23, 42, 0.5);
    transform: translateY(12px);
    transition: transform 0.2s ease;
}
.publish-disclosure-overlay.open .publish-disclosure-modal { transform: translateY(0); }
.publish-disclosure-modal h2 {
    font-family: 'Instrument Serif', Georgia, serif;
    font-weight: 400;
    font-size: 26px;
    letter-spacing: -0.01em;
    color: var(--ink-900);
    margin: 0 0 10px;
}
.publish-disclosure-modal > p {
    font-size: 14px;
    color: var(--ink-700);
    line-height: 1.55;
    margin: 0 0 14px;
}
.publish-disclosure-list {
    list-style: none;
    padding: 0;
    margin: 0 0 14px;
    border: 1px solid var(--ink-100);
    border-radius: 10px;
    overflow: hidden;
}
.publish-disclosure-list li {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    padding: 10px 14px;
    font-size: 13px;
    color: var(--ink-700);
    border-bottom: 1px solid #f3f4f6;
}
.publish-disclosure-list li:last-child { border-bottom: none; }
.publish-disclosure-list li strong { color: var(--ink-500); font-weight: 500; font-size: 12px; }
.publish-disclosure-list li span { color: var(--ink-900); font-weight: 600; text-align: right; }
.publish-disclosure-note {
    font-size: 12.5px;
    color: var(--ink-500);
    line-height: 1.55;
    margin: 0 0 16px;
}
.publish-disclosure-note a { color: var(--accent); }
.publish-disclosure-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    flex-wrap: wrap;
}
.publish-disclosure-actions .btn-primary { padding: 9px 18px; font-weight: 600; }
.publish-disclosure-actions .btn-ghost { padding: 9px 14px; font-size: 13px; }
@media (max-width: 520px) {
    .publish-disclosure-modal { padding: 22px 20px 18px; }
    .publish-disclosure-actions { flex-direction: column-reverse; }
    .publish-disclosure-actions > * { width: 100%; text-align: center; }
}

/* Header CTA badge — sits to the LEFT of the publish button to give
   organisers a one-glance read of state (Edits pending / Hidden / Ended).
   Colour-coded by severity; "Edits pending" gets the loudest treatment
   because it's the actionable signal. */
.publish-cta-badge {
    display: inline-flex; align-items: center;
    padding: 4px 10px; margin-right: 4px;
    font-size: 11.5px; font-weight: 600;
    letter-spacing: 0.01em;
    border-radius: 999px;
    border: 1px solid transparent;
    white-space: nowrap;
}
.publish-cta-badge--draft         { background: #f3f4f6; color: #4b5563; border-color: #e5e7eb; }
.publish-cta-badge--live_in_sync  { background: #ecfdf5; color: #047857; border-color: #a7f3d0; }
.publish-cta-badge--live_dirty    { background: #fff7ed; color: #c2410c; border-color: #fed7aa; }
.publish-cta-badge--unlisted      { background: #fef2f2; color: #b91c1c; border-color: #fecaca; }
.publish-cta-badge--past          { background: #f5f5f4; color: #57534e; border-color: #e7e5e4; }
.publish-cta-badge--viewer        { background: #eff6ff; color: #1d4ed8; border-color: #bfdbfe; }

/* Disabled-link variant — anchors can't use :disabled, so we mirror it. */
.btn-primary.is-disabled,
.btn-primary[aria-disabled="true"] {
    opacity: 0.55;
    pointer-events: none;
    cursor: not-allowed;
}

/* Each logical block (share/receipts/domain) separated by a subtle rule.
   Use sibling-aware rules instead of :first-of-type because headings use
   <section> too; :first-of-type caused accidental top gaps. */
.manage-main > .manage-block { margin-top: 36px; scroll-margin-top: 72px; }
.manage-main > .manage-section-head + .manage-block { margin-top: 0; }
.manage-main > .pay-required-banner + .manage-block { margin-top: 0; }
.manage-block-head { margin: 0 0 14px; }
.manage-block-head h2 {
    font-size: 18px;
    font-weight: 600;
    letter-spacing: -0.005em;
    margin: 0 0 4px;
    color: var(--ink-900);
}
.manage-block-head p {
    color: var(--ink-500);
    font-size: 13.5px;
    margin: 0;
}

.manage-details-chevron {
    margin-left: auto;
    color: var(--ink-500);
    transition: transform 0.15s;
}
details[open] > summary .manage-details-chevron { transform: rotate(180deg); }

/* Slim embed-card footer — replaces the old big .embed-try box. One
   row: contextual hint · demo button · open-in-new-tab link. */
.embed-foot {
    margin-top: 12px;
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}
.embed-foot-hint {
    color: var(--ink-500);
    font-size: 13px;
    line-height: 1.45;
    flex: 1 1 auto;
    min-width: 260px;
}
.embed-foot-hint code {
    font-family: 'SF Mono', Menlo, Consolas, monospace;
    font-size: 12px;
    padding: 1px 6px;
    background: #f3f4f6;
    border-radius: 4px;
    color: var(--ink-700);
}
.embed-foot-demo {
    flex-shrink: 0;
    padding: 6px 14px !important;
    font-size: 12px !important;
}

/* Slim share strip — replaces old .live-banner hero + hosted-preview card.
   Two rows on wide, stacks on narrow. URL input grows, chips are square
   icons (40×40). The pulsing green dot signals live state. */
.share-strip {
    margin-top: 18px;
    padding: 14px 16px;
    background: var(--surface);
    border: 1px solid var(--ink-100);
    border-radius: var(--radius-lg);
    display: flex;
    flex-direction: column;
    gap: 12px;
}
.share-strip.is-live { background: #f0fdf4; border-color: #bbf7d0; }
.share-strip-lead {
    display: flex;
    align-items: center;
    gap: 10px;
}
.share-strip-lead .live-dot {
    width: 9px;
    height: 9px;
    border-radius: 999px;
    background: var(--ink-300, #9ca3af);
    flex-shrink: 0;
}
.share-strip.is-live .live-dot {
    background: #16a34a;
    box-shadow: 0 0 0 3px rgba(22, 163, 74, 0.2);
    animation: managePulse 1.8s infinite;
}
.share-strip-copy { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.share-strip .live-title { font-size: 13.5px; font-weight: 600; color: var(--ink-900); }
.share-strip .live-sub   { font-size: 12.5px; color: var(--ink-500); }
.share-strip-row {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}
.share-strip-row input {
    flex: 1 1 280px;
    min-width: 0;
    border: 1px solid var(--ink-100);
    background: var(--surface);
    border-radius: 8px;
    padding: 8px 12px;
    font-size: 13px;
    color: var(--ink-700);
    font-family: inherit;
}
.share-strip-row > .btn-primary {
    padding: 8px 14px;
    font-size: 13px;
    flex-shrink: 0;
}
.share-preview-link { flex-shrink: 0; font-size: 12.5px; }
.share-strip-sep {
    width: 1px;
    height: 22px;
    background: var(--ink-100);
    margin: 0 2px;
    flex-shrink: 0;
}
.share-chip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 8px;
    border: 1px solid var(--ink-100);
    background: var(--surface);
    color: var(--ink-700);
    flex-shrink: 0;
    transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.share-chip:hover { border-color: var(--ink-300, #9ca3af); color: var(--ink-900); }
.share-chip.share-whatsapp:hover { color: #25d366; border-color: #25d366; background: #f0fdf4; }
.share-chip.share-linkedin:hover { color: #0a66c2; border-color: #0a66c2; background: #eff6ff; }

/* Landing-page context preview — a small iframe of the hosted landing
   so the organizer can see what their share link actually looks like.
   Height is clipped so only the top fold is visible (hero + reg form). */
.landing-context-preview {
    margin-top: 18px;
    border: 1px solid var(--ink-100);
    border-radius: var(--radius-lg);
    background: #f3f4f6;
    overflow: hidden;
}
.landing-context-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 14px;
    background: var(--surface);
    border-bottom: 1px solid var(--ink-100);
}
.landing-context-label {
    font-size: 12px;
    font-weight: 600;
    color: var(--ink-500);
    text-transform: uppercase;
    letter-spacing: 0.08em;
}
.landing-context-preview iframe {
    width: 100%;
    height: 520px;
    border: 0;
    background: var(--surface);
    display: block;
}

/* Responsive — collapse sidebar below 900px. Users on phones shouldn't
   see the full IA; a horizontal row of pills would be the follow-up. */
@media (max-width: 900px) {
    .manage-layout { grid-template-columns: minmax(0, 1fr); }
    .manage-sidebar {
        position: static;
        height: auto;
        border-right: 0;
        border-bottom: 1px solid var(--ink-100);
        padding: 12px 14px;
    }
    .manage-nav-list { flex-direction: row; flex-wrap: wrap; gap: 4px; }
    .manage-nav-label { display: none; }
    .manage-nav-item.locked { display: none; }
    .manage-main { padding: 28px 20px 60px; }
    .manage-event-name { max-width: 180px; }
}

/* ─── Platform admin (/admin/platform/*) ───────────────────────────────
   Reuses .manage-sidebar / .manage-main / .publish-card shells so the
   shape matches the per-event manage screens the organizer already
   knows. Only the platform-specific bits (stat cards, dense table
   rows, env-var display, foot pill) need net-new styles. */
.platform-sidebar { display: flex; flex-direction: column; }
.platform-sidebar-foot {
    margin-top: auto;
    padding: 14px 12px 4px;
    font-size: 11.5px;
    color: var(--ink-500);
    display: flex; flex-direction: column; gap: 4px;
    border-top: 1px dashed var(--ink-100);
}
.platform-access-note {
    display: inline-flex; align-items: center; gap: 6px;
    color: #92400e;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-size: 10.5px;
}
.platform-access-dot {
    width: 8px; height: 8px; border-radius: 50%;
    background: #f59e0b;
    box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.18);
}
.platform-user { font-size: 11px; color: var(--ink-500); word-break: break-all; }

.platform-stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 12px;
    margin-bottom: 6px;
}
.platform-stat-card {
    background: var(--surface);
    border: 1px solid var(--ink-100);
    border-radius: 12px;
    padding: 16px 18px;
    display: flex; flex-direction: column; gap: 4px;
    box-shadow: var(--shadow-sm);
}
.platform-stat-label {
    font-size: 11px;
    font-weight: 700;
    color: var(--ink-500);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
.platform-stat-value {
    font-family: 'Instrument Serif', Georgia, serif;
    font-size: 32px;
    font-weight: 400;
    color: var(--ink-900);
    line-height: 1.05;
    letter-spacing: -0.01em;
}
.platform-stat-sub { font-size: 12px; color: var(--ink-500); }

.platform-quick-links {
    list-style: none;
    padding: 0; margin: 0;
    display: flex; flex-direction: column;
}
.platform-quick-links li {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: 14px;
    padding: 10px 0;
    border-bottom: 1px solid #f3f4f6;
}
.platform-quick-links li:last-child { border-bottom: none; }
.platform-quick-links a {
    font-weight: 600;
    color: var(--ink-900);
    text-decoration: none;
}
.platform-quick-links a:hover { color: var(--accent); text-decoration: underline; }
.platform-quick-links li span { font-size: 12.5px; color: var(--ink-500); text-align: right; }

/* Filter bar above platform tables — free-text search + status/pay
   dropdowns + live result counter. Client-side only; hides rows via
   .row-hidden. Matches the look of .platform-table for a tight feel. */
.platform-filter-bar {
    display: flex;
    gap: 10px;
    align-items: center;
    margin-bottom: 14px;
    flex-wrap: wrap;
}
.platform-filter-bar input[type="search"] {
    flex: 1 1 320px;
    min-width: 220px;
    padding: 8px 12px;
    border: 1px solid var(--ink-100);
    border-radius: 8px;
    font-size: 13px;
    font-family: inherit;
    background: var(--surface);
    color: var(--ink-900);
}
.platform-filter-bar input[type="search"]:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.12);
}
.platform-filter-bar select {
    padding: 8px 12px;
    border: 1px solid var(--ink-100);
    border-radius: 8px;
    font-size: 13px;
    font-family: inherit;
    background: var(--surface);
    color: var(--ink-700);
    cursor: pointer;
}
.platform-filter-count {
    font-size: 12px;
    color: var(--ink-500);
    margin-left: auto;
    white-space: nowrap;
}
.platform-row-link {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--accent);
    text-decoration: none;
}
.platform-row-link:hover { text-decoration: underline; }

/* Filtered-out rows — hidden via display:none so the table reflows
   and the count is honest. */
.platform-table tbody tr.row-hidden { display: none; }

/* Fee-form inputs — inline number fields on /admin/platform/fees.
   Compact enough to fit inside the table cell without breaking the
   row height. Override-flag dot signals that the current value is
   a DB override of the code default. */
.fee-input {
    width: 110px;
    padding: 6px 10px;
    border: 1px solid var(--ink-100);
    border-radius: 6px;
    font-size: 13px;
    font-family: ui-monospace, SFMono-Regular, Consolas, Menlo, monospace;
    background: var(--surface);
    color: var(--ink-900);
}
.fee-input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
}
.fee-override-flag {
    display: inline-block;
    margin-left: 8px;
    color: #f59e0b;
    font-size: 14px;
    vertical-align: middle;
}

/* Provider edit form — label/notes inputs + enable toggle. Sits inline
   inside the platform-table so the admin can scan all four providers
   at once and edit in place. */
.provider-key {
    display: inline-block;
    font-size: 12px;
    font-family: ui-monospace, SFMono-Regular, Consolas, Menlo, monospace;
    font-weight: 600;
    color: var(--ink-900);
    background: #f3f4f6;
    padding: 2px 8px;
    border-radius: 6px;
    border: 1px solid var(--ink-100);
}
.provider-input {
    display: block;
    width: 100%;
    padding: 6px 10px;
    border: 1px solid var(--ink-100);
    border-radius: 6px;
    font-size: 13px;
    font-family: inherit;
    background: var(--surface);
    color: var(--ink-900);
    margin-bottom: 6px;
}
.provider-input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
}
.provider-input.provider-label { font-weight: 600; }
.provider-input.provider-notes { font-size: 12.5px; color: var(--ink-500); }
.provider-row.provider-disabled .provider-key,
.provider-row.provider-disabled .provider-input { opacity: 0.55; }

/* Tiny on/off toggle — plain checkbox styled as a pill. */
.provider-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 600;
    color: var(--ink-700);
    cursor: pointer;
    user-select: none;
}
.provider-toggle input[type="checkbox"] {
    appearance: none;
    width: 32px;
    height: 18px;
    border-radius: 999px;
    background: #d1d5db;
    position: relative;
    cursor: pointer;
    transition: background 0.15s;
    border: 0;
    margin: 0;
}
.provider-toggle input[type="checkbox"]::after {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 14px;
    height: 14px;
    border-radius: 999px;
    background: #fff;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
    transition: transform 0.15s;
}
.provider-toggle input[type="checkbox"]:checked {
    background: #16a34a;
}
.provider-toggle input[type="checkbox"]:checked::after {
    transform: translateX(14px);
}

.platform-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
    color: var(--ink-700);
}
.platform-table thead th {
    text-align: left;
    font-size: 11px;
    font-weight: 700;
    color: var(--ink-500);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    padding: 12px 16px;
    background: #fafafa;
    border-bottom: 1px solid var(--ink-100);
}
.platform-table tbody td {
    padding: 12px 16px;
    border-bottom: 1px solid #f3f4f6;
    vertical-align: top;
}
.platform-table tbody tr:last-child td { border-bottom: none; }
.platform-row-sub {
    font-size: 11.5px;
    color: var(--ink-500);
    margin-top: 2px;
}
.platform-envline {
    display: flex; align-items: center; gap: 10px;
    margin-bottom: 3px;
    font-size: 12px;
}
.platform-envline code {
    background: #f3f4f6;
    border: 1px solid var(--ink-100);
    border-radius: 4px;
    padding: 1px 5px;
    font-size: 11px;
    color: var(--ink-900);
}
.platform-envvalue {
    font-family: ui-monospace, SFMono-Regular, Consolas, Menlo, monospace;
    color: var(--ink-700);
    font-size: 12px;
}
.platform-envvalue.unset { color: #b45309; font-style: italic; }
.platform-mono {
    font-family: ui-monospace, SFMono-Regular, Consolas, Menlo, monospace;
    font-size: 12px;
    color: var(--ink-700);
}
.platform-muted { color: var(--ink-500); font-size: 12px; }
.platform-country-pill {
    display: inline-block;
    padding: 2px 8px;
    background: var(--accent-soft);
    color: var(--accent);
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    margin-right: 4px;
    margin-bottom: 2px;
}
.platform-badge {
    display: inline-block;
    padding: 3px 9px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    background: #f3f4f6;
    color: var(--ink-700);
}
.platform-badge.ok { background: #dcfce7; color: #166534; }
.platform-badge.warn { background: #fef3c7; color: #92400e; }
.platform-badge.danger { background: #fee2e2; color: #991b1b; }
/* Soft-deleted rows in the platform events table — a light red wash so a
   deleted event can't be mistaken for a live/draft one. The red "Deleted"
   status badge stays crisp (no opacity stacking on the badge). */
.platform-table tr.row-deleted td { background: #fef2f2; }

/* Provider-status pills on /admin/platform/integrations/mail. Green
   when the provider is configured, neutral grey when it isn't. The
   send order strip uses these to make the active/inactive rails
   visible at a glance. */
.platform-pill {
    display: inline-flex;
    align-items: center;
    padding: 4px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    border: 1px solid transparent;
}
.platform-pill-on {
    background: #dcfce7;
    color: #166534;
    border-color: #a7f3d0;
}
.platform-pill-off {
    background: #f3f4f6;
    color: #6b7280;
    border-color: #e5e7eb;
}

/* ─── Plans editor + pricing matrix ───────────────────────────────────── */
/* Country switcher on the Plans page. Horizontal scroll strip of flag +
   name pills. Renders the currently-selected locale summary in bold so
   the page doesn't need two spots to show "what am I looking at". */
.country-switcher {
    background: var(--surface);
    border: 1px solid var(--ink-100);
    border-radius: 12px;
    padding: 16px 18px 14px;
    margin-bottom: 20px;
    box-shadow: var(--shadow-sm);
}
.country-switcher-head {
    display: flex;
    align-items: baseline;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}
.country-switcher-label {
    font-size: 11.5px;
    font-weight: 600;
    color: var(--ink-500);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}
.country-switcher-current {
    font-size: 14px;
    font-weight: 600;
    color: var(--ink-900);
}
.country-switcher-pills {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
}
.country-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 10px;
    background: var(--surface);
    border: 1px solid var(--ink-100);
    border-radius: 999px;
    font-size: 12.5px;
    color: var(--ink-700);
    text-decoration: none;
    transition: border-color 0.12s, background 0.12s, color 0.12s;
}
.country-pill:hover {
    border-color: var(--accent);
    background: var(--accent-soft);
    color: var(--accent);
}
.country-pill.active {
    border-color: var(--ink-900);
    background: var(--ink-900);
    color: #fff;
    font-weight: 600;
}
.country-pill .country-flag { font-size: 14px; line-height: 1; }

/* Local-price annotation under a plan card's USD headline. Lives in the
   same column as .plan-price and shares the bottom border; no extra
   margin so cards don't lengthen when the switcher is off. */
.plan-price-local {
    display: flex;
    align-items: baseline;
    gap: 6px;
    font-size: 13px;
    color: var(--ink-500);
    margin: -14px 0 0;
    padding: 0 0 14px;
    border-bottom: 1px solid var(--ink-100);
    flex-wrap: wrap;
}
.plan-price-local-tax {
    font-size: 11.5px;
    color: var(--ink-500);
    font-weight: 500;
    background: #f3f4f6;
    border-radius: 4px;
    padding: 1px 6px;
}

.plan-matrix {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 14px;
    margin-bottom: 8px;
}
.plan-card {
    position: relative;
    background: var(--surface);
    border: 1px solid var(--ink-100);
    border-radius: 14px;
    padding: 24px 22px 20px;
    box-shadow: var(--shadow-sm);
    display: flex; flex-direction: column;
    min-height: 100%;
}
.plan-card-featured {
    border-color: var(--accent);
    box-shadow: 0 12px 32px -16px rgba(79, 70, 229, 0.35), 0 0 0 3px rgba(79, 70, 229, 0.08);
    transform: translateY(-4px);
}
.plan-badge {
    position: absolute;
    top: -12px; left: 22px;
    padding: 4px 10px;
    background: var(--accent);
    color: #fff;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    box-shadow: 0 4px 12px -4px rgba(79, 70, 229, 0.5);
}
.plan-card-head h3 {
    font-family: 'Instrument Serif', Georgia, serif;
    font-weight: 400;
    font-size: 28px;
    color: var(--ink-900);
    margin: 0 0 6px;
    letter-spacing: -0.01em;
}
.plan-tagline {
    font-size: 13px;
    color: var(--ink-500);
    line-height: 1.5;
    margin: 0 0 18px;
    min-height: 40px;
}
.plan-price {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin-bottom: 4px;
    padding-bottom: 18px;
    border-bottom: 1px solid var(--ink-100);
}
.plan-price-amount {
    font-family: 'Instrument Serif', Georgia, serif;
    font-size: 48px;
    font-weight: 400;
    color: var(--ink-900);
    line-height: 1;
    letter-spacing: -0.02em;
}
.plan-price-contact {
    font-family: 'Instrument Serif', Georgia, serif;
    font-size: 32px;
    font-weight: 400;
    color: var(--ink-900);
    line-height: 1;
    letter-spacing: -0.01em;
}
.plan-price-cadence {
    font-size: 13px;
    color: var(--ink-500);
}
.plan-cta-placeholder {
    margin: 16px 0 10px;
    padding: 11px 18px;
    background: var(--ink-900);
    color: #fff;
    border-radius: 10px;
    text-align: center;
    font-size: 14px;
    font-weight: 600;
    cursor: default;
    letter-spacing: -0.005em;
}
.plan-card-featured .plan-cta-placeholder { background: var(--accent); }
.plan-feature-list {
    list-style: none;
    padding: 0;
    margin: 10px 0 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.plan-feature-list li {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-size: 13px;
    color: var(--ink-700);
    line-height: 1.5;
}
.plan-feature-tick {
    display: inline-flex;
    width: 16px; height: 16px; flex-shrink: 0;
    align-items: center; justify-content: center;
    background: #dcfce7;
    color: #166534;
    border-radius: 999px;
    font-size: 10px;
    font-weight: 700;
    margin-top: 2px;
}
.plan-feature-label { flex: 1; min-width: 0; }
.plan-feature-detail { color: var(--ink-500); }

.plan-edit-card { margin-bottom: 18px; }
.plan-edit-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 16px;
    padding-bottom: 14px;
    border-bottom: 1px solid var(--ink-100);
    flex-wrap: wrap;
}
.plan-edit-title {
    font-size: 18px;
    font-weight: 700;
    color: var(--ink-900);
    margin: 0 0 2px;
    letter-spacing: -0.005em;
}
.plan-edit-key {
    display: inline-block;
    background: #f3f4f6;
    border: 1px solid var(--ink-100);
    border-radius: 4px;
    padding: 1px 6px;
    font-size: 11px;
    color: var(--ink-500);
    font-family: ui-monospace, SFMono-Regular, Consolas, Menlo, monospace;
}
.plan-edit-toggles { display: flex; gap: 16px; flex-wrap: wrap; }
.platform-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12.5px;
    color: var(--ink-700);
    cursor: pointer;
}
.platform-toggle input { accent-color: var(--accent); }
.plan-edit-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 12px 14px;
}
.plan-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--ink-500);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.plan-field-wide { grid-column: span 2; }
.plan-field small {
    font-weight: 400;
    text-transform: none;
    letter-spacing: normal;
    color: var(--ink-500);
    margin-left: 4px;
}
.plan-field input {
    padding: 8px 10px;
    border: 1px solid var(--ink-100);
    border-radius: 8px;
    font-family: inherit;
    font-size: 13px;
    font-weight: 400;
    color: var(--ink-900);
    text-transform: none;
    letter-spacing: normal;
    background: var(--surface);
    transition: border-color 0.15s, box-shadow 0.15s;
}
.plan-field input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}
.plan-edit-actions { margin-top: 16px; display: flex; justify-content: flex-end; }
@media (max-width: 720px) {
    .plan-matrix { grid-template-columns: 1fr; }
    .plan-card-featured { transform: none; }
    .plan-field-wide { grid-column: auto; }
}

/* ── /v3/account/showcase ─────────────────────────────────────────────
   Event showcase / multi-event hub control panel. Styles are scoped to
   .showcase-* classes so they don't leak into the legacy per-event
   domain page that still uses the .cd-* / .dns-table vocabulary. */
.showcase-summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    flex-wrap: wrap;
}
.showcase-summary-stats {
    display: flex;
    gap: 28px;
    flex-wrap: wrap;
}
.showcase-stat { display: flex; flex-direction: column; gap: 2px; }
.showcase-stat strong { font-size: 22px; font-weight: 700; color: var(--ink-900); line-height: 1.1; text-transform: capitalize; }
.showcase-stat span { font-size: 12px; color: var(--ink-500); }

/* ── Accordion (global-standard <details> pattern) ─────────────
   White card, subtle 1px border, chevron on the right, opens
   smoothly. Identical affordance across all showcase sections so
   the page reads as one scan-able list rather than a pile of
   different card styles. No coloured left borders anywhere — flat
   neutrals only. Mirrors the pattern used in Notion / Stripe
   dashboard / Apple System Settings. */
.showcase-acc {
    background: #fff;
    border: 1px solid var(--ink-100);
    border-radius: 10px;
    margin-top: 14px;
    overflow: hidden;
    transition: box-shadow 0.12s;
}
.showcase-acc + .showcase-acc { margin-top: 10px; }
.showcase-acc:hover { box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04); }
.showcase-acc[open] { box-shadow: 0 2px 10px -4px rgba(15, 23, 42, 0.06); }

.showcase-acc-summary {
    list-style: none;
    cursor: pointer;
    display: flex; align-items: center; justify-content: space-between;
    gap: 16px;
    padding: 14px 18px;
    user-select: none;
}
.showcase-acc-summary::-webkit-details-marker { display: none; }
.showcase-acc-summary:hover { background: #fafafa; }
.showcase-acc[open] > .showcase-acc-summary { border-bottom: 1px solid var(--ink-100); }

.showcase-acc-title-group {
    display: flex; flex-direction: column; gap: 2px; min-width: 0;
}
.showcase-acc-title {
    font-size: 14px; font-weight: 600;
    color: var(--ink-900);
    letter-spacing: -0.005em;
}
.showcase-acc-sub {
    font-size: 12.5px;
    color: var(--ink-500);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.showcase-acc-sub code {
    background: var(--ink-100); color: var(--ink-700);
    padding: 0 5px; border-radius: 4px;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 11.5px;
}
.showcase-acc-meta {
    display: inline-flex; align-items: center; gap: 14px; flex-shrink: 0;
}
.showcase-acc-inline-link { font-size: 12px; }
.showcase-acc-chevron {
    color: var(--ink-400);
    font-size: 12px;
    transition: transform 0.15s;
    display: inline-flex; align-items: center; justify-content: center;
    width: 18px; height: 18px;
}
.showcase-acc[open] > .showcase-acc-summary .showcase-acc-chevron {
    transform: rotate(180deg);
}
.showcase-acc-body {
    padding: 16px 18px;
}

/* Preview body: the iframe fills the body edge-to-edge (no inner
   padding — looks more like an actual preview window). */
.showcase-preview-acc .showcase-acc-body { display: none; }  /* unused container */
.showcase-preview-acc[open] .showcase-preview-frame {
    display: block;
}
.showcase-preview-frame {
    position: relative; width: 100%; height: 560px; background: #0b0f19;
}
.showcase-preview-frame iframe {
    width: 100%; height: 100%; border: 0; display: block;
}

.showcase-settings-form { display: contents; }

.showcase-field { display: block; }
.showcase-field > span {
    display: block;
    font-size: 12px;
    font-weight: 600;
    color: var(--ink-700);
    margin-bottom: 6px;
}
.showcase-field input[type="text"] {
    width: 100%;
    padding: 9px 12px;
    border: 1px solid var(--ink-100);
    border-radius: 8px;
    font-size: 13px;
    color: var(--ink-900);
    background: #fff;
}
.showcase-field input[type="text"]:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

.showcase-save-row {
    display: flex; align-items: center; gap: 12px;
    margin-top: 14px;
    padding: 0 2px;
    flex-wrap: wrap;
}

.showcase-modes { display: flex; flex-direction: column; gap: 12px; }
.showcase-mode {
    border: 1px solid var(--ink-100);
    border-radius: 10px;
    padding: 14px 16px;
    background: #fff;
}
.showcase-mode.is-locked { background: #fafafa; opacity: 0.92; }
.showcase-mode-head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 12px; margin-bottom: 10px;
}
.showcase-mode-title { font-size: 14px; font-weight: 600; color: var(--ink-900); }
.showcase-mode-sub { font-size: 12.5px; color: var(--ink-600); margin-top: 2px; }
.showcase-mode-badge {
    font-size: 11px; font-weight: 600; padding: 3px 9px; border-radius: 999px;
    background: #eef2ff; color: #3730a3; text-transform: none; white-space: nowrap;
    border: 1px solid #c7d2fe;
}
.showcase-mode-badge.ok { background: #ecfdf5; color: #065f46; border-color: #a7f3d0; }
.showcase-mode-badge.warn { background: #fef3c7; color: #92400e; border-color: #fde68a; }
.showcase-mode-badge.locked { background: #f5f5f7; color: var(--ink-500); border-color: var(--ink-100); }

.showcase-copy-row {
    display: flex; gap: 8px; align-items: stretch;
}
.showcase-copy-row input[type="text"],
.showcase-copy-row textarea {
    flex: 1; min-width: 0;
    border: 1.5px solid var(--ink-100); border-radius: 8px;
    padding: 9px 12px; font: inherit; font-size: 13px;
    font-family: "SFMono-Regular", Menlo, Consolas, monospace;
    color: var(--ink-900); background: #fff; resize: vertical;
}
.showcase-copy-row textarea { line-height: 1.45; min-height: 56px; }
.showcase-copy-btn { flex-shrink: 0; }
.showcase-mode-foot { font-size: 12px; color: var(--ink-500); margin: 8px 0 0; }
.showcase-mode-foot code { background: var(--ink-50, #f5f5f7); padding: 1px 5px; border-radius: 4px; }

.showcase-cname-row {
    display: flex; gap: 8px; align-items: stretch; margin-bottom: 10px;
}
.showcase-cname-row input[type="text"] {
    flex: 1; min-width: 0;
    border: 1.5px solid var(--ink-100); border-radius: 8px;
    padding: 9px 12px; font: inherit; font-size: 14px;
    color: var(--ink-900); background: #fff;
}
.showcase-cname-row input[type="text"]:focus {
    outline: none; border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}
.showcase-dns-help { margin-top: 4px; }
.showcase-dns-help > p { font-size: 12.5px; color: var(--ink-600); margin: 0 0 10px; }
.showcase-locked-copy {
    background: #fafafa; border: 1px dashed var(--ink-100);
    border-radius: 8px; padding: 12px 14px;
}
.showcase-locked-copy p { margin: 0; font-size: 13px; color: var(--ink-600); }

.showcase-events-subhead {
    font-size: 12px; font-weight: 600; color: var(--ink-700);
    text-transform: uppercase; letter-spacing: 0.4px;
    margin: 14px 0 8px;
}
.showcase-events-subhead:first-of-type { margin-top: 0; }
.showcase-events-subhead-muted { color: var(--ink-500); }
.showcase-events-list { list-style: none; padding: 0; margin: 0 0 8px; }
.showcase-events-list li {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; padding: 10px 12px; border: 1px solid var(--ink-100);
    border-radius: 8px; margin-bottom: 6px; background: #fff;
}
.showcase-events-list-muted li { background: #fafafa; }
.showcase-event-name {
    font-size: 13.5px; font-weight: 500; color: var(--ink-900); text-decoration: none;
    flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.showcase-event-name:hover { color: var(--accent); }
.showcase-event-date { font-size: 12px; color: var(--ink-500); white-space: nowrap; }

@media (max-width: 720px) {
    .showcase-summary { flex-direction: column; align-items: flex-start; }
    .showcase-preview-frame { height: 420px; }
    .showcase-copy-row, .showcase-cname-row { flex-direction: column; }
    .showcase-copy-btn { align-self: flex-start; }
}

/* ── Shared v3 account-menu dropdown (events list + studio manage
       headers + account shell). Lives here so every v3 screen loading
       studio.css gets the same chrome — no more inline duplicates. */
.events-user { position: relative; }
.events-avatar {
    width: 34px; height: 34px; border-radius: 12px; background: var(--ink-900);
    display: flex; align-items: center; justify-content: center;
    color: #fff; font-size: 12px; font-weight: 600; cursor: pointer;
    user-select: none; transition: box-shadow 0.12s;
    overflow: hidden;
}
.events-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.events-avatar:hover,
.events-user.is-open .events-avatar { box-shadow: 0 0 0 3px rgba(11, 15, 25, 0.1); }
.events-user-menu {
    position: absolute; top: calc(100% + 8px); right: 0; min-width: 260px;
    background: #fff; border: 1px solid var(--ink-100); border-radius: 12px;
    box-shadow: 0 12px 32px rgba(15, 23, 42, 0.14); padding: 6px;
    display: none; z-index: 70;
}
.events-user.is-open .events-user-menu { display: block; }
.events-user-identity {
    display: flex; align-items: center; gap: 10px;
    padding: 12px 12px 10px;
    border-bottom: 1px solid #f3f4f6; margin-bottom: 4px;
}
.events-user-photo {
    width: 44px; height: 44px; border-radius: 10px;
    object-fit: cover; flex-shrink: 0;
}
.events-user-text { min-width: 0; flex: 1; }
.events-user-name {
    font-size: 15px; font-weight: 600; color: var(--ink-900);
    line-height: 1.2;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.events-user-email {
    font-size: 12px; color: var(--ink-500);
    word-break: break-all; margin-top: 2px;
}
.events-user-link {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 12px; font-size: 13.5px; color: var(--ink-900);
    text-decoration: none; border-radius: 6px; font-weight: 500;
}
.events-user-link:hover { background: #f5f5f7; }
.events-user-link.danger { color: #b91c1c; }
.events-user-link.danger:hover { background: #fef2f2; }
.events-user-link-icon {
    flex-shrink: 0; color: var(--ink-400, #9ca3af);
}
.events-user-new-reg {
    display: flex; align-items: center; justify-content: center; gap: 6px;
    margin: 6px 8px 8px; padding: 10px 16px;
    background: var(--ink-900, #0b0f19); color: #fff;
    font-size: 13.5px; font-weight: 600;
    text-decoration: none; border-radius: 8px;
    transition: opacity 120ms ease;
}
.events-user-new-reg:hover { opacity: 0.88; }

/* ── Account-menu plan + upgrade nudge ───────────────────────────
   Sits right under the identity row. Shows the org's current tier
   as a colour-coded pill and (when below Max) an upgrade card with
   a one-line value pitch. Visible at-a-glance plan status is the
   thing organisers asked for — the pill alone covers it; the card
   is the optional upsell. */
.events-user-plan {
    display: flex; align-items: center; justify-content: space-between;
    padding: 8px 12px 6px;
    margin-bottom: 2px;
}
.events-user-plan-label {
    font-size: 11.5px; font-weight: 500;
    color: var(--ink-500);
    letter-spacing: 0.01em;
}
.events-user-plan-pill {
    display: inline-flex; align-items: center;
    padding: 2px 10px;
    border-radius: 999px;
    font-size: 10.5px; font-weight: 700; letter-spacing: 0.06em;
    text-transform: uppercase; text-decoration: none;
    transition: transform 120ms ease;
}
.events-user-plan-pill:hover { transform: translateY(-1px); }
.events-user-plan-pill-free {
    background: #f3f4f6; color: var(--ink-700, #374151);
    border: 1px solid #e5e7eb;
}
.events-user-plan-pill-pro {
    background: #eef2ff; color: #3730a3;
    border: 1px solid #c7d2fe;
}
.events-user-plan-pill-max {
    background: #fef3c7; color: #92400e;
    border: 1px solid #fcd34d;
}
.events-user-plan-pill-enterprise {
    background: #0b0f19; color: #fff;
    border: 1px solid #0b0f19;
}

.events-user-upgrade-icon {
    display: flex; align-items: center; justify-content: center;
    width: 32px; height: 32px; border-radius: 50%;
    background: #fef9c3; font-size: 15px; flex-shrink: 0;
}
.events-user-upgrade {
    display: flex; align-items: center; justify-content: space-between;
    gap: 10px;
    margin: 4px 8px 8px;
    padding: 10px 12px;
    background: linear-gradient(135deg, #eef2ff 0%, #f5f3ff 100%);
    border: 1px solid #c7d2fe;
    border-radius: 10px;
    text-decoration: none;
    transition: transform 120ms ease, box-shadow 120ms ease,
                border-color 120ms ease;
}
.events-user-upgrade:hover {
    transform: translateY(-1px);
    border-color: #a5b4fc;
    box-shadow: 0 6px 18px -8px rgba(79, 70, 229, 0.32);
}
.events-user-upgrade-text {
    display: flex; flex-direction: column; gap: 2px; min-width: 0;
}
.events-user-upgrade-text strong {
    font-size: 13px; font-weight: 600; color: #312e81;
}
.events-user-upgrade-sub {
    font-size: 11.5px; color: #4338ca; line-height: 1.35;
}
.events-user-upgrade-arrow {
    font-size: 16px; color: #4f46e5; flex-shrink: 0;
    transition: transform 160ms ease;
}
.events-user-upgrade:hover .events-user-upgrade-arrow {
    transform: translateX(2px);
}

/* Hairline between the plan/upgrade block and the link list below
   so the menu reads in three clear sections (identity / plan /
   actions) rather than one long chunk. */
.events-user-plan + .events-user-upgrade { margin-top: 0; }
.events-user-plan + .events-user-new-reg,
.events-user-upgrade + .events-user-new-reg {
    margin-top: 4px;
}
.events-user-new-reg + .events-user-link:first-of-type,
.events-user-new-reg + .events-user-link {
    margin-top: 4px;
    padding-top: 8px;
    position: relative;
}
.events-user-new-reg + .events-user-link::before {
    content: '';
    position: absolute; top: 0; left: 8px; right: 8px;
    border-top: 1px solid #f3f4f6;
}
/* Locked links (Max-tier features for below-Max orgs): dim the label
   slightly but KEEP the link clickable — the route-level gate sends
   them to /v3/plans?from=hub, so the "Max" badge doubles as a
   discovery path into pricing. */
.events-user-link.is-locked {
    color: var(--ink-500);
}
.events-user-link.is-locked:hover { background: #f5f5f7; color: var(--ink-900); }
.events-user-link-badge,
.manage-nav-plan-badge {
    font-size: 9.5px; font-weight: 700; letter-spacing: 0.5px; text-transform: uppercase;
    color: #3730a3; background: #eef2ff; border: 1px solid #c7d2fe;
    padding: 1px 6px; border-radius: 999px;
    margin-left: auto; flex-shrink: 0;
}
.manage-nav-item.is-locked {
    display: flex; align-items: center; justify-content: space-between;
    color: var(--ink-500);
}
.manage-nav-item.is-locked:hover { color: var(--ink-900); }
/* Compound state — user is viewing the locked page they haven't
   unlocked yet (Event hub on a below-Max org). Without this rule the
   `.active` dark-pill bg stayed on but `.is-locked` grey text took
   over, giving a black pill with unreadable grey text (see screenshot
   in PR). Subtle `#f5f5f7` surface says "you're on this page" while
   the muted text + plan badge still telegraph "you don't own it yet". */
.manage-nav-item.active.is-locked {
    background: #f5f5f7;
    color: var(--ink-700);
    font-weight: 600;
}
.manage-nav-item.active.is-locked:hover { background: #ececef; color: var(--ink-900); }
/* Plan badge on the active-locked row needs a touch more contrast
   against the lighter background — keep the indigo but deepen it. */
.manage-nav-item.active.is-locked .manage-nav-plan-badge {
    background: #e0e7ff; color: #312e81; border-color: #a5b4fc;
}

/* "NEW" badge for recently-shipped nav items (e.g. AI Voice Support).
   Distinct from the Max-plan badge so users don't read it as a
   paywall — green denotes availability, not premium gating. */
.manage-nav-with-badge { gap: 8px; }
.manage-nav-new-badge {
    font-size: 9px; font-weight: 700; letter-spacing: 0.6px;
    text-transform: uppercase;
    color: #065f46;
    background: #d1fae5;
    border: 1px solid #6ee7b7;
    padding: 1px 6px; border-radius: 999px;
    flex-shrink: 0;
    animation: manage-new-pulse 2.8s ease-in-out infinite;
}
.manage-nav-item.active .manage-nav-new-badge {
    background: #a7f3d0; color: #064e3b; border-color: #34d399;
}
@keyframes manage-new-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
    50%      { box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.18); }
}


/* ═══════════════════════════════════════════════════════════════════════
   Responsive polish pass — 2026-05-30
   ═══════════════════════════════════════════════════════════════════════

   Goal: make 768-1280 px feel calm + lower the small-screen gate to <768.

   Empirical Chrome test (live e2m.live /v3/studio/503/{plan,build,publish}
   in iframe-emulated viewports) found these pain points:

     • 1024-1100 px: plan-nav "1·Plan / 2·Form / 3·Publish" sequencer eats
                     ~280 px of the centred grid cluster → event-title in
                     the left slot gets truncated to "AI & Women in
                     Workforce Su…". Step LABELS are redundant with the
                     numbers + dividers — hiding them reclaims the room.

     • 900-1100 px:  Build / plan pages stay 2-col chat-plus-preview until
                     780-900 px where they go to 1fr. In the transition
                     window the chat is squeezed to 300-380 px and the
                     preview is too narrow for the modal overlay to read.

     • 768-899 px:   The previous rules HID the chat sidebar (display:
                     none on .plan-chat and .build-pane.chat). That's fine
                     when the gate is at <900 — phones never see this
                     state. But we're lowering the gate to <768, so an
                     organiser on iPad portrait NEEDS the chat to still be
                     reachable. New behaviour: stack chat ABOVE the form
                     / preview at full width with a capped max-height,
                     and let the page scroll between them.

     • 1200-: Already handled — `.plan-grid` / `.publish-grid` / `.publish-
              previews` collapse to 1fr.

   The rules below LAYER on top of the earlier media-query block (lines
   ~3977-4039) — same specificity + appended later means they win the
   cascade where they overlap. Existing rules stay as documentation of
   the design intent + a safety net if a future refactor undoes these. */

/* ── 1100-: hide step labels in plan-nav, keep just numbers + dividers — */
@media (max-width: 1100px) {
    /* The .plan-step-num pill (with the number + check icon) stays —
       it's the actual progress indicator. The text label was just
       reinforcement; redundant at narrow widths. */
    .plan-step-label { display: none; }
    .plan-step { padding: 4px 8px; }
    /* Tighten the divider between steps so the cluster feels compact. */
    .plan-step-divider { width: 24px; }
}

/* ── 1024-: tighten plan-nav padding so the title doesn't truncate ───── */
@media (max-width: 1024px) {
    .plan-nav { padding: 10px 16px; gap: 12px; }
    .plan-nav-right { gap: 8px; }
}

/* ── 900-: chat treatment for the (currently gated) bypass band ────────
   The studio gate at <1024 covers everything in this range, so these
   rules are only seen when an organiser bypasses the gate via
   ?force_desktop=1. Plan-chat stacks above the form; build-chat
   inherits the inline grid pane treatment (the desktop layout
   degraded). The drawer mode lives in a separate 1024-1199 block
   below — that's where it's actually visible. */
@media (max-width: 899px) {
    /* Release the page-scroll lock the desktop layout needs (each
       pane scrolls in a 100vh grid). Stacked layouts need the page
       as the scroll container. */
    body.studio-body.no-page-scroll { overflow-y: auto !important; }
    body.studio-body.no-page-scroll > .plan-nav { position: sticky; top: 0; }

    /* Plan + build collapse to one column. */
    .plan-layout,
    .build-layout {
        grid-template-columns: 1fr !important;
        height: auto !important;
    }

    /* Plan: chat is a static brief snapshot — stack above the form with
       a capped height + thin separator. Override the earlier 900-block
       display:none. */
    .plan-chat {
        display: flex !important;
        max-height: 42vh;
        border-bottom: 1px solid #e5e7eb;
    }
    .plan-layout { --plan-chat-w: auto; }

    /* Builder pane (legacy advanced editor) stays hidden. */
    .build-pane.builder { display: none !important; }
    .chat-resizer { display: none; }

    /* plan-nav: drop the step sequencer + ellipsis the title cleanly. */
    .plan-nav-steps { display: none; }
    .plan-nav-event {
        min-width: 0;
        flex: 1 1 0;
    }
    .plan-nav-event > .plan-nav-event-name,
    .plan-nav-event > a {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        max-width: 100%;
    }

    /* Manage-pages sidebar nav (publish, registrations, settings, etc.) —
       at narrow widths the items wrap to 2-3 rows which looks messy.
       Make the whole strip horizontally scrollable so they stay one row. */
    .manage-sidebar nav,
    .manage-nav {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        white-space: nowrap;
        scrollbar-width: thin;
    }
    .manage-nav-item { flex-shrink: 0; }

    /* Publish-share-split (preview frame + form) — already collapses at
       980 in the inline rule; nothing to add here. */
}

/* ── 768-: the OLD rule hid chat entirely; supersede with the stacked
        treatment from the 900- block above. This stanza now ONLY
        addresses the studio-nav (the cross-page top bar) which the
        original rule continues to handle. ─────────────────────────────── */
@media (max-width: 780px) {
    /* The studio-nav-steps class (used by the 5-step _studio_steps
       partial) is still hidden on phones — separate from the plan-nav
       steps which were already handled above. */
    .studio-nav-steps { display: none; }
}

/* ── Chat-drawer mode for the 1024-1099 band ────────────────────────
   At this width the studio is permitted (gate fires only below 1024)
   AND the side-by-side is very cramped (preview only 644-675 px after
   the compressed 300 px chat sidebar). So the chat becomes a slide-in
   drawer from the LEFT. The upper bound was 1199 px previously, which
   put full-viewport previews on most MacBooks (Arc sidebar + non-full-
   screen windows land in 1100-1199). Narrowing to 1099 means ≥1100
   uses inline layout with a compressed --chat-w (300 px cap), giving
   a preview of 800-899 px — enough for the event page hero. */
@media (min-width: 1024px) and (max-width: 1099px) {
    /* Preview takes the full grid; chat is removed from the flow and
       repositioned as a fixed-position drawer. */
    .build-layout {
        grid-template-columns: 1fr !important;
    }
    .build-pane.chat {
        display: flex !important;
        position: fixed !important;
        left: 0;
        /* Drawer starts BELOW the page nav. Uses a CSS custom property
           so JS can update it when notification banners (email-verify,
           unpublished-changes, etc.) push the build-layout down past
           the default 56 px nav height. JS measures
           build-layout.getBoundingClientRect().top on load + on banner
           dismiss and writes --build-pane-top on the root element.
           Falls back to 56px (nav-only) so no-JS or first-paint before
           the measurement runs still looks correct. */
        top: var(--build-pane-top, 56px);
        bottom: 0;
        width: min(86vw, 380px);
        max-height: none;
        z-index: 100;
        background: #fff;
        box-shadow: 6px 0 24px rgba(15, 23, 42, 0.18);
        transform: translateX(-100%);
        transition: transform 220ms ease;
        overflow-y: auto;
        border-bottom: 0;
    }
    body.chat-drawer-open .build-pane.chat { transform: translateX(0); }

    /* Dim backdrop while drawer is open. Pseudo-element on body so we
       don't have to inject a markup node; a JS handler catches clicks
       on the backdrop area + Esc to close. */
    body.chat-drawer-open::before {
        content: "";
        position: fixed;
        inset: 0;
        background: rgba(15, 23, 42, 0.42);
        z-index: 99;
        animation: chat-drawer-backdrop-in 180ms ease-out;
    }
    @keyframes chat-drawer-backdrop-in {
        from { opacity: 0; }
        to   { opacity: 1; }
    }
    @media (prefers-reduced-motion: reduce) {
        .build-pane.chat { transition: none; }
        body.chat-drawer-open::before { animation: none; }
    }

    /* 2026-05-31 — Tweaks drawer now PERMITTED at 1024-1199 (was
       force-hidden here; iPad-Mini users had a "Hide tweaks" toggle
       that did nothing because the panel could never appear). The
       default .build-pane.builder rule already positions it as a
       slide-in drawer from the RIGHT (absolute · transform:
       translateX(100%) · slides to 0 when .build-layout[data-advanced="on"]),
       so removing the display:none here is enough — the existing
       toggle button + drawer animation just start working. The chat
       drawer still slides in from the LEFT in this band; the two
       can coexist (different edges, both fixed/absolute, so neither
       blocks the other). */
    .chat-resizer { display: none; }

    /* 2026-06-01 — Tweaks drawer backdrop. Same dim-and-dismiss
       contract as the chat-drawer and manage-drawer ::before pseudos
       so all three drawers in the iPad-band feel like one consistent
       modal pattern. body.tweaks-drawer-open is toggled by the JS in
       build.html alongside layout.dataset.advanced. Z-index sits at
       99 so it covers the page chrome but stays under the drawer
       itself (.build-pane.builder is at z:100 in its existing
       drawer rule). */
    body.tweaks-drawer-open::before {
        content: "";
        position: fixed;
        inset: 0;
        background: rgba(15, 23, 42, 0.55);
        z-index: 99;
        animation: chat-drawer-backdrop-in 180ms ease-out;
    }
    @media (prefers-reduced-motion: reduce) {
        body.tweaks-drawer-open::before { animation: none; }
    }

    /* Lift the chat composer a touch off the bottom edge of the
       drawer so the input doesn't sit flush with the viewport floor
       (where the iPad OS bar / hint affordance can crowd it). User
       report at iPad 1024 — "input field ektu opre tule dile valo
       hoi". Padding goes on the pane, not the textarea, so the
       composer's existing shadows + rounded corners stay tidy. */
    .build-pane.chat { padding-bottom: 16px; }

    /* Tweaks resizer makes no sense in the drawer band — at 1024-1199
       the drawer covers the preview as a modal, so widening it eats
       the screen the user wants to inspect. Hide the handle here.
       Resize is re-enabled at ≥1200 px (the inline-grid mode). */
    .builder-resizer { display: none; }
}

/* ── Chat-drawer toggle button (build-page top nav) ────────────────────
   Only rendered at 1024-1199 — the band where the drawer mode lives.
   ≥1200 keeps the chat inline so the button is redundant; <1024 the
   gate covers the whole studio so a toggle would be unreachable.
   When the drawer is open the button gets a gradient pill treatment
   so the "chat mode active" state reads at a glance. */
.chat-drawer-toggle {
    display: none;
    align-items: center;
    gap: 6px;
    padding: 6px 10px;
    border: 1px solid #e5e7eb;
    border-radius: 8px;
    background: #fff;
    color: #1f2937;
    font-size: 12.5px;
    font-weight: 500;
    cursor: pointer;
    transition: background 120ms, border-color 120ms, color 120ms;
}
.chat-drawer-toggle:hover { background: #f9fafb; border-color: #d1d5db; }
.chat-drawer-toggle:focus-visible {
    outline: 2px solid #8b5cf6; outline-offset: 2px;
}
.chat-drawer-toggle svg { width: 14px; height: 14px; }
body.chat-drawer-open .chat-drawer-toggle {
    background: linear-gradient(135deg, #ec4899 0%, #8b5cf6 100%);
    border-color: transparent;
    color: #fff;
}
@media (min-width: 1024px) and (max-width: 1099px) {
    .chat-drawer-toggle { display: inline-flex; }
}

/* ── Chat-drawer EDGE HANDLE — vertical tab on the left edge ────────
   The nav-top toggle was easy to miss when the drawer is closed
   ("how to open a closed drawer? there is no way i see" — 2026-05-30
   dip-test). This handle sticks out of the left edge of the viewport,
   vertically centred, with the brand-gradient pill treatment so it's
   the first thing the eye lands on when scanning a closed drawer state.

   Behaviour:
     • Only rendered in the 1024-1199 band — same scope as the rest
       of the drawer (≥1200 chat is inline, <1024 the gate is up).
     • Visible ONLY when drawer is closed; once open it hides so it
       doesn't compete with the close affordances.
     • Hover nudges 4 px out from the edge to read as "pull me open"
       — same affordance pattern as Cursor / ChatGPT iPad.
     • Click delegates to the same open() handler the nav-top toggle
       uses, so aria-expanded + focus management stay in step. */
.chat-drawer-edge-handle {
    display: none;
    position: fixed;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    z-index: 95;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 14px 8px 14px 6px;
    border: 0;
    border-radius: 0 14px 14px 0;
    background: linear-gradient(135deg, #ec4899 0%, #8b5cf6 100%);
    color: #fff;
    font-family: inherit;
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: 0.5px;
    cursor: pointer;
    box-shadow: 4px 0 12px rgba(139, 92, 246, 0.28);
    transition: transform 180ms ease, box-shadow 180ms ease, padding-left 180ms ease;
}
.chat-drawer-edge-handle:hover {
    padding-left: 10px;
    box-shadow: 6px 0 16px rgba(139, 92, 246, 0.36);
}
.chat-drawer-edge-handle:focus-visible {
    outline: 2px solid #fff;
    outline-offset: -2px;
}
.chat-drawer-edge-handle svg { width: 18px; height: 18px; }
.chat-drawer-edge-handle-label {
    /* Vertical writing mode keeps the label readable + works in any
       locale (rotated devanagari would read sideways). The 180-deg
       rotate flips it from top→bottom to bottom→top so the first
       letter sits next to the icon at top. */
    writing-mode: vertical-rl;
    text-orientation: mixed;
    transform: rotate(180deg);
}
@media (min-width: 1024px) and (max-width: 1099px) {
    body:not(.chat-drawer-open) .chat-drawer-edge-handle {
        display: flex;
    }
}
@media (prefers-reduced-motion: reduce) {
    .chat-drawer-edge-handle { transition: none; }
}



/* ═══════════════════════════════════════════════════════════════════════
   Wheel-scroll containment pass — 2026-05-30
   ═══════════════════════════════════════════════════════════════════════

   User report: "in plan page, the right side mouse scroll shud work.
   currently have to click and drag."

   Root cause: the studio uses a "no-page-scroll" shell — body has
   overflow: hidden and the actual scrollable content lives in inner
   containers (.plan-questions-scroll, .plan-guide-inner, .pane-body,
   .manage-sidebar, etc.). When the cursor sits over an inner element
   whose own parent chain includes overflow: hidden boundaries
   (.plan-req-group-collapse <details>, .plan-layout, body), the
   browser can fail to identify the right scroll target — the wheel
   event walks up through overflow:hidden ancestors that "look"
   scrollable but actually have scrollHeight === clientHeight, then
   stops without scrolling anything. Empirically this presents as
   "wheel sometimes works, sometimes does nothing; click-and-drag
   the scrollbar handle always works".

   Fix: declare `overscroll-behavior: contain` on every studio scroll
   container. The contract is "this element is the scroll target,
   stop hunting up the tree." It does NOT change behaviour when the
   scroll works correctly; it only prevents the chain-up failure
   mode that produces the dead-wheel symptom.

   Applied to all 13 scroll containers found in studio.css + the
   key inline-styled ones (plan-guide-inner, pane-body, etc.).
   Safe to layer — the property has no per-rule side effects
   beyond its containment semantic. */
.plan-questions-scroll,
.plan-guide,
.plan-guide-inner,
.plan-main,
.plan-layout > .plan-main,
.plan-chat-body,
.plan-confirm,
.pane-body,
.manage-sidebar,
.manage-event-switcher-list,
.refund-drawer-body {
    /* Stop the browser from walking up the tree looking for a
       parent to scroll when this element reaches its edge. The
       walk-up is what breaks: ancestors with overflow: hidden +
       scrollHeight === clientHeight pretend to be candidates,
       then drop the event. `contain` short-circuits the walk. */
    overscroll-behavior: contain;
}
/* Plan/build inner-styled containers that are flex children: pin
   min-height: 0 so their intrinsic content height can't override
   the flex track and leave the scroller reporting scrollHeight ==
   clientHeight. Already true for most of the list above via inline
   rules; this re-asserts it where coverage is patchy. */
body.plan-body-refresh .plan-guide-inner,
.plan-questions-scroll,
.pane-body {
    min-height: 0;
}


/* ═══════════════════════════════════════════════════════════════════════
   Manage-sidebar drawer for the 1024-1199 band — 2026-05-30
   ═══════════════════════════════════════════════════════════════════════

   User direction (with screenshot): "for this page, at this viewport,
   the left menu can be collapsed to give space".

   At 1024-1199 the studio is permitted (gate fires only below 1024)
   but the manage-sidebar's 220 px eats half the remaining horizontal
   real estate. On /publish that crushes the 3 share-mode pills —
   POPUP BUTTON / INLINE EMBED / HOSTED URL clip to fragments
   ("JP BU / NE EM / STED" in the screenshot). On /registrations
   and /emails the table columns stack awkwardly. Same band where
   the chat already shifts to a drawer on /build + /plan — so the
   studio gets ONE consistent collapse contract: at 1024-1199, both
   side panels are drawers; ≥1200 both are inline.

   Behaviour:
     • Hidden by default, fixed-position left drawer
     • Vertical "Pages" edge handle on the left edge opens it
     • Body class `manage-drawer-open` flips the slide transform
     • Click outside + Esc + nav-link click close it
     • The chat-drawer-open backdrop ::before is shared via a
       comma-selector so two drawers on the same body don't double
       up the overlay opacity. */
@media (min-width: 1024px) and (max-width: 1199px) {
    /* Drawer mode for any page that uses .manage-sidebar — the
       studio editor pages (publish, registrations, settings,
       payments, payouts, team, emails, domain, support) AND the
       account shell (profile, organization, plans, billing
       history). Both ship the same shared drawer partials so the
       collapse contract is identical:
         • v3/_manage_drawer_close.html  — the × close button
         • v3/_manage_drawer_handle.html — the edge handle + JS

       Bengali user report 2026-05-30: "jokhon profile menu select
       korchi tokhon collapsible drawer show korche na" — account
       pages were excluded by an earlier :not(.account-shell) gate
       because they didn't have the open affordance. Now that they
       do (via the shared partials), the gate is removed and the
       account shell collapses to a drawer just like the studio. */
    .manage-sidebar {
        position: fixed !important;
        left: 0;
        /* Below the page nav — same 56 px offset as the chat drawer.
           Avoids the root-stacking conflict where the nav (static
           normal flow at body level) paints over the drawer (inside
           build-layout's z:1 stacking context). */
        top: 56px;
        bottom: 0;
        /* 2026-06-01 Bengali user dip-test ("drawer height 100% hobe"):
           the default .manage-sidebar above sets height: calc(100vh - 64px),
           which this drawer rule was inheriting. With position: fixed +
           top: 56px + bottom: 0 + height: calc(100vh - 64px), CSS over-
           constrains and the explicit height wins — so the drawer ended
           8 px short of the viewport bottom, reading as a floating card.
           height: auto lets top + bottom resolve the height cleanly so
           the panel reaches the bottom edge. */
        height: auto;
        width: min(86vw, 280px);
        z-index: 100;
        background: #fff;
        box-shadow: 6px 0 24px rgba(15, 23, 42, 0.18);
        /* Defensive — any rounded-corner treatment elsewhere on the
           sidebar reads as a "card" when full-height; the drawer is
           a side panel, not a card. Flatten the edges explicitly. */
        border-radius: 0;
        transform: translateX(-100%);
        transition: transform 220ms ease;
        overflow-y: auto;
        margin: 0;
    }
    body.manage-drawer-open .manage-sidebar { transform: translateX(0); }

    /* Main content area expands to fill the freed column. .manage-layout
       (publish / registrations / payments / settings / etc.) uses a
       grid-template-columns: 240px 1fr with .manage-sidebar in the left
       column; once the sidebar is pulled out of flow as a fixed drawer,
       the empty 240px column must be collapsed — regardless of whether
       the drawer is currently open or closed.

       2026-06-01 root-cause fix — was `body:not(.manage-drawer-open)`,
       which only applied when the drawer was CLOSED. When the drawer
       OPENED, the rule fell off and the grid reverted to 240px 1fr —
       but .manage-sidebar is position:fixed (out of normal flow) in
       this entire band, so the 240px column stayed empty and the main
       content shifted right: visible "structure break" reported by
       the user (Bengali: "drawer open korle structure break korche").
       Fix: remove the :not() qualifier so the grid is always 1fr here.

       2026-05-31 note — .plan-layout was previously in this selector,
       but the plan page has NO manage-sidebar; see the original comment
       above for the full rationale. */
    .manage-layout {
        grid-template-columns: 1fr !important;
    }

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

/* Shared backdrop — fires when EITHER drawer is open. Combining the
   two selectors here means the body never gets two ::before
   pseudo-elements (impossible in CSS anyway, but the comma keeps
   the contract honest if a future refactor splits them). */
body.manage-drawer-open::before {
    content: "";
    position: fixed;
    inset: 0;
    /* 2026-06-01 dip-test — user reported "ekta black overlay hole valo
       hoi": at 0.42 the dim was too subtle on a bright preview pane (the
       landing iframe sits behind the drawer and reads near-normal). 0.55
       gives the unambiguous "modal is open, the rest of the page is
       inactive" treatment — same intensity Stripe / Linear / Notion use
       for their slide-in drawers. */
    background: rgba(15, 23, 42, 0.55);
    z-index: 99;
    animation: chat-drawer-backdrop-in 180ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
    body.manage-drawer-open::before { animation: none; }
}

/* ── Edge handle on the left edge — vertical "Pages" pull tab ─────── */
.manage-drawer-edge-handle {
    display: none;
    position: fixed;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    z-index: 95;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 14px 8px 14px 6px;
    border: 0;
    border-radius: 0 14px 14px 0;
    /* Slate-gray gradient so it reads as "navigation" — distinct
       from the chat drawer's pink/purple "AI" gradient on /build
       so a future surface that hosts BOTH drawers (none today)
       reads them as different affordances. */
    background: linear-gradient(135deg, #1f2937 0%, #475569 100%);
    color: #fff;
    font-family: inherit;
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: 0.5px;
    cursor: pointer;
    box-shadow: 4px 0 12px rgba(15, 23, 42, 0.28);
    transition: transform 180ms ease, box-shadow 180ms ease, padding-left 180ms ease;
}
.manage-drawer-edge-handle:hover {
    padding-left: 10px;
    box-shadow: 6px 0 16px rgba(15, 23, 42, 0.36);
}
.manage-drawer-edge-handle:focus-visible {
    outline: 2px solid #fff;
    outline-offset: -2px;
}
.manage-drawer-edge-handle svg { width: 18px; height: 18px; }
.manage-drawer-edge-handle-label {
    writing-mode: vertical-rl;
    text-orientation: mixed;
    transform: rotate(180deg);
}
@media (min-width: 1024px) and (max-width: 1199px) {
    body:not(.manage-drawer-open) .manage-drawer-edge-handle {
        display: flex;
    }
}
@media (prefers-reduced-motion: reduce) {
    .manage-drawer-edge-handle { transition: none; }
}

/* ── Publish-mode pills: the "C dose" ────────────────────────────────
   Drawer mode reclaims ~220 px so the 3 share-mode-pills (Popup /
   Inline / Hosted) get ~165 px each instead of ~100 — plenty for
   the full uppercase labels. But the existing inline rules in
   publish.html set overflow:hidden + text-overflow:ellipsis with
   no minimum width, so an edge case (very long org name in the
   nav, wide pill-icon SVG, etc.) could still clip. Belt-and-braces
   safe minimum: `min-width: 96px` per pill so the labels never
   reach the truncation threshold. */
.publish-share-tabs .share-mode-pill {
    min-width: 96px;
}


/* ═══════════════════════════════════════════════════════════════════════
   Visible scrollbars on the chat panes — 2026-05-30
   ═══════════════════════════════════════════════════════════════════════

   User report (with /build screenshot at 1024 px showing the chat
   drawer open): "need a scoll bar at left chat panel".

   Root cause: Mac Chrome (and Safari) auto-hide scrollbars by default
   — they only appear while the user is mid-scroll. On a chat pane
   the content can be long enough to overflow (long brief bubble +
   stack of past edits + suggestion chips + composer) but the
   organiser has no visible cue that scrolling IS available. Click-
   and-drag the thumb works… if you can SEE the thumb.

   Fix: explicit ::-webkit-scrollbar styling on every chat-pane
   scroller — slate thumb on a transparent track, 8 px wide, hover
   darkens. Firefox gets the equivalent via `scrollbar-width: thin`
   + `scrollbar-color`. Always visible, never overlay-style. */

.build-pane.chat,
.build-pane.chat .pane-body,
.plan-chat,
.plan-chat-body,
.plan-guide,
body.plan-body-refresh .plan-guide-inner {
    /* Firefox: thin + slate-on-transparent. The two-value scrollbar-
       color shorthand sets thumb then track. */
    scrollbar-width: thin;
    scrollbar-color: rgba(15, 23, 42, 0.35) transparent;
}

/* WebKit (Chrome, Safari, Edge) — equivalent treatment. The
   ::-webkit-scrollbar pseudo-element must be styled with explicit
   width and a non-transparent thumb to defeat the OS-level
   overlay-style auto-hide behaviour. */
.build-pane.chat::-webkit-scrollbar,
.build-pane.chat .pane-body::-webkit-scrollbar,
.plan-chat::-webkit-scrollbar,
.plan-chat-body::-webkit-scrollbar,
.plan-guide::-webkit-scrollbar,
body.plan-body-refresh .plan-guide-inner::-webkit-scrollbar {
    /* 10 px (with the 2 px transparent border below) renders as a
       ~6 px visible thumb — wide enough to read at a glance,
       narrow enough not to crowd the message column. */
    width: 10px;
    height: 10px;
}
.build-pane.chat::-webkit-scrollbar-thumb,
.build-pane.chat .pane-body::-webkit-scrollbar-thumb,
.plan-chat::-webkit-scrollbar-thumb,
.plan-chat-body::-webkit-scrollbar-thumb,
.plan-guide::-webkit-scrollbar-thumb,
body.plan-body-refresh .plan-guide-inner::-webkit-scrollbar-thumb {
    background: rgba(15, 23, 42, 0.35);
    border-radius: 6px;
    /* 2 px transparent border insets the thumb so it doesn't visually
       butt up against the pane edge — gives the scrollbar room to
       breathe without changing the underlying scroll geometry. */
    border: 2px solid transparent;
    background-clip: content-box;
}
.build-pane.chat::-webkit-scrollbar-thumb:hover,
.build-pane.chat .pane-body::-webkit-scrollbar-thumb:hover,
.plan-chat::-webkit-scrollbar-thumb:hover,
.plan-chat-body::-webkit-scrollbar-thumb:hover,
.plan-guide::-webkit-scrollbar-thumb:hover,
body.plan-body-refresh .plan-guide-inner::-webkit-scrollbar-thumb:hover {
    background: rgba(15, 23, 42, 0.55);
    background-clip: content-box;
}
.build-pane.chat::-webkit-scrollbar-track,
.build-pane.chat .pane-body::-webkit-scrollbar-track,
.plan-chat::-webkit-scrollbar-track,
.plan-chat-body::-webkit-scrollbar-track,
.plan-guide::-webkit-scrollbar-track,
body.plan-body-refresh .plan-guide-inner::-webkit-scrollbar-track {
    background: transparent;
}


/* ═══════════════════════════════════════════════════════════════════════
   In-drawer × close buttons — 2026-05-30
   ═══════════════════════════════════════════════════════════════════════

   User report (Bengali): "left chat r header ta hide hoe jachhe.
   Cross icon lagbe" → the chat pane's regular header is hidden
   under .plan-body-refresh, so once the drawer opens there is no
   close affordance INSIDE the pane. The user has to mouse all the
   way to the top-nav "Chat" toggle or guess that clicking outside
   closes it.

   Fix: a small × button pinned to the top-right of each drawer.
   Visible only while the drawer is open + we're in the 1024-1199
   band. Same styling on both drawers (chat + manage) so the close
   affordance is a single mental model. */

/* 2026-05-31 rebuild — the icon-only × close was reported as
   invisible in a dip-test ("still cant see any hide button or
   close icon. tweaks panel has a nice hide option"). Switched to
   a proper sticky header bar mirroring the Tweaks pane pattern:
   title text on the left, "Hide" btn-ghost on the right. The
   header is a real flex item at the top of the drawer column,
   sticky-pinned so it stays at the top even when chat content
   scrolls underneath. Way more discoverable than the bare × icon. */

.chat-drawer-header,
.manage-drawer-header {
    display: none;
    /* Sticky so the header rides at the top of the visible drawer
       area even after the user scrolls chat history. */
    position: sticky;
    top: 0;
    z-index: 5;
    flex-shrink: 0;
    /* Two-column row: title (1fr) + Hide button (auto). */
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 16px;
    background: #fff;
    border-bottom: 1px solid #e5e7eb;
    /* Soft drop-shadow so the header reads as floating above the
       scrolling chat history rather than blending into it. */
    box-shadow: 0 2px 6px rgba(15, 23, 42, 0.08);
}
.chat-drawer-header-title,
.manage-drawer-header-title {
    font-size: 13px;
    font-weight: 600;
    color: #0f172a;
    letter-spacing: 0.02em;
}
.chat-drawer-close,
.manage-drawer-close {
    /* Scoped overrides for the .btn-ghost in the drawer header —
       tighter than the page-level ghost button (which is sized for
       primary surfaces). */
    padding: 5px 12px !important;
    font-size: 12px !important;
    border-radius: 6px !important;
    line-height: 1.2 !important;
}

/* Only show the header when the respective drawer is open AND we're
   in the drawer band. Outside that band the drawers are inline
   (≥1100 for chat, ≥1200 for manage) or the gate is up (<1024). */
@media (min-width: 1024px) and (max-width: 1099px) {
    body.chat-drawer-open .chat-drawer-header { display: flex; }
}
@media (min-width: 1024px) and (max-width: 1199px) {
    body.manage-drawer-open .manage-drawer-header { display: flex; }
}

/* The chat-drawer needs `position: relative` on the .build-pane.chat
   wrapper for absolute positioning of the × to anchor correctly.
   Already true at ≥1200 (inline grid cell) but at 1024-1199 the
   pane becomes position: fixed — fixed elements are positioning
   contexts themselves, so the absolute × pins to the drawer's
   top-right edge as intended. No extra rule needed. */

/* ── Google Places Autocomplete dropdown ─────────────────────────────
   Venue inputs (plan / build / manage) attach google.maps.places.
   Autocomplete, which injects an unstyled `.pac-container` dropdown.
   With no app styling it fell back to Google's defaults, and the grey
   sprite pin icons (`.pac-icon`, served from maps.gstatic.com) rendered
   as broken grey blobs overlapping the field on the live build (the
   venue "grey domes" bug). Style the dropdown to match the studio UI and
   drop the sprite icons so a blocked/failed sprite can never render as
   grey shapes. The "powered by Google" attribution row is left intact
   (required by Google's terms). */
.pac-container {
    z-index: 100000;                 /* above the sticky nav + scrolled panes */
    margin-top: 6px;
    background: #fff;
    border: 1px solid rgba(15, 23, 42, 0.10);
    border-radius: 10px;
    box-shadow: 0 12px 32px -8px rgba(15, 23, 42, 0.28);
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    overflow: hidden;
    padding: 4px 0;
}
.pac-item {
    display: block;                  /* force the vertical list layout */
    padding: 8px 12px;
    border: 0;
    font-size: 13px;
    line-height: 1.4;
    color: #334155;
    cursor: pointer;
}
.pac-item:hover,
.pac-item-selected { background: #f4f4f6; }
.pac-item-query { font-size: 13px; color: #0f172a; font-weight: 500; }
.pac-matched { font-weight: 600; }
/* Drop the sprite pin icon — it renders as a grey blob (the "grey domes")
   when the maps.gstatic.com sprite is blocked by referrer/CSP. The venue
   field already carries its own location glyph.
   !important is REQUIRED: the Maps SDK injects its own <style> into <head>
   at runtime (AFTER studio.css loads) with an equal-specificity
   `.pac-icon { display:inline-block; background:url(sprite) }`. On a
   specificity tie the later (Google) rule wins, so without !important the
   broken sprite paints again — 2026-06-29 QA still saw the grey domes
   despite the earlier non-!important fix. Also blank the background-image
   in case Google switches to an hdpi/marker variant. */
.pac-icon,
.pac-icon-marker {
    display: none !important;
    background-image: none !important;
}
