/* Mx2 Client Portal — base styles (shared by client + mx2 sides) */

*,
*::before,
*::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }

/* Transitions are disabled while body.preload is on (server-side
   default, removed by no-flash.js after window.load). Prevents the
   "first hover sticks" feel when the cursor is already over a card
   at initial paint — the :hover state would otherwise animate mid-
   script-execution and stutter. Once removed, normal hover
   transitions resume. */
body.preload,
body.preload *,
body.preload *::before,
body.preload *::after {
  transition: none !important;
  animation-duration: 0s !important;
  animation-delay: 0s !important;
}

body {
  margin: 0;
  font-family: var(--font-sans);
  font-weight: 400;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a { color: inherit; }

/* ── Typography scale ───────────────────────────────────────────────────── */
h1, h2, h3, h4, h5, h6 { margin: 0; }
h1 {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: 56px;
  line-height: 1.05;
  letter-spacing: -0.01em;
}
h2 {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: 34px;
}
h3 {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 15px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
}

p { margin: 0 0 12px; }

.lead { font-size: 18px; max-width: 640px; }

/* Canonical eyebrow per SKILL.md: Josefin bold, 13px, 0.22em tracking, gold. */
.section-kicker,
.mx2-eyebrow {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  color: var(--mx2-gold);
  margin-bottom: var(--space-3);
}

.mono { font-family: var(--font-mono); }
.dim  { color: var(--ink-soft); }
.micro { font-size: 12px; }

/* ── Buttons ────────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 12px 22px;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background var(--t-med), border-color var(--t-med), color var(--t-med), transform var(--t-fast);
  text-decoration: none;
  font-stretch: normal;
}
.btn:disabled,
.btn[aria-disabled='true'] {
  opacity: 0.55;
  cursor: not-allowed;
}
/* PR-5 — Existing .btn-* variants get re-skinned to match the
   MaestroX system (.btn.primary / .secondary / .ghost / .danger
   from maestrox-components.css). Doing this at the CSS layer means
   ~150 existing button instances in views inherit the new look
   without per-file markup edits. Mapping:

     .btn-primary    → gold filled (was black bg)
     .btn-secondary  → paper bg + stone border + ink (was transparent + black)
     .btn-tertiary   → keeps the gold filled look (matches .btn-primary now)
     .btn-danger     → stroke-only rust (matches new .btn.danger)
     .btn-ghost      → transparent + ink-soft, deepens on hover
     .btn-ghost-dark → same as .btn-ghost on the cream admin chrome;
                       any dark-island context (sidebar) re-pins
                       --mx2-text via the sidebar's local override.
*/
.btn-primary,
.btn-tertiary {
  background: var(--mx2-gold);
  color: #FFFFFF;
  border-color: var(--mx2-gold);
}
.btn-primary:hover:not(:disabled),
.btn-tertiary:hover:not(:disabled) {
  background: var(--mx2-gold-deep, #876e51);
  border-color: var(--mx2-gold-deep, #876e51);
  color: #FFFFFF;
}
.btn-secondary {
  background: #FFFFFF;
  color: var(--ink, #1A1A1A);
  border-color: var(--mx2-stone-300, #b8ae93);
}
.btn-secondary:hover:not(:disabled) {
  background: var(--mx2-paper-2, #f3eee2);
  border-color: var(--mx2-stone-400, #8c8068);
}
.btn-danger {
  background: transparent;
  color: var(--mx2-rust, #b84426);
  border-color: var(--mx2-rust, #b84426);
}
.btn-danger:hover:not(:disabled) {
  background: rgba(184, 68, 38, 0.10);
  border-color: var(--mx2-rust, #b84426);
}
/* Solid-filled danger (.is-active / "danger" inside confirm-dialog) —
   when red is the background, switch the text to white per Drew's
   2026-05-21 rule: text on any dark-colored button must be white. */
.btn-danger.is-active,
.btn-danger.is-filled,
button.btn.danger.filled,
[data-confirm-ok].danger {
  background: var(--mx2-rust, #b84426);
  color: #FFFFFF;
  border-color: var(--mx2-rust, #b84426);
}
.btn-ghost {
  background: transparent;
  color: var(--ink-soft, #5E5E5E);
  border: 1px solid transparent;
  padding: 8px 12px;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  cursor: pointer;
}
.btn-ghost:hover {
  color: var(--ink, #1A1A1A);
  background: var(--mx2-paper-2, #f3eee2);
}

.btn-ghost-dark {
  background: transparent;
  color: var(--mx2-text);
  border: 1px solid transparent;
  padding: 8px 12px;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  cursor: pointer;
}
.btn-ghost-dark:hover {
  color: var(--mx2-text-hi);
  background: var(--mx2-paper-2, #f3eee2);
}

.btn-sm { padding: 8px 14px; font-size: 11px; }

.inline-form { display: inline; margin: 0; padding: 0; }

/* ── Forms ──────────────────────────────────────────────────────────────── */
.field {
  display: flex;
  flex-direction: column;
  margin-bottom: var(--space-4);
}

/* Toggle switch — used for boolean on/off settings in admin forms.
   Wrap a hidden checkbox with .mx2-toggle on a <label>; the slider
   span renders the visual track + thumb, and .mx2-toggle__label
   carries the text on the right. */
.mx2-toggle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
  font-size: 13px;
  color: var(--ink, #1A1A1A);
  user-select: none;
  margin-bottom: 0;
}
.mx2-toggle input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.mx2-toggle__slider {
  position: relative;
  display: inline-block;
  width: 38px;
  height: 22px;
  background: var(--mx2-stone-500, #d9d2c0);
  border-radius: 999px;
  transition: background 150ms ease;
  flex: 0 0 auto;
}
.mx2-toggle__slider::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fff;
  transition: transform 150ms ease;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
}
.mx2-toggle input[type="checkbox"]:checked + .mx2-toggle__slider {
  background: var(--gold, #9f886c);
}
.mx2-toggle input[type="checkbox"]:checked + .mx2-toggle__slider::after {
  transform: translateX(16px);
}
.mx2-toggle input[type="checkbox"]:focus-visible + .mx2-toggle__slider {
  outline: 2px solid var(--gold, #9f886c);
  outline-offset: 2px;
}
.mx2-toggle__label {
  /* The label text inside the toggle — overrides the global uppercase
     label treatment so the toggle reads as a sentence, not a heading. */
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
  color: var(--ink, #1A1A1A);
  font-size: 13px;
}
label {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--ink-soft);
  margin-bottom: var(--space-2);
}
input[type='text'],
input[type='email'],
input[type='password'],
input[type='number'],
input[type='url'],
textarea,
select {
  font-family: var(--font-sans);
  font-size: 15px;
  padding: 12px 14px;
  border: 1px solid var(--rule);
  /* Form fields are the one place radius is allowed (2-4px per SKILL.md). */
  border-radius: var(--radius-2);
  background: #FFFFFF;
  color: var(--ink);
  width: 100%;
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
input:focus,
textarea:focus,
select:focus {
  outline: none;
  border-color: var(--gold);
  box-shadow: 0 0 0 3px rgba(158, 125, 74, 0.12);
}

textarea { min-height: 90px; resize: vertical; }

/* ── Breadcrumbs ────────────────────────────────────────────────────────── */
.breadcrumbs {
  margin: 0 0 24px;
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.breadcrumbs ol {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  align-items: center;
}
.breadcrumbs li {
  display: inline-flex;
  align-items: center;
}
.breadcrumbs li + li::before {
  content: '›';
  margin: 0 10px;
  opacity: 0.5;
  font-size: 14px;
  letter-spacing: 0;
  font-weight: 400;
}

/* Mobile breadcrumbs: collapse to "‹ <parent>" — show only the second-
   to-last crumb as a tappable back-link. The current page is already the
   page title beneath, so listing the full chain wastes vertical space.
   We don't change desktop behavior. */
@media (max-width: 720px) {
  /* HARD STOP on horizontal page scroll. Any wide child (table, image,
     overflowing toolbar) gets clipped at the viewport edge instead of
     pushing the body out. clip > hidden because it doesn't break
     position:sticky / position:fixed inside the body. */
  body.mx2-side,
  body.client-side {
    overflow-x: clip;
  }
  .breadcrumbs { margin: 0 0 12px; font-size: 10px; letter-spacing: 0.14em; }
  .breadcrumbs ol { flex-wrap: nowrap; }
  /* Hide every crumb except the last-tappable parent (second-to-last). */
  .breadcrumbs li { display: none; }
  .breadcrumbs li:nth-last-child(2) {
    display: inline-flex;
  }
  .breadcrumbs li:nth-last-child(2)::before {
    content: '‹';
    margin: 0 6px 0 0;
    opacity: 0.7;
    font-size: 14px;
    letter-spacing: 0;
    font-weight: 400;
  }
  .breadcrumbs li:nth-last-child(2) a {
    /* Truncate long parent labels */
    max-width: 70vw;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    display: inline-block;
  }
  /* Single-crumb pages (just "Dashboard") still get one visible item */
  .breadcrumbs li:only-child { display: inline-flex; }
  .breadcrumbs li:only-child::before { content: ''; margin: 0; }
}
.breadcrumbs a {
  text-decoration: none;
  transition: color var(--t-fast);
}
.breadcrumbs [aria-current='page'] {
  font-weight: 700;
}

/* Mode-specific colors picked up via the body class. */
body.client-side .breadcrumbs { color: var(--ink-soft); }
body.client-side .breadcrumbs a { color: var(--ink-mid); }
body.client-side .breadcrumbs a:hover { color: var(--mx2-gold-deep); }
body.client-side .breadcrumbs [aria-current='page'] { color: var(--ink); }

/* Light-mode admin chrome — breadcrumbs use the same warm-ink palette
   as the client side. Previous values (stone-300 base, gold-light
   links, #FFF current) were tuned for the old dark page chrome and
   washed out on cream. */
body.mx2-side .breadcrumbs { color: var(--ink-soft); font-size: 12px; }
body.mx2-side .breadcrumbs a {
  color: var(--ink-mid);
  border-bottom: 1px solid transparent;
  padding-bottom: 1px;
}
body.mx2-side .breadcrumbs a:hover {
  color: var(--mx2-gold-deep);
  border-bottom-color: var(--mx2-gold-deep);
}
body.mx2-side .breadcrumbs [aria-current='page'] { color: var(--ink); }

/* ── Avatars / org logos ────────────────────────────────────────────────── */
.avatar-preview {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  background: var(--cream-lt);
  color: var(--mx2-gold-deep);
  border: none;
  overflow: hidden;
  flex: none;
}
.avatar-preview img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.avatar-fallback {
  position: absolute;
  inset: 0;
  /* Hidden by default — img onerror handler flips this to flex when the
     image is missing or fails to load. Otherwise the fallback would sit
     on top of a successfully-loaded image. */
  display: none;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: 0.04em;
  background: var(--cream-lt);
  color: var(--mx2-gold-deep);
}
.avatar-xs  { width: 24px; height: 24px; border-radius: 999px; font-size: 11px; }
.avatar-xs .avatar-fallback { font-size: 11px; border-radius: 999px; }
.avatar-sm  { width: 32px; height: 32px; border-radius: 999px; font-size: 14px; }
.avatar-sm .avatar-fallback { font-size: 14px; border-radius: 999px; }
.avatar-md  { width: 64px; height: 64px; border-radius: 999px; font-size: 24px; }
.avatar-md .avatar-fallback { font-size: 24px; border-radius: 999px; }
.avatar-lg  { width: 120px; height: 120px; border-radius: 999px; font-size: 48px; }
.avatar-lg .avatar-fallback { font-size: 48px; border-radius: 999px; }
.avatar-xxl { width: 168px; height: 168px; border-radius: 999px; font-size: 64px; }
.avatar-xxl .avatar-fallback { font-size: 64px; border-radius: 999px; }

/* Org-logo: square, transparent bg, no frame, contain (preserves wordmarks) */
.org-logo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  background: transparent;
  border: none;
  overflow: hidden;
  flex: none;
  vertical-align: middle;
}
.org-logo img {
  width: 100%; height: 100%;
  object-fit: contain;
  padding: 3px;
  display: block;
}
.org-logo .avatar-fallback {
  background: transparent;
  color: var(--mx2-gold-deep);
}
.org-logo-xs { width: 28px; height: 28px; }
.org-logo-xs .avatar-fallback { font-size: 12px; }
.org-logo-sm { width: 40px; height: 40px; }
.org-logo-sm .avatar-fallback { font-size: 16px; }
.org-logo-md { width: 64px; height: 64px; }
.org-logo-md .avatar-fallback { font-size: 22px; }

/* Inline logo + label, used in tables and breadcrumbs. */
.cell-with-logo {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

/* Hero with logo on the left */
.hero-with-logo {
  display: flex;
  align-items: center;
  gap: 20px;
}
.hero-with-logo > .org-logo {
  flex: none;
}

/* Org logos: white background (logos are designed for white), object-fit
   contain so wide / tall / square wordmarks all show fully without crop.
   Transparent PNGs render on white instead of cream. */
.org-logo-preview {
  width: 120px;
  height: 120px;
  border-radius: 0;
  background: transparent;
  border: none;
}
.org-logo-preview img { object-fit: contain; padding: 6px; }
.org-logo-preview .avatar-fallback { border-radius: 0; background: transparent; }

.avatar-edit,
.org-logo-edit {
  display: grid;
  grid-template-columns: 140px 1fr;
  gap: 24px;
  align-items: start;
  margin-top: 12px;
}
@media (max-width: 720px) {
  .avatar-edit, .org-logo-edit { grid-template-columns: 1fr; }
}

/* Profile tab on /account — avatar column (stacked photo + upload +
   remove) on the left, info form on the right. Collapses to a single
   column on small screens. */
.account-profile-grid {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: 36px;
  align-items: start;
}
@media (max-width: 720px) {
  .account-profile-grid { grid-template-columns: 1fr; gap: 20px; }
}
.account-avatar-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}
.account-avatar-form {
  width: 100%;
}
/* Center the file-picker label inside the column and let it span. */
.account-avatar-col .po-slot-picker {
  width: 100%;
  justify-content: center;
}
.account-info-col {
  min-width: 0;
}

/* ── Banners ────────────────────────────────────────────────────────────── */
.banner {
  padding: 14px 18px;
  border-left: 3px solid var(--gold);
  background: var(--cream-lt);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  margin-bottom: var(--space-4);
  font-size: 14px;
  color: var(--ink);
}
.banner-warn    { border-left-color: var(--amber); background: #FDF5DC; }
.banner-danger  { border-left-color: var(--red);   background: #F8E0DB; }
.banner-success { border-left-color: var(--status-launched); background: #DEEAE2; }

/* ── Site footer (used on both client + Mx2 layouts) ────────────────────── */
.site-footer {
  border-top: 1px solid var(--rule-lt, #e6dfce);
  padding: 32px 32px calc(40px + env(safe-area-inset-bottom));
  margin-top: 56px;
  font-family: var(--font-body);
  color: var(--ink-soft, #5E5E5E);
  background: transparent;
}
.site-footer__inner {
  max-width: 1200px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1.2fr 1fr;
  gap: 32px;
  align-items: start;
}
.site-footer__brand {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.site-footer__mark {
  height: 38px;
  width: auto;
  align-self: flex-start;
}
/* The footer renders both light + dark variants of the mark. The
   layout's body class picks which to show. */
body:not(.mx2-side) .site-footer__mark--dark { display: none; }
body.mx2-side       .site-footer__mark--light { display: none; }
.site-footer__tag {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-soft, #5E5E5E);
  max-width: 380px;
}
.site-footer__tag a {
  color: var(--mx2-gold-deep, #876e51);
  text-decoration: underline;
}
.site-footer__legal p {
  margin: 0 0 8px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink-soft, #5E5E5E);
}
.site-footer__small {
  font-size: 11px !important;
  color: var(--ink-soft, #5E5E5E);
  opacity: 0.85;
}

/* Mx2 admin uses the same cream chrome the client side does, so the
   footer should render its body copy in a proper dark ink — not the
   muted on-dark cream the previous dark-mode admin needed. Pinning to
   --mx2-stone-600 keeps it slightly softer than primary body text but
   still legible on the cream page. */
body.mx2-side .site-footer {
  border-top-color: var(--mx2-stone-200);
  background: transparent;
}
body.mx2-side .site-footer__tag,
body.mx2-side .site-footer__legal p,
body.mx2-side .site-footer__small {
  color: var(--mx2-stone-600);
  opacity: 1;
}
body.mx2-side .site-footer__tag a {
  color: var(--mx2-gold-deep);
}

@media (max-width: 720px) {
  .site-footer {
    padding: 24px 16px calc(28px + env(safe-area-inset-bottom));
    margin-top: 32px;
  }
  .site-footer__inner {
    grid-template-columns: 1fr;
    gap: 18px;
  }
}

/* ── Asset Library — folder cards (drill-in index) ─────────────────────── */
.asset-folder-toolbar {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: 24px;
}
.asset-folder-sort {
  /* Inline style from the EJS sets display:flex + margin-left:auto.
     Adding flex-wrap so the inline sort links break onto a second
     row on narrow viewports instead of getting clipped at the
     viewport edge (no horizontal scroll on the page anymore). */
  flex-wrap: wrap;
  row-gap: 4px !important;
}
.asset-folder-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 18px;
  /* Hard cap at 5 cards across (5 * 220 + 4 * 18). Below this width
     auto-fill picks the natural column count for the viewport; above
     it the grid stops growing instead of spreading into wider cards
     that look gappy. */
  max-width: 1172px;
}
.asset-folder-card {
  display: flex;
  flex-direction: column;
  background: var(--surface, #FFFFFF);
  border: 1px solid var(--rule, #E6DEC7);
  border-radius: 8px;
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  transition: transform 120ms ease, box-shadow 120ms ease, border-color 120ms ease;
  /* position: relative so the nest-target ::after badge can absolutely-
     position itself within this card. */
  position: relative;
}
body.mx2-side .asset-folder-card {
  /* White card on the dark Mx2 layout — gallery cards read as discrete
     items and the folder name + description need dark-on-light to
     stay legible at the small font sizes used here. */
  background: #FFFFFF;
  color: var(--ink, #1A1A1A);
  border-color: var(--rule, #E6DEC7);
}
body.mx2-side .asset-folder-card__name { color: var(--ink, #1A1A1A); }
body.mx2-side .asset-folder-card__desc { color: var(--ink-soft, #5E5E5E); }
body.mx2-side .asset-folder-card__meta { color: var(--ink-soft, #5E5E5E); }
/* Draggable card cues for the manual-sort gallery index. We use
   Pointer Events for the drag (not HTML5 native drag), so the
   cursor cue is purely cosmetic. touch-action: none stops the
   browser from interpreting touch-drags as page-panning gestures
   on iPad / Chrome DevTools touch emulation. */
.asset-folder-card--draggable { cursor: grab; touch-action: none; -webkit-user-select: none; user-select: none; }
.asset-folder-card--draggable:active { cursor: grabbing; }
.asset-folder-card--dragging { opacity: 0.5; transform: scale(0.97); }
/* Suppress native image-drag on thumbnails inside draggable cards.
   Without this, click+drag on a thumb starts the browser's "drag the
   image" gesture instead of our Pointer Events drag. The HTML
   draggable="false" attribute is the primary fix; these are belt-
   and-suspenders for browsers that ignore the attribute. */
.asset-folder-card--draggable img,
.asset-folder-card--draggable .asset-folder-card__preview {
  -webkit-user-drag: none;
  -khtml-user-drag: none;
  -moz-user-drag: none;
  user-drag: none;
  -webkit-touch-callout: none;
}
/* Floating clone that follows the cursor while dragging. JS sets
   most styles inline; this just neutralizes any hover/transition
   that would mis-render on the ghost. */
.asset-folder-card--ghost {
  transition: none !important;
  cursor: grabbing !important;
}

/* "This is a gallery" badge — always-visible folder icon pill in the
   TOP-left of every gallery card's preview area. Distinguishes a
   gallery card from a loose-asset card at a glance, regardless of
   whether the preview shows a mosaic or the empty-state icon. Paired
   with the existing parent-badge (bottom-left) when a gallery has
   sub-galleries inside — so a parent gallery can show both. */
.asset-folder-card__gallery-badge {
  position: absolute;
  top: 6px;
  left: 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  background: rgba(201, 168, 106, 0.95); /* gold */
  color: #1a1410;
  border-radius: 999px;
  pointer-events: none;
  z-index: 2;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}
.asset-folder-card__gallery-badge svg {
  width: 14px; height: 14px;
}

/* "This is a parent gallery" badge — appears in the bottom-left of
   the preview mosaic when the gallery contains sub-galleries. Helps
   users distinguish container galleries from leaf galleries at a
   glance, especially since the mosaic now backfills from descendant
   thumbs (so a container gallery looks visually identical to a leaf
   gallery without this cue). */
.asset-folder-card__preview--parent { /* anchor for the absolutely-positioned badge */ }
.asset-folder-card__parent-badge {
  position: absolute;
  bottom: 6px;
  left: 6px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 8px 3px 6px;
  background: rgba(0, 0, 0, 0.65);
  color: #FFFFFF;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  border-radius: 999px;
  pointer-events: none;
  z-index: 2;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
body.mx2-side .asset-folder-card__parent-badge {
  /* On the dark Mx2 admin layout the cards are white; the badge stays
     dark for contrast on the colorful thumbnail mosaic underneath. */
}

/* "Hidden from client" overlay — Mx2-admin-only. A translucent
   diagonal-stripe sheet drops across the preview area with an
   eye-slash pill so it's impossible to miss while scanning a
   gallery grid that the client will never see this one. Client +
   share views never pass the showHiddenIndicator flag, so the
   overlay can't leak. */
.asset-folder-card-wrap--hidden-from-client .asset-folder-card__preview {
  position: relative;
}
.asset-folder-card-wrap--hidden-from-client .asset-folder-card__preview::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    repeating-linear-gradient(
      45deg,
      rgba(159, 136, 108, 0.18) 0,
      rgba(159, 136, 108, 0.18) 12px,
      rgba(159, 136, 108, 0.08) 12px,
      rgba(159, 136, 108, 0.08) 24px
    ),
    rgba(20, 18, 16, 0.30);
  pointer-events: none;
  z-index: 1;
  border-radius: inherit;
}
.asset-folder-card__hidden-overlay {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px 6px 10px;
  background: rgba(15, 13, 10, 0.85);
  color: #FFFFFF;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-radius: 999px;
  pointer-events: none;
  z-index: 3;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.30);
  white-space: nowrap;
}
.asset-folder-card__hidden-overlay svg { flex-shrink: 0; }
/* Body text dims slightly so the card reads as "muted / hidden". */
.asset-folder-card-wrap--hidden-from-client .asset-folder-card__body {
  opacity: 0.72;
}
/* Nest-target — applied while a dragged card is hovering in the
   center of THIS card. Tells the user "drop here to put the dragged
   gallery inside this one." Wrap-around outline + cream tint so it
   stands out visibly without being garish. */
.asset-folder-card--nest-target {
  outline: 3px solid var(--mx2-gold-deep, #876e51);
  outline-offset: -3px;
  background: var(--mx2-paper, #FBF9F4);
  box-shadow: 0 0 0 6px rgba(159, 136, 108, 0.18);
  transform: translateY(-1px) scale(1.01);
  transition: outline 80ms ease, transform 120ms ease, box-shadow 120ms ease;
}
.asset-folder-card--nest-target::after {
  content: 'Drop to nest →';
  position: absolute;
  top: 6px;
  right: 8px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--mx2-gold-deep, #876e51);
  background: #FFFFFF;
  padding: 3px 7px;
  border-radius: 4px;
  border: 1px solid var(--mx2-gold-deep, #876e51);
  pointer-events: none;
  z-index: 2;
}
/* Insert-before / insert-after — vertical gold bar on the leading or
   trailing edge of the target card. Renders during dragover when the
   user is in the reorder edge zone (vs. the center nest zone). The
   pseudo-element is overlaid via box-shadow so it doesn't shift layout. */
.asset-folder-card--insert-before {
  box-shadow: -4px 0 0 0 var(--mx2-gold-deep, #876e51);
}
.asset-folder-card--insert-after {
  box-shadow: 4px 0 0 0 var(--mx2-gold-deep, #876e51);
}

/* Promote drop zone — sits above the sub-gallery grid on folder.ejs.
   Drag a card onto it to move that folder up one level (to the
   current gallery's parent, or to top level if at root). Always
   visible (subtle dashed gold) so users know the gesture exists;
   highlights solid when a drag enters. */
.asset-folder-promote-zone {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 14px 20px;
  margin: 0 0 14px;
  background: var(--mx2-paper, #FBF9F4);
  border: 2px dashed var(--mx2-gold-deep, #876e51);
  border-radius: 8px;
  text-align: center;
  transition: background 120ms ease, border-style 120ms ease, transform 120ms ease;
}
.asset-folder-promote-zone--active {
  background: rgba(159, 136, 108, 0.18);
  border-style: solid;
  transform: translateY(-1px);
}

/* "Move to..." button on each folder card — appears in the top-right
   corner. Always visible (subtle), so users know they can move a
   gallery without needing to discover the drag gesture. Click opens
   the move-folder modal below. */
.asset-folder-card-wrap { display: flex; }
.asset-folder-card-wrap > .asset-folder-card { width: 100%; }
/* Gallery multi-select checkbox (Drew 2026-06-08). Top-left, on its own
   paint layer above the thumb mosaic, comfortably tappable on mobile.
   Lives OUTSIDE the card <a> so a tap selects rather than navigates. */
.asset-folder-card__select {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 4;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.94);
  border-radius: 7px;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.22);
  cursor: pointer;
}
.asset-folder-card__select input {
  width: 17px;
  height: 17px;
  margin: 0;
  cursor: pointer;
  accent-color: var(--mx2-teal, #214c60);
}

.asset-folder-card__move-btn {
  position: absolute;
  top: 8px;
  right: 8px;
  /* Slightly bigger + higher-contrast than v1 — the v1 26px gold-on-
     transparent was too subtle for users to discover. Now: 32px,
     gold pill with white icon, visible against any thumbnail. */
  height: 32px;
  padding: 0 12px 0 10px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--mx2-gold-deep, #876e51);
  border: 0;
  border-radius: 999px;
  color: #FFFFFF;
  cursor: pointer;
  z-index: 3;
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18);
  transition: background 120ms ease, transform 120ms ease, box-shadow 120ms ease;
}
.asset-folder-card__move-btn::after {
  content: 'Move';
}
.asset-folder-card__move-btn:hover {
  background: var(--mx2-gold, #9f886c);
  transform: translateY(-1px);
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.24);
}
.asset-folder-card__move-btn:focus-visible {
  outline: 2px solid var(--mx2-gold);
  outline-offset: 2px;
}

/* Per-asset "Move to gallery" button — same gold pill treatment as
   the gallery-card move button, scaled down for the smaller asset
   tile. Sits in the top-right corner; only shown on hover so it
   doesn't clutter the grid at rest. Always visible when focused via
   keyboard so it stays reachable without a pointer. */
.asset-tile__move-btn {
  position: absolute;
  top: 6px;
  /* Shift left to leave clear space for the absolute-positioned
     .asset-tile__remove × (right: 4px, 22px wide). Without this
     the Move pill overlapped the X on every tile. Drew, 2026-05-22. */
  right: 34px;
  z-index: 3;
  height: 26px;
  padding: 0 10px 0 8px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: var(--mx2-gold-deep, #876e51);
  border: 0;
  border-radius: 999px;
  color: #FFFFFF;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.20);
  opacity: 0;
  transition: opacity 120ms ease, background 120ms ease, transform 120ms ease;
}
.asset-tile__move-btn::after { content: 'Move'; }
.asset-tile:hover .asset-tile__move-btn,
.asset-tile__move-btn:focus-visible {
  opacity: 1;
}
.asset-tile__move-btn:hover {
  background: var(--mx2-gold, #9f886c);
  transform: translateY(-1px);
}
.asset-tile__move-btn:focus-visible {
  outline: 2px solid var(--mx2-gold);
  outline-offset: 2px;
}

/* Touch devices (no hover): the per-asset Move pill was rendering on top
   of the image and stealing the first tap — opening Move instead of the
   preview. Hide it on touch; move assets via bulk-select instead. The
   GALLERY-card Move pill goes the other way: keep it always-visible on
   touch, since drag-to-reorder is disabled there and the pill is the only
   way to move a gallery on a phone. Drew 2026-06-17. */
@media (hover: none) {
  .asset-tile__move-btn { display: none !important; }
  .asset-folder-card__move-btn { opacity: 1; }
}

/* Move-folder picker modal. Centered card with a scrollable list of
   destinations. Click outside or Escape closes. Active row (current
   parent) renders in italic so the user sees current state at a glance. */
.move-folder-modal {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: rgba(15, 15, 15, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  opacity: 0;
  transition: opacity 140ms ease-out;
}
.move-folder-modal[hidden] { display: none; }
.move-folder-modal--visible { opacity: 1; }
.move-folder-modal__panel {
  background: #FFFFFF;
  border: 1px solid var(--rule, #d9d2c0);
  border-radius: 8px;
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.32);
  max-width: 480px;
  width: 100%;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  font-family: var(--font-sans);
  color: var(--ink, #1A1A1A);
}
.move-folder-modal__title {
  font-size: 18px;
  font-weight: 600;
  margin: 0;
  padding: 18px 22px 6px;
}
.move-folder-modal__lead {
  margin: 0;
  padding: 0 22px 14px;
  font-size: 13px;
}
/* The <form> sits between the panel (flex column, max-height 80vh) and
   the scrollable list. Without making it a flex column that can shrink,
   the list's overflow-y never engages — it grows to fit every option and
   spills out the bottom of the panel (Drew 2026-06-08). */
.move-folder-modal__panel > form {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1 1 auto;
}
.move-folder-modal__list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  border-top: 1px solid var(--rule, #d9d2c0);
  padding: 6px 0;
}
.move-folder-modal__option {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 22px;
  background: transparent;
  border: 0;
  border-left: 3px solid transparent;
  cursor: pointer;
  font: inherit;
  color: inherit;
  text-align: left;
  transition: background 100ms ease;
}
.move-folder-modal__option:hover { background: var(--mx2-paper, #FBF9F4); }
.move-folder-modal__option:focus-visible {
  outline: 2px solid var(--mx2-gold);
  outline-offset: -2px;
}
.move-folder-modal__option:disabled { opacity: 0.5; cursor: wait; }
.move-folder-modal__option--current {
  font-style: italic;
  border-left-color: var(--mx2-gold-deep, #876e51);
}
.move-folder-modal__option--current::after {
  content: 'current parent';
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--mx2-gold-deep, #876e51);
  font-style: normal;
}
.move-folder-modal__opt-name { font-weight: 500; }
.move-folder-modal__actions {
  border-top: 1px solid var(--rule, #d9d2c0);
  padding: 12px 22px;
  display: flex;
  justify-content: flex-end;
}
.asset-folder-card:hover {
  transform: translateY(-1px);
  border-color: var(--mx2-gold-deep, #c89a3c);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
}
.asset-folder-card__preview {
  aspect-ratio: 4 / 3;
  background: #F6F2E7;
  position: relative;
  display: grid;
  overflow: hidden;
}
body.mx2-side .asset-folder-card__preview {
  background: rgba(255, 255, 255, 0.06);
}
.asset-folder-card__preview--mosaic {
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  gap: 1px;
}
.asset-folder-card__preview--single {
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
}
.asset-folder-card__preview img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.asset-folder-card__preview--empty {
  display: flex;
  align-items: center;
  justify-content: center;
}
.asset-folder-card__empty-icon {
  color: var(--mx2-gold-deep, #c89a3c);
  opacity: 0.4;
}
.asset-folder-card__cell-blank {
  background: rgba(0, 0, 0, 0.06);
}
body.mx2-side .asset-folder-card__cell-blank {
  background: rgba(255, 255, 255, 0.04);
}
.asset-folder-card__body {
  padding: 12px 14px 14px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.asset-folder-card__name {
  font-family: var(--font-serif);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.2;
}
.asset-folder-card__desc {
  font-size: 12px;
  line-height: 1.3;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.asset-folder-card__meta {
  display: flex;
  gap: 6px;
  margin-top: 2px;
  flex-wrap: wrap;
}

.asset-folder-meta {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 6px;
}

/* ── Bulk-action bar (folder detail) ───────────────────────────────────── */
.asset-bulk-bar {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--mx2-gold-soft, #f6e9c8);
  border: 1px solid var(--mx2-gold-deep, #c89a3c);
  border-radius: 6px;
  padding: 10px 14px;
  margin-bottom: 16px;
}
body.mx2-side .asset-bulk-bar {
  background: rgba(200, 154, 60, 0.18);
  border-color: var(--mx2-gold-deep, #c89a3c);
}
.asset-bulk-bar__form {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.asset-bulk-bar__label {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
}
.asset-bulk-bar__form select {
  flex: 1;
  min-width: 200px;
  max-width: 360px;
}

/* Bulk-select checkbox on tiles */
.asset-tile { position: relative; }
.asset-tile__select {
  position: absolute;
  top: 6px;
  left: 6px;
  z-index: 2;
  width: 22px;
  height: 22px;
  border-radius: 4px;
  background: rgba(20, 20, 20, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  transition: opacity 120ms ease;
}
.asset-tile:hover .asset-tile__select,
.asset-tile__select:has(input:checked) {
  opacity: 1;
}
.asset-tile__select input {
  margin: 0;
  cursor: pointer;
}

/* ── Asset Library — grid + tile + upload bar + preview modal ──────────── */
.asset-collection-section { margin-bottom: 32px; }
.asset-collection-section__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}
.asset-collection-section__head h2 {
  font-family: var(--font-serif);
  font-size: 20px;
  font-weight: 600;
  margin: 0;
  color: inherit;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.asset-collection-icon {
  flex: none;
  color: var(--mx2-gold-deep);
}
.asset-collection-icon--loose {
  color: var(--ink-soft, #6b6b6b);
  opacity: 0.7;
}
.asset-collection-name { color: inherit; }
.asset-collection-desc { font-size: 13px; font-weight: 400; }
.asset-grid {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 14px;
}
.asset-tile {
  position: relative;
  background: #FFFFFF;
  border: 1px solid var(--rule-lt);
  border-radius: 6px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
body.mx2-side .asset-tile {
  /* White card on the dark Mx2 layout — same treatment as the gallery
     index folder cards. The thumbnail area inside keeps its own beige
     placeholder when there's no image, and the meta strip uses dark
     text for legibility against the white. */
  background: #FFFFFF;
  color: var(--ink, #1A1A1A);
  border-color: var(--rule-lt, #E6DEC7);
}
body.mx2-side .asset-tile__name { color: var(--ink, #1A1A1A); }
body.mx2-side .asset-tile__size { color: var(--ink-soft, #5E5E5E); }
.asset-tile__thumb {
  background: #F6F2E7;
  border: 0;
  cursor: pointer;
  height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 4px;
  padding: 0;
  width: 100%;
  color: var(--ink-soft, #5E5E5E);
  position: relative;
}
body.mx2-side .asset-tile__thumb {
  background: rgba(255, 255, 255, 0.06);
  color: var(--mx2-text-mid, rgba(244, 234, 211, 0.6));
}
.asset-tile__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.asset-tile__icon { display: inline-flex; }
.asset-tile__ext {
  font-family: var(--font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
/* Play-arrow badge layered over a video tile's poster frame so the tile
   still reads as "video" at a glance even though it shows a still. */
.asset-tile__play-badge {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  color: #FFFFFF;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}
.asset-tile__play-badge svg {
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.7));
  opacity: 0.92;
}
/* File-type pill (JPG / MP4 / YT / etc.) in the bottom-left of the
   thumbnail. Reads at a glance even when the thumb itself is busy.
   pointer-events: none so it doesn't intercept clicks on the thumb. */
.asset-tile__type-badge {
  position: absolute;
  left: 6px;
  bottom: 6px;
  pointer-events: none;
  background: rgba(20, 20, 20, 0.72);
  color: #FFFFFF;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.06em;
  padding: 2px 6px;
  border-radius: 3px;
  line-height: 1.2;
  text-transform: uppercase;
}
/* Duration chip — bottom-right mirror of the type badge for video tiles
   (ffprobe-extracted seconds formatted as 0:27 or 1:23:45). */
.asset-tile__duration-badge {
  position: absolute;
  right: 6px;
  bottom: 6px;
  pointer-events: none;
  background: rgba(20, 20, 20, 0.72);
  color: #FFFFFF;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.04em;
  padding: 2px 6px;
  border-radius: 3px;
  line-height: 1.2;
}
.asset-tile__meta {
  padding: 8px 10px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-size: 12px;
}
.asset-tile__name {
  font-family: var(--font-mono);
  font-size: 11px;
  word-break: break-all;
  line-height: 1.3;
}
.asset-tile__description {
  /* Auto-described (Claude vision) or human-written. Clamped to two
     lines so tiles in a grid stay uniform height. Full text in the
     title attribute for hover + visible in the asset-detail modal. */
  font-size: 11px;
  line-height: 1.35;
  color: var(--ink-soft, #5E5E5E);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
  word-break: break-word;
}
body.mx2-side .asset-tile__description { color: var(--ink-soft, #5E5E5E); }
.asset-tile__size {
  font-size: 10px;
  color: var(--ink-soft, #5E5E5E);
}
.asset-tile__remove {
  position: absolute;
  top: 4px;
  right: 4px;
  margin: 0;
}
.asset-tile__remove button {
  background: rgba(20, 20, 20, 0.65);
  color: #FFFFFF;
  border: 0;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}
.asset-tile__remove button:hover { background: var(--mx2-rust, #b14b3a); }

/* Mx2 per-client upload bar */
.asset-upload-bar { padding: 16px 18px; }
body.mx2-side .asset-upload-bar {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--mx2-rule);
  border-radius: 6px;
}
.asset-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.asset-action-btn {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 12px 16px;
  min-width: 96px;
  background: var(--mx2-gold, #c9a86a);
  color: #1A1A1A;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
  font-family: inherit;
  text-decoration: none;
  transition: background 120ms ease, transform 120ms ease;
}
.asset-action-btn:hover { background: #d8b87a; transform: translateY(-1px); }
.asset-action-btn:active { transform: translateY(0); }
.asset-action-btn input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.asset-action-btn__icon { display: block; flex-shrink: 0; }
.asset-action-btn__label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 700;
}
/* The upload variant used to flip to black-on-gold so it stood out
   against the old dark admin chrome. The page is cream now, so just
   render it identically to the base gold-on-ink button — matches the
   Add link / Add reference buttons next to it. The selector stays so
   any JS / templates referencing it keep working as a no-op. */
.asset-action-btn--upload { /* inherits base gold styles */ }
/* The upload trigger is a <label> (wraps the hidden <input type=file>),
   so the global `body.mx2-side label { color: var(--mx2-text) }` rule
   shadowed the button's dark-ink color and made the text invisible on
   gold. Pin the asset-action-btn label color so the upload trigger
   reads the same as the sibling <button> ones. */
body.mx2-side .asset-action-btn,
body.mx2-side label.asset-action-btn {
  color: #1A1A1A;
}
.asset-actions__upload-target {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
}
.asset-actions__collection-select {
  width: auto;
  min-width: 180px;
  padding: 6px 10px;
  font-size: 13px;
  border-radius: 4px;
}

.asset-panel {
  margin-top: 14px;
  padding: 14px 16px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--rule, #d9d2c0);
  border-radius: 6px;
}
body.mx2-side .asset-panel {
  background: rgba(212, 175, 110, 0.08);
  border-color: var(--mx2-rule, #2a2a2a);
}
.asset-panel[hidden] { display: none; }
.asset-panel__actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 6px;
}

/* Live progress banner shown above the upload form during multi-file ingest */
.asset-upload-banner {
  background: rgba(212, 175, 110, 0.16);
  border: 1px solid rgba(212, 175, 110, 0.45);
  border-radius: 6px;
  padding: 10px 14px;
  margin: 0 0 12px;
  font-size: 13px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.asset-upload-banner__bar {
  /* Bumped from 3px → 6px now that the bar reports real bytes-uploaded
     progress — the prior version was a faux progress so a thin line was
     fine; with real progress the fill needs to read clearly. */
  height: 6px;
  background: rgba(0, 0, 0, 0.08);
  overflow: hidden;
  border-radius: 3px;
}
.asset-upload-banner__fill {
  height: 100%;
  background: var(--mx2-gold, #c9a86a);
  width: 0;
  transition: width 150ms ease;
}
.asset-upload-banner__text {
  font-family: var(--font-mono);
  font-size: 12px;
}

/* Modal previewer (used on both Mx2 and client sides) */
.asset-modal {
  position: fixed;
  inset: 0;
  background: rgba(10, 10, 10, 0.82);
  z-index: 10100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.asset-modal[hidden] { display: none; }
.asset-modal__inner {
  max-width: min(1200px, 96vw);
  max-height: 92vh;
  background: #0F0F0F;
  border-radius: 6px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
/* When the active asset is a YouTube/Vimeo embed, the iframe sizes
   itself by aspect ratio (16:9) so we want the surrounding modal to
   sit close to the iframe footprint rather than the 1200px image
   default. asset-library.js toggles this class on open/close. */
.asset-modal.asset-modal--embed .asset-modal__inner {
  max-width: min(1150px, 96vw);
}
.asset-modal__media {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #000;
  overflow: auto;
}
.asset-modal__media img {
  max-width: 100%;
  max-height: 80vh;
  display: block;
}
.asset-modal__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Wrap so the action buttons (Download etc.) never get clipped off the
     right edge on a narrow phone. row-gap keeps wrapped rows legible.
     Drew 2026-06-17. */
  flex-wrap: wrap;
  gap: 10px 12px;
  padding: 12px 16px;
  background: rgba(255, 255, 255, 0.04);
  color: #f4ead3;
  font-family: var(--font-mono);
  font-size: 12px;
}
/* .btn sets display:inline-flex, which beats the UA [hidden]{display:none}
   — so a `<a class="btn" hidden>` in the bar (the Download-JPG button, the
   View-in-gallery link) stayed visible even on assets that shouldn't show
   it (e.g. the JPG button on a video). Restore the hidden behaviour for
   bar buttons. Drew 2026-06-17. */
.asset-modal__bar .btn[hidden] { display: none; }
/* The default .btn-secondary is black-on-transparent which renders
   as invisible-on-dark inside this modal. Flip the colors so the
   Download button reads as cream-on-dark with a gold-on-hover. */
.asset-modal__bar .btn-secondary {
  color: #f4ead3;
  border-color: rgba(244, 234, 211, 0.55);
  background: transparent;
}
.asset-modal__bar .btn-secondary:hover:not(:disabled) {
  color: var(--mx2-black, #1a1a1a);
  background: var(--mx2-gold, #c9a86a);
  border-color: var(--mx2-gold, #c9a86a);
}
.asset-modal__name-wrap {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  flex: 1;
}
.asset-modal__name {
  /* overflow-wrap: anywhere breaks long filenames only when needed
     (instead of mid-character on every word like break-all did). On
     mobile we constrain to a single line + ellipsis so the bar stays
     readable; full filename is always available in the details
     panel. */
  overflow-wrap: anywhere;
  min-width: 0;
}
@media (max-width: 720px) {
  .asset-modal__name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}
.asset-modal__name-input {
  flex: 1;
  min-width: 0;
  padding: 6px 10px;
  border: 1px solid var(--mx2-gold-deep, #c89a3c);
  border-radius: 4px;
  background: #FFFFFF;
  color: var(--ink, #1A1A1A);
  font-family: var(--font-mono);
  font-size: 13px;
}
.asset-modal__name-input:focus {
  outline: none;
  border-color: var(--mx2-gold, #c9a86a);
  box-shadow: 0 0 0 3px rgba(200, 169, 94, 0.25);
}
/* Modal pencil overrides the .editable-name__pencil colors so it stays
   readable on the modal's dark surface. Same shape, adjusted contrast. */
.asset-modal__name-wrap .editable-name__pencil {
  color: rgba(244, 234, 211, 0.8);
  border-color: rgba(244, 234, 211, 0.35);
  flex: none;
}
.asset-modal__name-wrap .editable-name__pencil:hover {
  color: var(--mx2-gold, #c9a86a);
  border-color: var(--mx2-gold, #c9a86a);
}
.asset-modal__close {
  position: absolute;
  top: 16px;
  right: 16px;
  background: rgba(0, 0, 0, 0.6);
  color: #FFFFFF;
  border: 0;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  font-size: 22px;
  cursor: pointer;
  z-index: 1;
}
/* Prev / next arrows in the asset preview modal — cycle through tiles
   in the visible grid (also keyboard ←/→). Hidden via [hidden] when
   the gallery has 0 or 1 tiles, disabled at the ends. */
.asset-modal__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  background: rgba(0, 0, 0, 0.6);
  color: #FFFFFF;
  border: 0;
  width: 48px;
  height: 56px;
  font-size: 32px;
  line-height: 1;
  cursor: pointer;
  z-index: 1;
  border-radius: 4px;
  transition: background 120ms ease, opacity 120ms ease;
}
.asset-modal__nav:hover { background: rgba(0, 0, 0, 0.85); }
.asset-modal__nav:disabled { opacity: 0.25; cursor: not-allowed; }
.asset-modal__nav--prev { left: 16px; }
.asset-modal__nav--next { right: 16px; }
.asset-modal__nav[hidden] { display: none; }
/* Position indicator ("3 / 12") in the bar next to the asset name. */
.asset-modal__position {
  margin-left: auto;
  margin-right: 12px;
  color: rgba(244, 234, 211, 0.6);
}
/* ⓘ button in the bar that toggles the slide-up details panel. */
.asset-modal__details-toggle {
  background: transparent;
  border: 0;
  color: rgba(244, 234, 211, 0.7);
  font-size: 18px;
  line-height: 1;
  padding: 4px 8px;
  cursor: pointer;
  border-radius: 4px;
  transition: color 120ms ease, background 120ms ease;
}
.asset-modal__details-toggle:hover {
  color: var(--mx2-gold, #c9a86a);
  background: rgba(255, 255, 255, 0.06);
}
.asset-modal[data-details-open] .asset-modal__details-toggle {
  color: var(--mx2-gold, #c9a86a);
}
/* Share button — sibling shape to the details toggle, with the
   share-network SVG instead of the ⓘ glyph. One-click create-and-
   copy: posts to the tile's data-share-url, copies the returned
   URL, and toasts. */
.asset-modal__share-btn {
  background: transparent;
  border: 0;
  color: rgba(244, 234, 211, 0.7);
  padding: 6px 8px;
  cursor: pointer;
  border-radius: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 120ms ease, background 120ms ease;
}
.asset-modal__share-btn:hover {
  color: var(--mx2-gold, #c9a86a);
  background: rgba(255, 255, 255, 0.06);
}
.asset-modal__share-btn:disabled { opacity: 0.5; cursor: progress; }

/* Share dialog — small overlay card centered over the asset modal.
   Mounted inside .asset-modal so it inherits the dark overlay and
   z-index context. Click outside the card OR the × closes it; Enter
   inside the recipient field triggers Send. */
.asset-modal__share-dialog {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(8, 6, 4, 0.55);
  z-index: 10;
  padding: 20px;
}
.asset-modal__share-dialog[hidden] { display: none; }
.asset-modal__share-dialog-card {
  background: #FFFFFF;
  color: var(--ink, #1A1A1A);
  border-radius: 10px;
  padding: 24px;
  width: 100%;
  max-width: 440px;
  box-shadow: 0 24px 48px rgba(0, 0, 0, 0.35);
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.asset-modal__share-dialog-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}
.asset-modal__share-dialog-head .section-kicker {
  color: var(--mx2-gold-deep, #876e51);
}
.asset-modal__share-dialog-sub {
  margin-top: 4px;
  color: var(--ink-soft, #5E5E5E);
}
.asset-modal__share-dialog-close {
  background: none;
  border: 0;
  font-size: 22px;
  line-height: 1;
  color: var(--ink-soft, #5E5E5E);
  cursor: pointer;
  padding: 0;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  transition: background 80ms ease, color 80ms ease;
}
.asset-modal__share-dialog-close:hover {
  background: var(--mx2-paper-2, #f3eee2);
  color: var(--ink, #1A1A1A);
}
.asset-modal__share-dialog .field { margin: 0; }
.asset-modal__share-dialog label {
  color: var(--ink-soft, #5E5E5E);
  font-size: 12px;
  letter-spacing: 0.04em;
  font-weight: 500;
  margin-bottom: 4px;
  display: block;
}
.asset-modal__share-dialog input,
.asset-modal__share-dialog textarea {
  background: #FFFFFF;
  color: var(--ink, #1A1A1A);
  border: 1px solid var(--rule, #d9d2c0);
  border-radius: 4px;
  padding: 8px 10px;
  font-size: 13px;
  font-family: inherit;
  width: 100%;
}
.asset-modal__share-dialog input:focus,
.asset-modal__share-dialog textarea:focus {
  outline: none;
  border-color: var(--mx2-gold, #c9a86a);
  box-shadow: 0 0 0 3px rgba(200, 169, 94, 0.2);
}
.asset-modal__share-dialog-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 4px;
}
.asset-modal__share-dialog-actions .btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
/* Slide-up details panel between media and bar. Collapses to height
   0 when closed; max-height transition gives the slide-up effect. */
.asset-modal__details {
  max-height: 0;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.55);
  color: rgba(244, 234, 211, 0.92);
  font-family: var(--font-mono);
  font-size: 12px;
  transition: max-height 240ms ease;
}
.asset-modal[data-details-open] .asset-modal__details {
  max-height: 50vh;
  overflow-y: auto;
  border-top: 1px solid rgba(244, 234, 211, 0.12);
}
.asset-modal__details-list {
  margin: 0;
  padding: 14px 16px;
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: 16px;
  row-gap: 8px;
}
.asset-modal__details-row {
  display: contents;
}
.asset-modal__details-row[hidden] { display: none; }
.asset-modal__details-row dt {
  color: rgba(244, 234, 211, 0.55);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 10.5px;
  align-self: baseline;
}
.asset-modal__details-row dd {
  margin: 0;
  overflow-wrap: anywhere;
  color: #f4ead3;
}
/* Edit / Save / Cancel buttons at the top of the panel (mx2 only —
   the wrapper stays hidden when the tile has no data-edit-url). */
.asset-modal__details-actions {
  display: flex;
  gap: 8px;
  padding: 10px 16px 0 16px;
  justify-content: flex-end;
}
.asset-modal__details-actions[hidden] { display: none; }
.asset-modal__details-actions .btn-secondary {
  color: #f4ead3;
  border-color: rgba(244, 234, 211, 0.45);
  background: transparent;
}
/* Bump specificity past .btn { display: inline-flex } so the HTML
   hidden attribute actually hides these buttons. Without this, Save
   and Cancel render before the user has tapped Edit details. */
.asset-modal__details-actions .btn[hidden] { display: none; }
.asset-modal__details-actions .btn-secondary:hover:not(:disabled) {
  color: var(--mx2-black, #1a1a1a);
  background: var(--mx2-gold, #c9a86a);
  border-color: var(--mx2-gold, #c9a86a);
}
/* Editable inputs inside the panel — text + textarea share styles.
   White fill with dark text matches every other editable input in
   the system (the modal overlay is dark but text-entry surfaces stay
   light per the project rule). Selector chained through .asset-modal
   + .asset-modal__details (0,3,1) to beat mx2.css's
   body.mx2-side input[type='text'] (0,2,1). */
.asset-modal .asset-modal__details input.asset-modal__details-input,
.asset-modal .asset-modal__details textarea.asset-modal__details-input {
  width: 100%;
  padding: 6px 10px;
  border: 1px solid var(--mx2-gold-deep, #c89a3c);
  border-radius: 4px;
  background: #FFFFFF;
  color: var(--ink, #1A1A1A);
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.45;
  resize: vertical;
  min-height: 32px;
}
.asset-modal .asset-modal__details input.asset-modal__details-input:focus,
.asset-modal .asset-modal__details textarea.asset-modal__details-input:focus {
  outline: none;
  border-color: var(--mx2-gold, #c9a86a);
  box-shadow: 0 0 0 3px rgba(200, 169, 94, 0.25);
}
.asset-modal__details-checkbox {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  user-select: none;
}
.asset-modal__details-checkbox input { margin: 0; }

@media (max-width: 720px) {
  .asset-grid { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; }
  .asset-tile__thumb { height: 96px; }
  .asset-upload-form { flex-direction: column; align-items: stretch; }
  .asset-upload-form__collection { width: 100%; }
  .asset-modal { padding: 0; }
  .asset-modal__inner { max-width: 100%; max-height: 100%; height: 100%; border-radius: 0; }
  .asset-modal__close { top: env(safe-area-inset-top, 12px); right: 12px; }
}

/* ── Centered toast (replaces top-of-page flash banners) ──────────────────
   Restyled 2026-05-22 to match the design system glass look — same
   frosted-cream + gold-tinted border + layered-shadow recipe used by
   .mx2-confirm / .glass-modal / .move-folder-modal. The kind-specific
   left rail stays as the at-a-glance success/danger/warn signal.

   2026-05-23: wrapped in a .toast-backdrop overlay so the page dims
   behind the toast (matches .mx2-confirm-backdrop pattern), and the
   toast positions itself via flex-center on the backdrop instead of
   position:fixed on the toast itself. The previous setup positioned
   correctly in isolation but if anything in the ancestor chain had
   transform/filter/contain set, position:fixed resolved against that
   ancestor instead of the viewport — Drew reported the toast
   rendering inline on the admin clients page. Backdrop-on-the-flex-
   container makes that ambiguity irrelevant. */
.toast-backdrop {
  position: fixed;
  inset: 0;
  z-index: 10001;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(15, 12, 8, 0.42);
  opacity: 0;
  visibility: hidden;
  transition: opacity 200ms ease, visibility 200ms ease;
  pointer-events: none;
}
.toast-backdrop.toast-backdrop--visible {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}
.toast {
  position: relative;
  background: rgba(255, 252, 244, 0.92);
  backdrop-filter: blur(24px) saturate(1.3);
  -webkit-backdrop-filter: blur(24px) saturate(1.3);
  border: 1px solid rgba(200, 169, 94, 0.28);
  border-left: 4px solid var(--mx2-gold, #c9a86a);
  border-radius: 14px;
  padding: 22px 56px 22px 26px;
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.45;
  color: var(--ink);
  box-shadow:
    0 30px 80px rgba(40, 28, 12, 0.32),
    0 10px 30px rgba(40, 28, 12, 0.14),
    inset 0 1px 0 rgba(255, 255, 255, 0.6);
  min-width: 320px;
  max-width: min(480px, 90vw);
  transform: scale(0.96);
  transition: transform 200ms ease;
}
.toast-backdrop--visible .toast {
  transform: scale(1);
}
/* Legacy single-element render (toast.js hydratePendingToast inserts a
   <div class="toast"> directly into body without the backdrop). Give it
   its own position:fixed centering as a fallback. */
.toast:not(.toast-backdrop > .toast) {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.96);
  opacity: 0;
  visibility: hidden;
  transition: opacity 200ms ease, visibility 200ms ease, transform 200ms ease;
}
.toast:not(.toast-backdrop > .toast).toast--visible {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, -50%) scale(1);
}
.toast--success { border-left-color: var(--status-launched, #4a8c5e); }
.toast--danger  { border-left-color: var(--red, #b14b3a); }
.toast--warn    { border-left-color: var(--amber, #d4a755); }
.toast--info    { border-left-color: var(--mx2-gold, #c9a86a); }
.toast__message {
  margin: 0;
  font-weight: 500;
}
.toast__close {
  position: absolute;
  top: 8px;
  right: 10px;
  background: none;
  border: 0;
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  line-height: 1;
  color: rgba(40, 28, 12, 0.45);
  cursor: pointer;
  border-radius: 6px;
  transition: background 120ms ease, color 120ms ease;
}
.toast__close:hover {
  color: var(--ink);
  background: rgba(40, 28, 12, 0.06);
}

/* ── Loading overlay (form submit feedback) ─────────────────────────────── */
.loading-overlay {
  position: fixed;
  inset: 0;
  background: rgba(20, 20, 20, 0.45);
  z-index: 10000;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: all;
}
.loading-overlay[hidden] { display: none; }
.loading-overlay__spinner {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 4px solid rgba(255, 255, 255, 0.25);
  border-top-color: var(--mx2-gold, #c9a86a);
  animation: loading-spin 800ms linear infinite;
}
@keyframes loading-spin {
  to { transform: rotate(360deg); }
}

/* ── Status pills ───────────────────────────────────────────────────────── */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 11px;
  font-family: var(--font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 500;
  border-radius: var(--radius-pill);
  background: var(--cream);
  color: var(--black);
}
.pill::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}
/* PR-4 — Map the existing .pill .pill-* variants onto the MaestroX
   chip palette (info / success / warning / danger / neutral) so the
   ~20 views still using legacy .pill markup pick up the new tinted-
   background look without per-file rewrites. Each variant inherits
   from the closest semantic chip role. */
.pill-warn           { background: var(--danger-bg);  color: var(--danger);  border: 1px solid var(--danger-border); }
.pill-brief-draft    { background: var(--neutral-bg); color: var(--neutral); border: 1px solid var(--neutral-border); }
.pill-brief-sub      { background: var(--info-bg);    color: var(--info);    border: 1px solid var(--info-border); }
.pill-plan-gen       { background: var(--warning-bg); color: var(--warning); border: 1px solid var(--warning-border); }
.pill-plan-waiting   { background: var(--warning-bg); color: var(--warning); border: 1px solid var(--warning-border); }
.pill-assets-wait    { background: var(--warning-bg); color: var(--warning); border: 1px solid var(--warning-border); }
.pill-campaign-build { background: var(--info-bg);    color: var(--info);    border: 1px solid var(--info-border); }
.pill-launched       { background: var(--success-bg); color: var(--success); border: 1px solid var(--success-border); }
.pill-ended          { background: var(--neutral-bg); color: var(--neutral); border: 1px solid var(--neutral-border); }
.pill-cancelled      { background: var(--danger-bg);  color: var(--danger);  border: 1px solid var(--danger-border); }
/* Strong-urgency pill — creative needed within the 5-business-day
   window. Solid red so it stands out from the gold/cream brand
   palette around it. */
.pill-asap {
  background: #b14b3a;
  color: #FFFFFF;
}
.pill-asap::before { background: #FFFFFF; opacity: 0.85; }

/* PO tactic-status pills (Mx2-managed lifecycle).
   - requested (was pending): neutral cream
   - submitted: soft blue (ready for Mx2 review)
   - rejected_resubmit: solid red (loud — needs client action)
   - approved: solid dark green (matches po-check circle)
   - campaign_build: light blue (work-in-progress signal)
   - campaign_launched: solid gold (live!) */
.pill-requested      { background: #EDE6D6; color: #6B5C42; }
.pill-submitted      { background: #DDE6F0; color: #2c4a6b; }
.pill-rejected       { background: #b14b3a; color: #FFFFFF; }
.pill-rejected::before { background: #FFFFFF; opacity: 0.85; }
.pill-approved       { background: #4a8c5e; color: #FFFFFF; }
.pill-approved::before { background: #FFFFFF; opacity: 0.85; }
.pill-build          { background: #D9E5EE; color: #2c4a6b; }
.pill-launched-gold  { background: var(--mx2-gold, #9f886c); color: #1A1A1A; }
.pill-launched-gold::before { background: #1A1A1A; opacity: 0.7; }

/* Soft "Due Mon 5" indicator next to the status pill — same shape
   family but with a calendar icon and muted gold-deep type so it
   reads as info, not an alert. The actual urgency cue is the
   ASAP-red variant above. */
.po-due-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 5px 11px;
  font-family: var(--font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 500;
  border-radius: var(--radius-pill);
  background: rgba(159, 136, 108, 0.14);
  color: var(--mx2-gold-deep, #876e51);
  white-space: nowrap;
}
.po-due-pill svg { flex-shrink: 0; }

/* ── Spacing utilities ──────────────────────────────────────────────────── */
.mt-sm { margin-top: var(--space-3); }
.mt-md { margin-top: var(--space-5); }
.mt-lg { margin-top: var(--space-7); }

/* ── Modal (centered card with backdrop) ────────────────────────────────── */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  z-index: 200;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 10vh 16px 16px;
  overflow-y: auto;
  animation: modal-fade-in 120ms ease-out;
}
.modal-overlay[hidden] { display: none; }

/* ── Avatar cropper modal ─────────────────────────────────────────────────── */
.avatar-crop-modal {
  position: fixed; inset: 0;
  background: rgba(0, 0, 0, 0.62);
  z-index: 250;
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
  animation: modal-fade-in 120ms ease-out;
}
.avatar-crop-card {
  width: min(420px, 100%);
  background: var(--bg-card, #FFFFFF);
  color: var(--ink, #1A1A1A);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 20px 22px 18px;
  box-shadow: 0 24px 48px rgba(0, 0, 0, 0.30);
  animation: modal-pop 140ms ease-out;
}
.avatar-crop-title { margin: 0 0 4px; font-size: 18px; }
.avatar-crop-hint  { margin: 0 0 14px; }
.avatar-crop-stage {
  position: relative;
  width: 320px; height: 320px;
  margin: 0 auto;
  background: #111;
  border-radius: 6px;
  overflow: hidden;
  touch-action: none; /* let pointer events handle pan, not the browser */
}
.avatar-crop-canvas { display: block; cursor: grab; }
.avatar-crop-canvas:active { cursor: grabbing; }
.avatar-crop-mask {
  position: absolute; inset: 0;
  pointer-events: none;
  /* Dark overlay everywhere except a circular cutout in the center */
  background:
    radial-gradient(circle at center, transparent 0 158px, rgba(0,0,0,0.55) 159px);
  border-radius: 6px;
}
.avatar-crop-mask::after {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  width: 316px; height: 316px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 2px solid var(--mx2-gold, #c9a86a);
  pointer-events: none;
}
.avatar-crop-controls {
  display: flex; align-items: center; gap: 10px;
  margin: 14px 0 16px;
}
.avatar-crop-zoom { flex: 1; accent-color: var(--mx2-gold, #c9a86a); }
.avatar-crop-zoom-icon { font-size: 14px; color: var(--ink-soft, #5E5E5E); width: 14px; text-align: center; }
.avatar-crop-actions {
  display: flex; gap: 8px; justify-content: flex-end;
}
@media (max-width: 380px) {
  /* Tight phones — keep the 320 crop area and just trim card padding */
  .avatar-crop-card { padding: 14px 12px; }
}
@keyframes modal-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.modal-card {
  position: relative;
  width: min(480px, 100%);
  background: var(--bg-card, #FFFFFF);
  color: var(--ink, #1A1A1A);
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: 24px 28px;
  box-shadow: 0 24px 48px rgba(0, 0, 0, 0.30);
  animation: modal-pop 140ms ease-out;
}
@keyframes modal-pop {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}
.modal-card__close {
  position: absolute;
  top: 8px;
  right: 12px;
  background: none;
  border: 0;
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  color: var(--ink-soft, #5E5E5E);
  padding: 4px 8px;
}
.modal-card__close:hover { color: var(--ink, #1A1A1A); }
body.modal-open { overflow: hidden; }

/* ── Mobile: modal becomes a bottom-sheet ──────────────────────────
   On small viewports the centered card is awkward (the keyboard
   covers it, the backdrop tap-target is fiddly). Convert to a
   full-width sheet that slides up from the bottom. Same .modal-card
   markup, different presentation. */
@media (max-width: 640px) {
  .modal-overlay {
    align-items: flex-end;
    padding: 0;
  }
  .modal-card {
    width: 100%;
    max-height: 92vh;
    border-radius: 16px 16px 0 0;
    padding: 18px 16px 28px;
    overflow-y: auto;
    animation: modal-sheet-up 200ms ease-out;
  }
  @keyframes modal-sheet-up {
    from { opacity: 0; transform: translateY(20px); }
    to   { opacity: 1; transform: translateY(0); }
  }
}

/* ── Asset toolbar — icon-only buttons with hover tooltip ─────────
   Replaces the older text/pill action buttons in the gallery-detail
   header area on both client and admin sides. Square 36-40px target
   with a centered icon. The native title attribute + aria-label
   handle the tooltip + screen-reader text. */
.asset-toolbar {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px;
  background: var(--bg-card, #fff);
  border: 1px solid var(--rule);
  border-radius: 10px;
}
.asset-toolbar-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 8px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--ink, #1a1410);
  cursor: pointer;
  transition: background 100ms ease, border-color 100ms ease, color 100ms ease;
  padding: 0;
  text-decoration: none;
}
.asset-toolbar-btn:hover {
  background: var(--cream, #f4efe6);
  border-color: var(--rule);
}
.asset-toolbar-btn:focus-visible {
  outline: 2px solid var(--gold, #c9a86a);
  outline-offset: 2px;
}
.asset-toolbar-btn[aria-disabled="true"],
.asset-toolbar-btn[disabled] {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}
.asset-toolbar-btn svg {
  display: block;
  width: 18px;
  height: 18px;
}
/* Gold accent treatment for the primary "create" action so the
   "+ New gallery" button reads as the primary affordance. */
.asset-toolbar-btn--primary {
  background: var(--gold, #c9a86a);
  color: #1a1410;
}
.asset-toolbar-btn--primary:hover {
  background: var(--gold-deep, #b08c4a);
  border-color: transparent;
}
/* Mx2 admin side renders on a darker page background — bump the
   toolbar shell contrast so it doesn't disappear into the bg. */
body.mx2-side .asset-toolbar {
  background: var(--bg-card, #fff);
}
@media (max-width: 540px) {
  .asset-toolbar-btn { width: 40px; height: 40px; } /* bigger tap target */
  /* Wrap the icon row instead of overflowing/clipping past the viewport
     edge (body has overflow-x:clip on mobile). Drew 2026-06-17. */
  .asset-toolbar { display: flex; flex-wrap: wrap; max-width: 100%; }
}

/* Cosimo Plan Assistant trigger — small portrait icon next to the
   Media Plan heading. The supplied portrait already has its own circular
   frame; the button is just a transparent shell that crops the image to
   fill its bounds and a soft hover halo. */
.cosimo-icon {
  display: inline-block;
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  border-radius: 50%;
  border: 0;
  background: transparent;
  padding: 0;
  cursor: pointer;
  overflow: hidden;
  vertical-align: middle;
  line-height: 0;
  transition: transform 120ms ease, box-shadow 120ms ease;
}
.cosimo-icon img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  border-radius: 50%;
}
.cosimo-icon:hover {
  transform: translateY(-1px);
  box-shadow: 0 0 0 3px rgba(232, 222, 201, 0.25);
}
.cosimo-icon:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--mx2-gold-light, #e8dec9);
}

/* Modal that holds the Cosimo panel — wider than the default .modal-card. */
.cosimo-modal__card {
  width: min(820px, 100%);
  padding: 28px 32px;
}

/* Cosimo modal toolbar (top-right): print + download + close. */
.cosimo-modal__toolbar {
  position: absolute;
  top: 10px;
  right: 12px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.cosimo-modal__toolbar .modal-card__close { position: static; }
.cosimo-modal__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 6px;
  border: 1px solid var(--rule);
  background: var(--bg-card, #FFFFFF);
  color: var(--ink-mid, #5E5E5E);
  cursor: pointer;
  padding: 0;
  transition: border-color 120ms ease, color 120ms ease, background 120ms ease;
}
.cosimo-modal__icon:hover {
  border-color: var(--mx2-gold);
  color: var(--mx2-gold-deep, #876e51);
  background: rgba(159, 136, 108, 0.08);
}
.cosimo-modal__icon[hidden] { display: none; }

/* Reassign-planner icon piggy-backs on .editable-name__pencil for circle
   size + color, but pumps up the glyph so the ⟳ reads at a similar visual
   weight to the ✎ (the pencil glyph is denser at the same font-size). */
.editable-name__pencil.reassign-icon {
  font-size: 22px;
  font-family: inherit;
  font-weight: 400;
}

/* Trash / cancel-campaign icon — same chip as pencil, but rust-tinted on
   hover to telegraph the destructive action. */
.editable-name__pencil.trash-icon { padding: 0; }
.editable-name__pencil.trash-icon svg { display: block; }
.editable-name__pencil.trash-icon:hover {
  border-color: var(--mx2-rust, #b14b3a);
  color: var(--mx2-rust, #b14b3a);
  background: rgba(177, 75, 58, 0.10);
}
body.mx2-side .editable-name__pencil.trash-icon:hover {
  border-color: var(--mx2-rust, #b14b3a);
  color: var(--mx2-rust, #b14b3a);
  background: rgba(177, 75, 58, 0.10);
}

/* ── Campaign stages progress bar ────────────────────────────────────────── */
.campaign-stages {
  display: flex;
  align-items: center;
  gap: 0;
  margin: 24px 0 0;
  padding: 4px 0;
  position: relative;
}
.campaign-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}
.campaign-stage__dot {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 2px solid var(--rule);
  background: var(--bg-card, #FFFFFF);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: transparent;
  transition: background 200ms ease, border-color 200ms ease, color 200ms ease, transform 200ms ease;
}
.campaign-stage__label {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink-soft, #5E5E5E);
  white-space: nowrap;
}
.campaign-stage__connector {
  flex: 1;
  height: 2px;
  background: var(--rule);
  margin: 0 4px 22px;       /* shift up to align with the dot row */
  min-width: 24px;
}

/* Done */
.campaign-stage--done .campaign-stage__dot {
  background: var(--mx2-gold);
  border-color: var(--mx2-gold);
  color: #FFFFFF;
}
.campaign-stage--done .campaign-stage__label { color: var(--ink, #1A1A1A); }
.campaign-stage__connector--done { background: var(--mx2-gold); }

/* Current */
.campaign-stage--current .campaign-stage__dot {
  background: var(--mx2-gold);
  border-color: var(--mx2-gold);
  /* Number/glyph inside the dot reads as white per Drew's 2026-05-21
     rule (text on dark/colored fills must be white). */
  color: #FFFFFF;
  box-shadow: 0 0 0 4px rgba(159, 136, 108, 0.18);
  transform: scale(1.1);
}
.campaign-stage--current .campaign-stage__label {
  color: var(--mx2-gold-deep, #876e51);
  font-weight: 700;
}

/* Future = default styles already applied (rule-grey dot, soft text) */

/* Cancelled override — entire bar reads as a struck-through state */
.campaign-stages--cancelled { opacity: 0.7; }
.campaign-stage--cancelled .campaign-stage__dot {
  background: var(--mx2-rust, #b14b3a);
  border-color: var(--mx2-rust, #b14b3a);
  color: #FFFFFF;
}
.campaign-stage--cancelled .campaign-stage__label { color: var(--mx2-rust, #b14b3a); }
.campaign-stage__connector--cancelled { background: var(--mx2-rust, #b14b3a); }
.campaign-stages__cancelled-label {
  position: absolute;
  top: 0;
  right: 0;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--mx2-rust, #b14b3a);
  font-weight: 700;
}

/* Mx2 admin chrome is now cream; the previous on-dark overrides
   (white 6%/20% rgba dots + connectors, gold-light current label)
   washed out completely. Re-tune to opaque stone borders + cream
   fill so the rail reads against the page chrome, and use
   gold-deep for the current label so it matches the active dot. */
body.mx2-side .campaign-stage__dot {
  background: #FFFFFF;
  border-color: var(--mx2-stone-200);
}
body.mx2-side .campaign-stage__label { color: var(--ink-soft, #5E5E5E); }
body.mx2-side .campaign-stage__connector { background: var(--mx2-stone-200); }
body.mx2-side .campaign-stage--done .campaign-stage__dot,
body.mx2-side .campaign-stage--current .campaign-stage__dot {
  background: var(--mx2-gold);
  border-color: var(--mx2-gold);
}
body.mx2-side .campaign-stage--done .campaign-stage__label { color: var(--ink, #1A1A1A); }
body.mx2-side .campaign-stage--current .campaign-stage__label { color: var(--mx2-gold-deep, #876e51); }
body.mx2-side .campaign-stage__connector--done { background: var(--mx2-gold); }

@media (max-width: 720px) {
  /* Stages bar — compact, tighter rhythm, all stages still visible.
     Margin pulls it up flush with the hero so the page top doesn't
     feel padded. */
  .campaign-stages { gap: 0; padding: 0; margin: 8px 0 14px; }
  .campaign-stage__label {
    font-size: 8px;
    letter-spacing: 0.04em;
    margin-top: 4px;
    max-width: 56px;
  }
  .campaign-stage__dot { width: 14px; height: 14px; }
  .campaign-stage__connector { margin: 0 1px 18px; min-width: 6px; height: 1px; }

  /* Campaign tabs: scroll affordance — fade on the right edge */
  .campaign-tabs {
    /* mask is already applied via mx2.css; add fade indicator */
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 22px), transparent 100%);
            mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 22px), transparent 100%);
  }
  .campaign-tab--active {
    /* Active tab gets a clearer underline indicator on mobile */
    box-shadow: inset 0 -2px 0 0 var(--mx2-gold);
  }
  .campaign-tab-panel {
    /* Tighter padding for thumb-comfortable read width */
    padding: 18px 14px !important;
    border-radius: 0 6px 6px 6px;
  }

  /* Hero with the campaign name + inline action icons. Tight line-height
     so a 3-word title doesn't blow out vertically. */
  .editable-name {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px;
  }
  .editable-name h1,
  .editable-name__display {
    font-size: 19px;
    line-height: 1.2;
    word-break: break-word;
    margin: 0;
  }
  .editable-name__display { padding: 0; }
  /* Action icons (pencil, reassign ⟳, trash) sit inline at the end of
     the title — they wrap below if there's no room. */
  .editable-name__pencil {
    width: 28px;
    height: 28px;
    flex: 0 0 28px;
  }

  /* Brief summary card: switch to single-column with hairline rules */
  .brief-section { padding: 14px 14px; }
  .brief-section h3 { font-size: 14px; }
}

/* Tabbed sections on the campaign show page — visual: file-folder tabs
   anchored to the top of a beige-paper card. Active tab and panel share
   the same paper background; inactive tabs are darker chips. */
.campaign-tabs {
  display: flex;
  gap: 4px;
  margin: 32px 0 0;
  flex-wrap: wrap;
  position: relative;
  z-index: 1;
}
.campaign-tab {
  appearance: none;
  background: rgba(159, 136, 108, 0.20);
  border: 1px solid var(--rule);
  border-bottom: 0;
  border-radius: 8px 8px 0 0;
  padding: 12px 22px;
  font-family: inherit;
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink-soft, #5E5E5E);
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.campaign-tab:hover {
  background: rgba(159, 136, 108, 0.32);
  color: var(--ink, #1A1A1A);
}
.campaign-tab--active {
  background: var(--mx2-paper-2, #f3eee2);
  color: var(--mx2-gold-deep, #876e51);
  /* Pull down 1px so the active tab visually merges with the panel below */
  margin-bottom: -1px;
  position: relative;
  z-index: 2;
}
.campaign-tab:focus-visible { outline: 2px solid var(--mx2-gold); outline-offset: -2px; }

.campaign-tab-panel[hidden] { display: none; }
.campaign-tab-panel {
  position: relative;
  background: var(--mx2-paper-2, #f3eee2);
  color: var(--ink, #1A1A1A);
  border: 1px solid var(--rule);
  border-radius: 0 8px 8px 8px;
  padding: 28px 32px;
  margin-bottom: 32px;
}
.campaign-tab-panel__action {
  position: absolute;
  top: 20px;
  right: 24px;
}

/* Brief-summary card was its own beige paper — now that the tab panel IS
   the paper, neutralize its frame so the section dividers between BASICS,
   OBJECTIVES, etc. still render as horizontal rules. */
.campaign-tab-panel .brief-summary-grid,
body.mx2-side .campaign-tab-panel .brief-summary-grid {
  background: transparent;
  border: 0;
  border-radius: 0;
}
.campaign-tab-panel .brief-section { padding: 18px 0; }
.campaign-tab-panel .brief-section:first-child { padding-top: 0; }
.campaign-tab-panel .brief-section:last-child { padding-bottom: 0; border-bottom: 0; }

/* Banners + form fields render fine on beige; just kill any nested .mx2-section
   margins that get flattened into the panel. */
.campaign-tab-panel .mx2-section { margin: 0; }
.campaign-tab-panel .mx2-card-form {
  background: rgba(255, 255, 255, 0.55);
  border-color: var(--rule);
}

/* Mx2 admin runs on the same cream chrome the client side uses, so
   the inactive-tab chip + hover colors that used to be tuned for
   the old dark page (white 6% bg, white text on hover) read as
   invisible on cream. Re-tune to a gold-tinted chip + dark ink
   hover that matches the base campaign-tab look. */
body.mx2-side .campaign-tab {
  background: rgba(159, 136, 108, 0.16);
  border-color: var(--rule);
  color: var(--ink-soft, #5E5E5E);
}
body.mx2-side .campaign-tab:hover {
  background: rgba(159, 136, 108, 0.28);
  color: var(--ink, #1A1A1A);
}
body.mx2-side .campaign-tab--active {
  background: var(--mx2-paper-2, #f3eee2);
  color: var(--mx2-gold-deep, #876e51);
  border-color: var(--rule);
}
body.mx2-side .campaign-tab-panel {
  background: var(--mx2-paper-2, #f3eee2);
  color: var(--ink, #1A1A1A);
  border-color: rgba(0, 0, 0, 0.10);
}
body.mx2-side .campaign-tab-panel .section-kicker { color: var(--mx2-gold-deep, #876e51); }
body.mx2-side .campaign-tab-panel .dim { color: var(--ink-soft, #5E5E5E); }
body.mx2-side .campaign-tab-panel label { color: var(--ink, #1A1A1A); }
body.mx2-side .campaign-tab-panel input[type='text'],
body.mx2-side .campaign-tab-panel input[type='email'],
body.mx2-side .campaign-tab-panel input[type='file'],
body.mx2-side .campaign-tab-panel select,
body.mx2-side .campaign-tab-panel textarea {
  background: #FFFFFF;
  color: var(--ink, #1A1A1A);
  border-color: var(--rule);
}

/* ── Brief summary (read-only) ──────────────────────────────────────────── */
.brief-summary-grid {
  display: flex;
  flex-direction: column;
  gap: 0;
  margin-top: 16px;
  border: 1px solid var(--rule);
  border-radius: 6px;
  background: var(--bg-card, #FFFFFF);
  overflow: hidden;
}
.brief-section {
  padding: 22px 28px;
  border-bottom: 1px solid var(--rule);
}
.brief-section:last-child { border-bottom: none; }
.brief-section h3 {
  margin: 0;
  color: var(--mx2-gold-deep, #876e51);
  font-size: 13px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 700;
}
.brief-section-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  padding-bottom: 8px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--rule-lt, var(--rule));
}
.brief-section:not(:has(.brief-section-head)) h3 {
  /* sections without the head wrapper still get the rule */
  padding-bottom: 8px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--rule-lt, var(--rule));
}
.brief-submitted {
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--ink-soft, #5E5E5E);
  font-weight: 500;
}
.brief-section-body {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: 10px 24px;
  align-items: baseline;
}
.bsr {
  display: contents;
}
.bsr label {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink-soft, #5E5E5E);
  margin: 0;
  padding-top: 2px;
}
.bsr .val {
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink, #1A1A1A);
  white-space: pre-wrap;
}
@media (max-width: 720px) {
  .brief-section-body { grid-template-columns: 1fr; gap: 4px 0; }
  .bsr label { padding-top: 8px; }
}

/* Dark Mx2 layout — render the brief on a brand-paper beige with dark text
   for readability instead of trying to compete with the dark page bg. */
body.mx2-side .brief-summary-grid {
  background: var(--mx2-paper-2, #f3eee2);
  border-color: rgba(0, 0, 0, 0.10);
  color: var(--ink, #1A1A1A);
}
body.mx2-side .brief-section { border-bottom-color: rgba(0, 0, 0, 0.10); }
body.mx2-side .brief-section h3 { color: var(--mx2-gold-deep, #876e51); }
body.mx2-side .brief-section-head { border-bottom-color: rgba(0, 0, 0, 0.10); }
body.mx2-side .brief-submitted { color: var(--ink-soft, #5E5E5E); }
body.mx2-side .bsr label { color: var(--ink-soft, #5E5E5E); opacity: 1; }
body.mx2-side .bsr .val { color: var(--ink, #1A1A1A); }

/* ── Rendered plan document (client-side review) ────────────────────────── */
.plan-document {
  margin: 16px 0;
  padding: 24px 28px;
  border: 1px solid var(--rule);
  border-radius: 6px;
  background: #FBF9F4;
  /* Force dark ink so the text reads cleanly when the plan is rendered
     inside the dark Mx2 admin layout (which sets a light body color) */
  color: var(--ink, #1A1A1A);
  line-height: 1.6;
}
.plan-document h1,
.plan-document h2,
.plan-document h3,
.plan-document h4,
.plan-document p,
.plan-document li,
.plan-document strong,
.plan-document em,
.plan-document th,
.plan-document td {
  color: var(--ink, #1A1A1A);
}
.plan-document h1 { font-size: 22px; margin: 0 0 12px; }
.plan-document h2 { font-size: 18px; margin: 22px 0 8px; border-bottom: 1px solid var(--rule); padding-bottom: 4px; }
.plan-document h3 { font-size: 15px; margin: 18px 0 6px; }
.plan-document p { font-size: 14px; margin: 8px 0; }
.plan-document ul, .plan-document ol { margin: 8px 0 16px 24px; }
.plan-document li { font-size: 14px; line-height: 1.55; margin: 3px 0; }
.plan-document table {
  width: 100%;
  border-collapse: collapse;
  margin: 14px 0;
  font-size: 13px;
}
.plan-document th, .plan-document td {
  border: 1px solid var(--rule);
  padding: 8px 12px;
  text-align: left;
  vertical-align: top;
}
.plan-document th { background: rgba(159, 136, 108, 0.08); font-weight: 600; }
.plan-document strong { font-weight: 600; }
.plan-document code { font-size: 0.92em; background: rgba(0, 0, 0, 0.05); padding: 1px 4px; border-radius: 3px; }

/* ── AI Plan Assistant panel ────────────────────────────────────────────── */
.ai-plan-panel {
  border: 1px solid var(--rule);
  background: var(--bg-card, #FFFFFF);
  border-radius: 6px;
  padding: 16px 20px;
  margin-bottom: 16px;
}
.ai-plan-panel__head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  flex-wrap: wrap;
}
.ai-plan-panel__body { margin-top: 16px; }

/* Optional steer textarea — collapsed by default below the head row */
.ai-plan-panel__steer {
  margin-top: 12px;
}
.ai-plan-panel__steer-toggle {
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--mx2-gold-deep, #876e51);
  list-style: none;
  padding: 4px 0;
  user-select: none;
}
.ai-plan-panel__steer-toggle::-webkit-details-marker { display: none; }
.ai-plan-panel__steer-toggle::before {
  content: '+ ';
  font-weight: 700;
  color: var(--mx2-gold, #c9a86a);
}
.ai-plan-panel__steer details[open] .ai-plan-panel__steer-toggle::before,
.ai-plan-panel__steer details[open] > .ai-plan-panel__steer-toggle::before {
  content: '− ';
}
.ai-plan-panel__steer textarea {
  width: 100%;
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.45;
  padding: 10px 12px;
  border: 1px solid var(--rule);
  border-radius: 4px;
  resize: vertical;
  min-height: 70px;
  background: var(--bg-card, #FFFFFF);
}

/* ── Cosimo "outside the standard library" planner-only suggestions ─────
   Rendered below the main plan preview, only when Cosimo flagged 1-2
   ideas. Gold-bordered to distinguish from in-library tactic content. */
.cosimo-outside {
  margin-top: 18px;
  padding: 14px 16px 12px;
  border: 1px solid var(--mx2-gold, #c9a86a);
  border-radius: 6px;
  background: rgba(159, 136, 108, 0.06);
}
.cosimo-outside .section-kicker {
  color: var(--mx2-gold-deep, #876e51);
}
.cosimo-outside__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 10px;
}
.cosimo-outside__item {
  background: var(--bg-card, #FFFFFF);
  border: 1px solid var(--rule);
  border-radius: 4px;
  padding: 12px 14px;
}
.cosimo-outside__head strong {
  font-family: var(--font-serif);
  font-size: 16px;
  color: var(--ink);
}
.cosimo-outside__rationale {
  margin: 6px 0 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-mid);
}
.cosimo-outside__gap {
  margin: 6px 0 0;
}
.cosimo-outside__actions {
  margin-top: 8px;
}
.ai-plan-panel__preview,
.ai-plan-panel__editor {
  border: 1px solid var(--rule);
  border-radius: 4px;
  padding: 16px 18px;
  max-height: 480px;
  overflow-y: auto;
  background: var(--bg-card, #FFFFFF);
}
.ai-plan-panel__editor {
  width: 100%;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 13px;
  line-height: 1.5;
  resize: vertical;
}
.ai-plan-panel__preview h2 { font-size: 18px; margin: 18px 0 8px; }
.ai-plan-panel__preview h3 { font-size: 15px; margin: 14px 0 6px; }
.ai-plan-panel__preview p  { font-size: 14px; line-height: 1.55; margin: 6px 0; }
.ai-plan-panel__preview ul { margin: 6px 0 12px 20px; }
.ai-plan-panel__preview li { font-size: 14px; line-height: 1.5; }
.ai-plan-panel__preview table.ai-plan-table {
  width: 100%;
  border-collapse: collapse;
  margin: 12px 0;
  font-size: 13px;
}
.ai-plan-panel__preview .ai-plan-table th,
.ai-plan-panel__preview .ai-plan-table td {
  border: 1px solid var(--rule);
  padding: 6px 10px;
  text-align: left;
  vertical-align: top;
}
.ai-plan-panel__preview .ai-plan-table th { background: rgba(159, 136, 108, 0.08); }
.ai-plan-panel__actions {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 12px;
}
.ai-plan-loading {
  font-style: italic;
  color: var(--ink-soft, #5E5E5E);
  padding: 12px 0;
}

/* Previous versions list inside the Cosimo modal. Mirrors the social
   composer's snapshot panel visually (small disclosure + bordered tiles)
   but adapted to the marketing modal's paper-card surface. */
.ai-plan-history {
  margin-top: 12px;
  border-top: 1px solid var(--mx2-stone-300, #cdbfa5);
  padding-top: 8px;
}
.ai-plan-history__toggle {
  cursor: pointer;
  color: var(--mx2-text-mid);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 2px 0;
  list-style: none;
}
.ai-plan-history__toggle::-webkit-details-marker { display: none; }
.ai-plan-history__toggle::before {
  content: '▸ ';
  display: inline-block;
}
.ai-plan-history[open] .ai-plan-history__toggle::before { content: '▾ '; }
.ai-plan-history__list {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.ai-plan-history__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  background: rgba(159, 136, 108, 0.06);
  border: 1px solid rgba(159, 136, 108, 0.25);
  border-radius: 4px;
  padding: 6px 10px;
}
.ai-plan-history__meta {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ai-plan-history__actions {
  display: inline-flex;
  gap: 6px;
  flex-shrink: 0;
}

/* Plan snapshot preview modal — paper-card surface, matches the rest
   of the marketing-side UI rather than the dark composer chrome. */
.ai-plan-preview-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 110;
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms ease;
}
.ai-plan-preview-backdrop.is-visible {
  opacity: 1;
  pointer-events: auto;
}
.ai-plan-preview {
  background: var(--mx2-paper, #f5efe3);
  border-radius: 10px;
  width: min(760px, 94vw);
  max-height: 84vh;
  display: flex;
  flex-direction: column;
  color: var(--mx2-stone-800, #1a1a1a);
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.35);
}
.ai-plan-preview__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid rgba(159, 136, 108, 0.25);
}
.ai-plan-preview__title {
  margin: 0;
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--mx2-text-mid, #5e5042);
}
.ai-plan-preview__close {
  background: transparent;
  border: none;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
  color: var(--mx2-text-mid, #5e5042);
}
.ai-plan-preview__body {
  padding: 18px;
  overflow-y: auto;
  flex: 1;
  font-family: Georgia, serif;
  line-height: 1.55;
}
.ai-plan-preview__body h1,
.ai-plan-preview__body h2,
.ai-plan-preview__body h3 { font-family: Georgia, serif; }
.ai-plan-preview__body h2 {
  border-bottom: 1px solid rgba(159, 136, 108, 0.25);
  padding-bottom: 4px;
  margin-top: 18px;
}
.ai-plan-preview__foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 18px;
  border-top: 1px solid rgba(159, 136, 108, 0.25);
}
.ai-plan-error {
  color: var(--mx2-rust, #b14b3a);
  padding: 12px 0;
}
.ai-plan-status--ok    { color: var(--status-launched, #5e8a6a); }
.ai-plan-status--error { color: var(--mx2-rust, #b14b3a); }

/* Dark Mx2 admin layout: lighten the panel surfaces */
body.mx2-side .ai-plan-panel {
  background: rgba(255, 255, 255, 0.04);
  border-color: rgba(255, 255, 255, 0.14);
}
body.mx2-side .ai-plan-panel__preview,
body.mx2-side .ai-plan-panel__editor {
  background: rgba(0, 0, 0, 0.18);
  border-color: rgba(255, 255, 255, 0.14);
  color: var(--mx2-text-hi, #FFFFFF);
}
body.mx2-side .ai-plan-panel__preview .ai-plan-table th { background: rgba(159, 136, 108, 0.18); }

/* When the panel is inside a modal (light surface), revert to light
   styling regardless of body.mx2-side — the modal card is a different
   context and the dark-mode rules above wash out against it. */
.modal-card .ai-plan-panel,
body.mx2-side .modal-card .ai-plan-panel {
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
}
.modal-card .ai-plan-panel__preview,
.modal-card .ai-plan-panel__editor,
body.mx2-side .modal-card .ai-plan-panel__preview,
body.mx2-side .modal-card .ai-plan-panel__editor {
  background: var(--mx2-paper, #fbf9f4);
  color: var(--ink, #1A1A1A);
  border: 1px solid var(--rule);
}
.modal-card .ai-plan-panel__preview h2,
body.mx2-side .modal-card .ai-plan-panel__preview h2 { color: var(--ink, #1A1A1A); border-bottom: 1px solid var(--rule); padding-bottom: 4px; }
.modal-card .ai-plan-panel__preview h3,
body.mx2-side .modal-card .ai-plan-panel__preview h3 { color: var(--mx2-gold-deep, #876e51); }
.modal-card .ai-plan-panel__preview p,
.modal-card .ai-plan-panel__preview li,
body.mx2-side .modal-card .ai-plan-panel__preview p,
body.mx2-side .modal-card .ai-plan-panel__preview li { color: var(--ink, #1A1A1A); }
.modal-card .ai-plan-panel__preview .ai-plan-table th,
body.mx2-side .modal-card .ai-plan-panel__preview .ai-plan-table th {
  background: rgba(159, 136, 108, 0.10);
  color: var(--ink, #1A1A1A);
}
.modal-card .ai-plan-panel__head h3,
body.mx2-side .modal-card .ai-plan-panel__head h3 { color: var(--ink, #1A1A1A); }
.modal-card .ai-plan-panel__head p,
body.mx2-side .modal-card .ai-plan-panel__head p { color: var(--ink-mid, #5E5E5E); }

/* ── Report bars ────────────────────────────────────────────────────────── */
.report-bar {
  width: 100%;
  height: 8px;
  background: rgba(159, 136, 108, 0.12);
  border-radius: 4px;
  overflow: hidden;
}
.report-bar__fill {
  height: 100%;
  background: var(--mx2-gold);
  transition: width 200ms ease;
}
body.mx2-side .report-bar { background: rgba(255, 255, 255, 0.08); }

/* ── List filter bar (search + dropdowns) ───────────────────────────────── */
.list-filter {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: nowrap;
  margin-bottom: 12px;
}
.list-filter > .btn,
.list-filter > a.btn,
.list-filter > button,
.list-filter > a {
  flex-shrink: 0;
}
.list-filter__search,
.list-filter__select {
  font-family: inherit;
  font-size: 13px;
  padding: 8px 10px;
  border: 1px solid var(--rule);
  border-radius: 4px;
  background: var(--bg-card, #FFFFFF);
  color: var(--ink, #1A1A1A);
}
.list-filter__search {
  flex: 0 1 320px;
  min-width: 0;
}
.list-filter__select {
  flex: 0 0 auto;
  width: 200px;
  max-width: 200px;
}
.list-filter__search::placeholder { color: var(--ink-soft, #5E5E5E); }
.list-filter__search:focus,
.list-filter__select:focus {
  outline: none;
  border-color: var(--mx2-gold);
  box-shadow: 0 0 0 3px rgba(159, 136, 108, 0.18);
}
/* Mx2 dark layout: lighten the inputs so they read on the dark bg. */
body.mx2-side .list-filter__search,
body.mx2-side .list-filter__select {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.18);
  color: var(--mx2-text-hi, #FFFFFF);
}
body.mx2-side .list-filter__search::placeholder { color: var(--mx2-text, #C8C0AE); }

/* ── Inline editable name (h1 ↔ input) ──────────────────────────────────── */
.editable-name { position: relative; }
.editable-name__display {
  cursor: pointer;
  margin: 0;
}
.editable-name__text {
  border-bottom: 1px dashed transparent;
  transition: border-color 120ms ease;
}
.editable-name__display:hover .editable-name__text { border-bottom-color: var(--rule); }
.editable-name__pencil {
  margin-left: 10px;
  vertical-align: middle;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 1px solid var(--rule);
  background: transparent;
  color: var(--ink-soft, #5E5E5E);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: border-color 120ms ease, color 120ms ease;
}
.editable-name__pencil:hover {
  border-color: var(--mx2-gold);
  color: var(--mx2-gold-deep, #876e51);
}
/* Mx2 admin chrome is now cream — the previous on-dark gold-light /
   white-hover colors disappeared on cream. Re-tune to the same warm
   ink + gold-on-hover the base styles already use. */
body.mx2-side .editable-name__pencil {
  color: var(--ink-soft, #5E5E5E);
  border-color: var(--rule);
}
body.mx2-side .editable-name__pencil:hover {
  color: var(--mx2-gold-deep, #876e51);
  border-color: var(--mx2-gold);
  background: rgba(159, 136, 108, 0.10);
}
body.mx2-side .editable-name__status { color: var(--mx2-text, #C8C0AE); }
body.mx2-side .editable-name__display:hover .editable-name__text {
  border-bottom-color: var(--rule);
}
.editable-name__input {
  /* Inherits the h1 font scale from its container so the swap is visually
     stable. Caller pages have h1 sized via .page-hero / .mx2-page-hero. */
  width: 100%;
  max-width: 720px;
  font: inherit;
  font-size: 1em;
  font-weight: 600;
  padding: 4px 8px;
  border: 1px solid var(--mx2-gold);
  border-radius: 4px;
  background: var(--bg-card, #FFFFFF);
  color: inherit;
  box-shadow: 0 0 0 3px rgba(159, 136, 108, 0.18);
  outline: none;
}
.editable-name__status {
  display: inline-block;
  margin-left: 12px;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-soft, #5E5E5E);
  vertical-align: middle;
}
.editable-name__status--ok    { color: var(--status-launched, #5e8a6a); }
.editable-name__status--error { color: var(--mx2-rust, #b14b3a); }

/* ── Brief-help: per-field "?" icon + popover ───────────────────────────── */
.field--has-help { position: relative; }
.field-help-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  margin-left: 8px;
  border-radius: 50%;
  border: 1px solid var(--rule);
  background: var(--bg-card, #FFFFFF);
  color: var(--mx2-text-mid, #5E5E5E);
  font-family: Georgia, serif;
  font-size: 12px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  vertical-align: middle;
  transition: border-color 150ms ease, color 150ms ease;
}
.field-help-icon:hover,
.field-help-icon:focus-visible {
  border-color: var(--mx2-gold);
  color: var(--mx2-gold-deep, #876e51);
  outline: none;
}
.field-help-popover {
  position: absolute;
  z-index: 50;
  top: calc(100% + 4px);
  right: 0;
  width: min(360px, 90vw);
  background: var(--bg-card, #FFFFFF);
  border: 1px solid var(--rule);
  border-radius: 6px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  padding: 12px 14px;
  animation: field-help-pop-in 120ms ease-out;
}
@keyframes field-help-pop-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.field-help-popover__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft, #5E5E5E);
  margin-bottom: 8px;
}
.field-help-popover__close {
  background: none;
  border: 0;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  color: var(--ink-soft, #5E5E5E);
  padding: 0 4px;
}
.field-help-popover__body {
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink, #1A1A1A);
  white-space: pre-wrap;
}
.field-help-popover__body--error { color: var(--mx2-rust, #b14b3a); }
.field-help-popover__body--loading {
  color: var(--ink-mid, #5E5E5E);
  font-style: italic;
}
.field-help-dots {
  display: inline-flex;
  gap: 3px;
  margin-right: 4px;
  vertical-align: middle;
}
.field-help-dots span {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--mx2-gold);
  animation: field-help-dot-bounce 1.0s infinite ease-in-out;
}
.field-help-dots span:nth-child(2) { animation-delay: 0.15s; }
.field-help-dots span:nth-child(3) { animation-delay: 0.30s; }
@keyframes field-help-dot-bounce {
  0%, 80%, 100% { opacity: 0.3; transform: translateY(0); }
  40%           { opacity: 1.0; transform: translateY(-3px); }
}
.brief-help-panel {
  margin-top: 12px;
  border: 1px solid var(--rule);
  border-radius: 6px;
  background: var(--bg-card, #FFFFFF);
  padding: 14px 16px;
  text-align: left;
  max-width: 720px;
}
.brief-help-panel-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--mx2-text-mid, #5E5E5E);
}
.brief-help-close {
  background: none;
  border: 0;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  color: var(--mx2-text-mid, #5E5E5E);
  padding: 0 4px;
}
.brief-help-quick {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
.brief-help-quick .btn-ghost-dark { font-size: 12px; padding: 6px 10px; }
.brief-help-thread {
  max-height: 360px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 12px;
}
.brief-help-bubble {
  padding: 10px 12px;
  border-radius: 8px;
  font-size: 14px;
  line-height: 1.5;
  white-space: pre-wrap;
}
.brief-help-bubble--user {
  background: var(--mx2-stone-100, #F2EFE3);
  align-self: flex-end;
  max-width: 80%;
}
.brief-help-bubble--assistant {
  background: rgba(159, 136, 108, 0.10);
  border-left: 3px solid var(--mx2-gold);
  align-self: flex-start;
  max-width: 90%;
}
.brief-help-bubble--pending { font-style: italic; opacity: 0.7; }
.brief-help-bubble--error {
  background: rgba(177, 75, 58, 0.08);
  border-left: 3px solid var(--mx2-rust, #b14b3a);
  color: var(--mx2-rust, #b14b3a);
}
.brief-help-form {
  display: flex;
  gap: 8px;
  align-items: flex-end;
}
.brief-help-form textarea {
  flex: 1;
  resize: vertical;
  min-height: 50px;
  font-family: inherit;
  font-size: 14px;
  padding: 8px 10px;
}

/* ── Pending-actions badge (nav + chip) ─────────────────────────────────── */
.nav-pending-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  margin-left: 8px;
  border-radius: 999px;
  background: var(--mx2-gold);
  color: #000;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0;
  line-height: 1;
}
.nav-pending { position: relative; }
.nav-pending-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 14px;
  border-radius: 999px;
  border: 1px solid var(--rule);
  background: var(--bg-card, #FFFFFF);
  text-decoration: none;
  color: var(--ink-soft, #5E5E5E);
  font-family: inherit;
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
}
.nav-pending-chip:hover,
.nav-pending-chip[aria-expanded='true'] {
  border-color: var(--mx2-gold);
  color: var(--ink, #1A1A1A);
}
.nav-pending-chip .nav-pending-badge { margin-left: 0; }

.nav-pending-panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 100;
  width: min(380px, 92vw);
  background: var(--bg-card, #FFFFFF);
  border: 1px solid var(--rule);
  border-radius: 6px;
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.14);
  padding: 14px 16px;
  animation: nav-pending-pop 120ms ease-out;
}
@keyframes nav-pending-pop {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.nav-pending-panel__head {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-soft, #5E5E5E);
  margin-bottom: 10px;
}
.nav-pending-group { margin-bottom: 14px; }
.nav-pending-group:last-child { margin-bottom: 0; }
.nav-pending-group__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  margin-bottom: 6px;
}
.nav-pending-group__label {
  font-size: 12px;
  font-weight: 600;
  color: var(--ink, #1A1A1A);
  text-decoration: none;
  text-transform: none;
  letter-spacing: 0;
}
.nav-pending-group__label:hover { color: var(--mx2-gold-deep, #876e51); }
.nav-pending-badge--ghost {
  background: transparent;
  border: 1px solid var(--mx2-gold);
  color: var(--mx2-gold-deep, #876e51);
}
.nav-pending-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav-pending-list li + li { margin-top: 2px; }
.nav-pending-item {
  display: block;
  padding: 8px 10px;
  border-radius: 4px;
  text-decoration: none;
  color: var(--ink, #1A1A1A);
  transition: background 100ms ease;
}
.nav-pending-item:hover { background: rgba(159, 136, 108, 0.08); }
.nav-pending-item__title {
  display: block;
  font-size: 14px;
  font-weight: 500;
  margin-bottom: 1px;
}
.nav-pending-item__sub {
  display: block;
  font-size: 11px;
  color: var(--ink-soft, #5E5E5E);
  letter-spacing: 0.04em;
}
.nav-pending-more {
  display: block;
  padding: 6px 10px;
  font-size: 12px;
  color: var(--mx2-gold-deep, #876e51);
  text-decoration: none;
}
.nav-pending-more:hover { text-decoration: underline; }

/* ── Upload progress bar ────────────────────────────────────────────────── */
.upload-progress {
  display: none;
  margin-top: 12px;
  width: 100%;
}
.upload-progress--active { display: block; }
.upload-progress__track {
  width: 100%;
  height: 8px;
  background: rgba(0, 0, 0, 0.08);
  border-radius: 4px;
  overflow: hidden;
}
.upload-progress__fill {
  height: 100%;
  width: 0%;
  background: var(--mx2-gold);
  transition: width 200ms ease-out;
}
.upload-progress__text {
  margin-top: 6px;
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--mx2-text-mid, #5E5E5E);
}
.upload-progress--error .upload-progress__fill { background: var(--mx2-rust, #b14b3a); }
.upload-progress--error .upload-progress__text { color: var(--mx2-rust, #b14b3a); }
/* Dark backgrounds (Mx2 admin layout) want a lighter track + label. */
.mx2-side .upload-progress__track { background: rgba(255, 255, 255, 0.08); }
.mx2-side .upload-progress__text  { color: var(--mx2-text-mid, #C8C0AE); }

/* ── .desktop-only / .mobile-only visibility helpers ──────────────────────
   Use these to gate features that only make sense on one form factor.
   Defaults: desktop-only renders, mobile-only hides. The 720px breakpoint
   flips both. */
.mobile-only { display: none; }
@media (max-width: 720px) {
  .desktop-only { display: none !important; }
  .mobile-only  { display: block; }
}

/* The "use a desktop for this" upsell card. Friendly, gold-bordered.
   Used inside campaign-show panels so it inherits paper-beige bg. */
.desktop-only-card {
  background: rgba(159, 136, 108, 0.08);
  border: 1px solid var(--mx2-gold);
  border-radius: 6px;
  padding: 14px 16px;
  margin: 12px 0;
}
.desktop-only-card strong {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--mx2-gold-deep, #876e51);
  margin-bottom: 6px;
}
.desktop-only-card p {
  margin: 0;
  font-size: 14px;
  line-height: 1.45;
  color: var(--ink, #1A1A1A);
}

/* ── MOBILE FOUNDATION ────────────────────────────────────────────────────
   Universal touch / no-zoom rules. Scoped to coarse pointers so DESKTOP
   STYLING IS UNCHANGED — these only kick in on phones, tablets, and any
   touch laptop a user might pick up.
   ──────────────────────────────────────────────────────────────────────── */
@media (pointer: coarse) {
  /* iOS auto-zooms when an input has font-size < 16px. Bumping every
     interactive control to 16px on coarse pointers eliminates the zoom
     without affecting desktop typography. */
  input[type='text'],
  input[type='email'],
  input[type='password'],
  input[type='number'],
  input[type='tel'],
  input[type='url'],
  input[type='date'],
  input[type='datetime-local'],
  input[type='month'],
  input[type='week'],
  input[type='time'],
  input[type='search'],
  select,
  textarea {
    font-size: 16px;
  }

  /* Apple HIG floor (44pt). Anything tappable hits at least 44px tall. */
  button,
  .btn, .btn-tertiary, .btn-secondary, .btn-primary,
  .btn-ghost, .btn-ghost-dark,
  .mx2-nav-link, .campaign-tab,
  .editable-name__pencil, .cosimo-icon,
  .nav-pending-chip, .mx2-table-link,
  a.auth-link {
    min-height: 44px;
  }
  /* Buttons that are .btn-sm intentionally stay smaller — keep them
     tappable but let them be 36 instead of 44 (badges, inline actions). */
  .btn-sm { min-height: 36px; }
  /* Same for the small icon buttons inside an h1 (pencil / reassign /
     trash). They live in a tight horizontal cluster — a 44px floor would
     break the cluster. */
  .editable-name__pencil { min-height: 28px; min-width: 28px; }
}

/* ── Plan document on small screens — markdown render must not push the
   page wider than the viewport. Constrain width, let inner tables and
   code blocks scroll inside themselves. ──────────────────────────────── */
@media (max-width: 720px) {
  .plan-document {
    max-width: 100%;
    overflow-wrap: break-word;
    word-break: break-word;
    padding: 14px 14px;
  }
  .plan-document table {
    display: block;
    max-width: 100%;
    overflow-x: auto;
    white-space: nowrap;
  }
  .plan-document table th,
  .plan-document table td { white-space: normal; }
  .plan-document pre,
  .plan-document code {
    max-width: 100%;
    overflow-x: auto;
    white-space: pre-wrap;
    word-break: break-word;
  }
  /* Banners on plan-view page (revision/rejection notes) */
  .banner { word-break: break-word; }
}

/* ── Green checkmark badge for completed PO tactics + slots ─────────────
   Used by both the client PO cards and the Mx2 admin PO summary, so it
   lives in base.css (loaded by both layouts). */
.po-check {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 999px;
  background: var(--status-launched, #4a8c5e);
  color: #FFFFFF;
  flex-shrink: 0;
}
.po-check--inline {
  width: 16px;
  height: 16px;
  margin-right: 4px;
  vertical-align: -3px;
}
/* `display: inline-flex` above defeats the bare `hidden` attribute, so
   force it on for the toggle-via-JS slot check. */
.po-check[hidden] { display: none; }

/* ── Plan tactics list (client plan review + Mx2 plan preview) ──────────── */
.plan-tactics {
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid var(--rule);
}
.plan-tactics-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 10px;
}
.plan-tactic {
  background: rgba(159, 136, 108, 0.05);
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 10px 14px;
}
.plan-tactic-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.plan-tactic-head .cell-with-logo {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.plan-tactic-meta {
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.10em;
  white-space: nowrap;
}
.plan-tactic-rationale {
  margin: 6px 0 0;
  font-size: 13px;
  line-height: 1.45;
}


/* ── Product hub tiles (client + Mx2) ───────────────────────────────────── */
.hub {
  max-width: 1100px;
  margin: 0 auto;
  padding: clamp(24px, 6vw, 64px) 20px;
}
.hub-head {
  text-align: center;
  margin-bottom: clamp(24px, 5vw, 48px);
}
.hub-head h1 {
  font-size: clamp(32px, 5vw, 48px);
  margin: 0 0 8px;
  letter-spacing: -0.01em;
}
.hub-head p {
  margin: 0;
  font-size: 16px;
  opacity: 0.7;
}
.hub-grid {
  /* Balanced layouts — no 3+1 orphan rows. Tiles stretch to fill the
     row at their tile count, aligned to the left edge so the section
     anchors to the same gutter as the KPI strip and recent-activity
     card above and below. Achieved via :has() child-count detection
     (Safari 15.4+, Chrome 105+, Firefox 121+ — all our supported
     browsers). */
  display: grid;
  gap: 20px;
  justify-content: start;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}

/* Carousel variant — one row, max 4 tiles visible, additional tiles
   scrolled in via the arrows. Drives the Mx2 admin /mx2 hub which
   now has 6 tiles (Marketing, Asset Library, Social Media,
   Discussions, Content Planner, Event Shoots). Each tile = 25% of
   visible width minus gap, so 4 always fit; the rest sit off-screen
   and become reachable via [data-hub-next]. #155 */
.hub-carousel {
  position: relative;
  /* Extra padding so the absolute-positioned arrows can sit on the
     edge without overlapping the first/last tile's hover affordance. */
  padding: 0;
}
.hub-grid--carousel {
  /* Overrides the responsive auto-fit grid above — explicitly one
     row, column auto-flow, fixed-width tiles. */
  display: grid !important;
  grid-auto-flow: column;
  grid-auto-columns: calc((100% - 60px) / 4); /* 4 tiles - 3 gaps of 20px */
  overflow-x: auto;
  overflow-y: visible;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  /* Hide scrollbar — the arrows are the affordance. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.hub-grid--carousel::-webkit-scrollbar { display: none; }
.hub-grid--carousel > .hub-tile {
  scroll-snap-align: start;
}

/* Arrow buttons. Float over the track edges so they don't push the
   row width around. Hidden when there's nothing to scroll on that
   side — toggled by hub-carousel.js. */
.hub-carousel__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  width: 44px;
  height: 44px;
  border-radius: 999px;
  border: 1px solid var(--border, var(--rule, #d9d2c0));
  background: var(--bg-surface, #fff);
  color: var(--fg-1, #1a1a1a);
  font-size: 26px;
  font-weight: 400;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.10);
  transition: background var(--dur-fast) var(--ease),
              transform var(--dur-fast) var(--ease);
}
.hub-carousel__arrow:hover {
  background: var(--bg-surface-2, var(--mx2-paper-2, #f3eee2));
  transform: translateY(-50%) scale(1.05);
}
.hub-carousel__arrow--prev { left: -22px; }
.hub-carousel__arrow--next { right: -22px; }
.hub-carousel__arrow[hidden] { display: none; }

/* On very narrow viewports the arrows would sit off-page; pull them
   inward so they're still tappable. */
@media (max-width: 720px) {
  .hub-carousel__arrow--prev { left: 4px; }
  .hub-carousel__arrow--next { right: 4px; }
  .hub-grid--carousel {
    grid-auto-columns: 80%; /* one tile-ish per view on phones */
  }
}

/* Phones — drop the carousel entirely. The horizontal scroller fights
   with the .hub-grid mobile rule below (`grid-template-columns:
   minmax(0, 360px)`) since this modifier never resets the template,
   so on first paint tiles end up stacked but cropped at 80% width
   and visually overlap each other. Force a clean vertical stack +
   hide the arrows. Matches the standard .hub-grid mobile behavior. */
@media (max-width: 560px) {
  .hub-grid--carousel {
    display: grid !important;
    grid-auto-flow: row;
    grid-template-columns: minmax(0, 1fr);
    grid-auto-columns: auto;
    overflow-x: visible;
    scroll-snap-type: none;
  }
  .hub-carousel__arrow { display: none !important; }
}
/* Wide layouts — fixed column count per tile-total so rows always
   fill at the section's natural width. minmax(_, 1fr) lets the
   cards grow to consume any remaining horizontal space instead of
   leaving dead gutters on the right. */
.hub-grid:has(> :nth-child(1):last-child) { grid-template-columns: minmax(240px, 320px); }
.hub-grid:has(> :nth-child(2):last-child) { grid-template-columns: repeat(2, minmax(240px, 1fr)); }
.hub-grid:has(> :nth-child(3):last-child) { grid-template-columns: repeat(3, minmax(220px, 1fr)); }
.hub-grid:has(> :nth-child(4):last-child) { grid-template-columns: repeat(4, minmax(200px, 1fr)); }

/* Narrower viewports — 3 + 4 tiles collapse to 2 columns. With 4
   tiles that's a clean 2×2 (XX/XX). With 3 tiles the orphan would
   default to the left; force it span-2 + centered (XX/X centered). */
@media (max-width: 1000px) {
  .hub-grid:has(> :nth-child(3):last-child),
  .hub-grid:has(> :nth-child(4):last-child) {
    grid-template-columns: repeat(2, minmax(220px, 320px));
  }
  .hub-grid:has(> :nth-child(3):last-child) > :nth-child(3) {
    grid-column: 1 / -1;
    justify-self: center;
    max-width: 320px;
    width: 100%;
  }
}

/* Mobile — every tile gets its own row, centered. The per-tile-count
   :has() rules above have higher specificity than a bare .hub-grid
   selector (the `:nth-child(N):last-child` argument inflates :has()
   specificity to (0,3,0)), so the mobile rule has to repeat each
   variant explicitly or the 2-column 1000px-breakpoint rule will win
   and the grid overflows the viewport. */
@media (max-width: 560px) {
  .hub-grid,
  .hub-grid:has(> :nth-child(1):last-child),
  .hub-grid:has(> :nth-child(2):last-child),
  .hub-grid:has(> :nth-child(3):last-child),
  .hub-grid:has(> :nth-child(4):last-child) {
    grid-template-columns: minmax(0, 360px);
  }
  .hub-grid:has(> :nth-child(3):last-child) > :nth-child(3) {
    grid-column: auto;
    max-width: none;
  }
}
.hub-tile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
  padding: 28px 24px;
  border: 1px solid var(--rule);
  border-radius: 12px;
  background: var(--bg-card, #FFFFFF);
  text-decoration: none;
  color: inherit;
  transition: transform 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
  min-height: 200px;
}
.hub-tile:hover {
  transform: translateY(-2px);
  border-color: var(--accent, currentColor);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.08);
}
.hub-tile-icon {
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  background: rgba(159, 136, 108, 0.12);
  color: var(--accent, #9f886c);
}
.hub-tile-icon svg { width: 26px; height: 26px; }
.hub-tile-name {
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.005em;
}
.hub-tile-blurb {
  font-size: 14px;
  line-height: 1.5;
  opacity: 0.7;
  flex-grow: 1;
}
.hub-tile-cta {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.85;
}
/* Empty-state card shown when the user has access to no products in
   the active workspace. Replaces the tile grid entirely. */
.hub-empty {
  margin-top: 16px;
  padding: 28px 32px;
  background: #FFFFFF;
  border: 1px solid var(--rule, #d9d2c0);
  border-radius: 10px;
  max-width: 560px;
  text-align: center;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.04);
}
.hub-empty p { margin: 0 0 12px; color: var(--ink-soft, #5E5E5E); line-height: 1.5; }
.hub-empty p:last-child { margin-bottom: 0; }
/* On the dark Mx2 workspace, the tiles render as white cards (matching
   the iter3 white-card aesthetic on the rest of the admin) so they
   actually read as cards on a black backdrop. Text + meta colors flip
   to ink so contrast holds; the icon tile keeps its gold-tinted wash. */
body.mx2-side .hub-tile {
  background: #FFFFFF;
  border-color: var(--mx2-stone-200, #E8E0CD);
  color: var(--ink, #1A1A1A);
}
body.mx2-side .hub-tile:hover {
  background: #FFFFFF;
  border-color: var(--mx2-gold-mid, #b59a73);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
}
body.mx2-side .hub-tile-blurb {
  color: var(--ink-soft, #5E5E5E);
  opacity: 1;
}
body.mx2-side .hub-tile-cta {
  color: var(--mx2-gold-deep, #876e51);
  opacity: 1;
}

/* ── Social post attachment grids (Mx2 + client) ─────────────────────────── */
.social-attached-grid,
.social-picker-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px;
  margin: 12px 0;
}
.social-attached-tile,
.social-picker-tile {
  position: relative;
  display: block;
  border: 1px solid var(--rule);
  border-radius: 8px;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.02);
  transition: border-color 0.12s ease, transform 0.12s ease;
}
body.mx2-side .social-attached-tile,
body.mx2-side .social-picker-tile {
  background: rgba(255, 255, 255, 0.02);
}
.social-picker-tile { cursor: pointer; }
.social-picker-tile:hover { transform: translateY(-1px); border-color: var(--accent, #9f886c); }
.social-picker-tile input[type="checkbox"] {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 2;
  width: 18px;
  height: 18px;
  cursor: pointer;
}
.social-picker-tile:has(input:checked) {
  border-color: var(--accent, #9f886c);
  box-shadow: 0 0 0 2px var(--accent, #9f886c);
}
.social-attached-tile img,
.social-picker-tile img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: #000;
}
.social-attached-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 4 / 3;
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: rgba(0, 0, 0, 0.5);
  background: rgba(0, 0, 0, 0.06);
}
body.mx2-side .social-attached-fallback {
  color: rgba(255, 255, 255, 0.5);
  background: rgba(255, 255, 255, 0.04);
}
.social-attached-meta {
  padding: 8px 10px;
}
.social-attached-name {
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Social content calendar ────────────────────────────────────────────── */
.social-calendar-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}
/* The nav is the toolbar's primary content; let it span the full
 * width so the grid inside can anchor prev/next to the calendar
 * edges. Falls back gracefully when a sibling (e.g. the optional
 * List view link) is present — flex still respects the explicit
 * percentage and the sibling drops to the next row via flex-wrap. */
.social-calendar-toolbar > .social-calendar-nav { flex: 1 1 100%; }
.social-calendar-nav {
  /* Three-column grid: prev arrow flush-left, month label dead-center,
   * next arrow flush-right — anchored to the calendar grid edges
   * regardless of arrow widths. Mirrors the shoots calendar treatment
   * shipped 2026-05-23. Replaces the prior flex-row which sized to
   * content and left the prev/next clustered toward the middle. */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 12px;
  width: 100%;
}
.social-calendar-nav > :first-child { justify-self: start; }
.social-calendar-nav > :last-child  { justify-self: end; }
.social-calendar-month {
  margin: 0;
  text-align: center;
  font-family: var(--font-display, Cormorant, Georgia, serif);
  font-size: 28px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.1;
}
/* Calendar styling brought into line with the Event Shoots calendar
   (#149): cream paper grid, taller cells, mono day-numbers in the
   upper-left, slightly darker out-of-month cells. Keeps the existing
   per-platform chip tints + today's gold circle so the social-specific
   density signals still work. 2026-05-23. */
.social-calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 0;
  /* Inner hairline border around the grid (shoots-style). Frame +
   * shadow live on the wrapper section below so the toolbar gets
   * included in the framed card, matching the shoots .profile-section
   * treatment. */
  border: 1px solid var(--mx2-rule, var(--rule, #e6dcc5));
  border-radius: 6px;
  overflow: hidden;
  background: var(--mx2-paper, #f6efe3);
}
/* Frame around the social calendar — mirrors the shoots calendar's
 * .profile-section card (cream-overlay bg, gold-tinted hairline, soft
 * drop shadow). The cells inside use --mx2-paper, so the lighter
 * frame around them gives the same "darker tan cells against a
 * lighter cream frame" hierarchy you see in shoots. Wraps the
 * toolbar + grid + legend; pending-approval cards sit outside.
 * Same rule both sides via .social-calendar-frame. */
.social-calendar-frame {
  background: rgba(255, 252, 244, 0.92);
  border: 1px solid rgba(200, 169, 94, 0.22);
  border-radius: 12px;
  box-shadow:
    0 8px 32px rgba(40, 28, 12, 0.10),
    0 2px 8px rgba(40, 28, 12, 0.06);
  padding: 20px 24px;
  margin-bottom: 16px;
}
.social-calendar-dayhead {
  background: var(--mx2-paper-2, #faf6ec);
  padding: 8px 6px;
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  text-align: center;
  color: var(--text-muted, var(--ink-soft, #5E5E5E));
  border-right: 1px solid var(--mx2-rule, var(--rule, #e6dcc5));
  border-bottom: 1px solid var(--mx2-rule, var(--rule, #e6dcc5));
}
.social-calendar-dayhead:nth-child(7) { border-right: 0; }
body.mx2-side .social-calendar-dayhead {
  background: var(--mx2-paper-2, #faf6ec);
  color: var(--text-muted, var(--ink-soft, #5E5E5E));
}
.social-calendar-cell {
  background: var(--mx2-paper, #fffdf6);
  color: var(--ink, #1A1A1A);
  /* 110px to match the shoots cells — gives chips room to breathe and
     keeps the grid from looking cramped on light months. */
  min-height: 110px;
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  border-right: 1px solid var(--mx2-rule, var(--rule, #e6dcc5));
  border-bottom: 1px solid var(--mx2-rule, var(--rule, #e6dcc5));
  position: relative;
}
/* Hover-only "+ Add post" for admins — twin of .shoot-cal-day__add. */
.social-calendar-cell__add {
  position: absolute;
  top: 4px;
  right: 6px;
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  font-weight: 700;
  line-height: 1;
  border: 1px dashed var(--mx2-gold, #c4a772);
  border-radius: 50%;
  background: transparent;
  color: var(--mx2-gold, #c4a772);
  cursor: pointer;
  opacity: 0;
  transition: opacity 120ms ease, background 120ms ease;
  text-decoration: none;
}
.social-calendar-cell:hover .social-calendar-cell__add {
  opacity: 1;
}
.social-calendar-cell__add:hover {
  background: var(--mx2-gold, #c4a772);
  color: #1a1a1a;
  text-decoration: none;
}
.social-calendar-cell:nth-child(7n) { border-right: 0; }
.social-calendar-grid > .social-calendar-cell:nth-last-child(-n+7) { border-bottom: 0; }
body.mx2-side .social-calendar-cell {
  background: var(--mx2-paper, #fffdf6);
  color: var(--ink, #1A1A1A);
}
.social-calendar-cell--empty {
  /* Out-of-month cells get the darker paper-2 fill (matches shoots).
     Cleaner visual hierarchy than the prior "blend into white" approach. */
  background: var(--mx2-paper-2, #faf6ec);
  color: var(--mx2-stone-400, #a89e8c);
}
.social-calendar-cell--empty .social-calendar-daynum {
  color: var(--mx2-stone-400, #a89e8c);
  font-weight: 500;
}
body.mx2-side .social-calendar-cell--empty {
  background: var(--mx2-paper-2, #faf6ec);
}
/* "Start new post on this day" — admin only. JS in
 * social-calendar-newpost.js binds the click; the cursor hint here
 * keeps the affordance discoverable. Skipped on client side so users
 * who can't author posts don't get a misleading cursor. */
body.mx2-side .social-calendar-cell[data-cell-date] { cursor: pointer; }
/* Hover tint on every day cell across all four calendars (Drew, 2026-05-28).
   Subtle paper-2 wash so the cursor's position is obvious without
   competing with the today highlight. Both sides — clients get the
   visual feedback even though they can't click to add. */
.social-calendar-cell[data-cell-date]:hover {
  background: var(--mx2-paper-2, #faf6ec);
}
.social-newpost-popover {
  position: absolute;
  z-index: 1200;
  background: var(--mx2-paper, #fffdf6);
  border: 1px solid var(--mx2-rule, #e6dcc5);
  border-radius: 8px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.social-newpost-popover__date {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--mx2-gold, #c0a26b);
  text-align: center;
}
.social-newpost-popover__cta { text-align: center; }
.social-newpost-popover__close {
  background: transparent;
  border: 0;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--mx2-stone-500, #6f6452);
  cursor: pointer;
  padding: 4px 0 0;
}
.social-newpost-popover__close:hover { color: var(--ink); }
.social-calendar-cell--today {
  /* Soft gold tint + accent left border so the whole cell stands out,
   * not just the day number. Previously the only signal was a gold
   * circle around the daynum, which got lost the moment a chip was
   * in the cell. */
  background: rgba(200, 169, 94, 0.10);
  box-shadow: inset 4px 0 0 var(--mx2-gold, #c8a95e);
}
/* Day number matches shoots: mono, 12px, upper-left, muted. The cell
 * background already signals out-of-month / today, so the number can
 * stay quiet. */
.social-calendar-daynum {
  font-size: 12px;
  font-weight: 600;
  font-family: var(--font-mono, ui-monospace, "SF Mono", Menlo, monospace);
  color: var(--text-muted, var(--ink-soft, #5E5E5E));
  margin-bottom: 2px;
  line-height: 1;
  align-self: flex-start;
}
/* Today: the cell already has a gold tint + inset gold rail (rules
 * above), so the day number just needs a gold-deep colour bump to
 * read as the active day. Dropped the prior 22px circle treatment —
 * the cell background does the work now. */
.social-calendar-cell--today .social-calendar-daynum {
  color: var(--mx2-gold-deep, #876e51);
  font-weight: 700;
}
.social-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px;
  border-radius: 4px;
  font-size: 11px;
  text-decoration: none;
  color: inherit;
  border: 1px solid transparent;
  background: rgba(0, 0, 0, 0.04);
  overflow: hidden;
}
body.mx2-side .social-chip {
  /* Chips sit on white calendar cells now, so the previous translucent-
     white treatment would render invisibly. Switch to a soft gray-on-
     white default; status modifiers below override with their own tints. */
  background: rgba(0, 0, 0, 0.05);
  color: var(--ink, #1A1A1A);
}
.social-chip:hover {
  border-color: currentColor;
}
.social-chip-time {
  font-family: var(--font-mono);
  font-size: 10px;
  opacity: 0.7;
  flex-shrink: 0;
}
.social-chip-platform {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.05em;
  flex-shrink: 0;
}
.social-chip-caption {
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 11px;
}
.social-chip--draft        { background: rgba(120, 120, 120, 0.15); }
.social-chip--awaiting     { background: rgba(218, 165, 32, 0.18); border-color: rgba(218, 165, 32, 0.35); }
.social-chip--revisions    { background: rgba(200, 70, 70, 0.18); border-color: rgba(200, 70, 70, 0.35); }
.social-chip--approved     { background: rgba(70, 150, 90, 0.18); border-color: rgba(70, 150, 90, 0.35); }
.social-chip--scheduled    { background: rgba(70, 130, 200, 0.18); border-color: rgba(70, 130, 200, 0.35); }
.social-chip--published    { background: rgba(70, 130, 200, 0.32); border-color: rgba(70, 130, 200, 0.5); }
.social-chip--failed       { background: rgba(200, 70, 70, 0.32); border-color: rgba(200, 70, 70, 0.5); }

/* Compact calendar chips override their status-tinted background
   with platform-tinted background, matching the showcase's
   .cal-post.li / .fb / .ig pattern. Status remains expressed via
   the existing modifier classes everywhere ELSE (list view,
   detail page), but inside the calendar's tight cells the
   platform tint reads more usefully than the status one. */
.social-chip--compact.social-chip--platform-linkedin  { background: #eaf2fb; border-color: #c5dcf5; color: #0a66c2; }
.social-chip--compact.social-chip--platform-facebook  { background: #eaf2fb; border-color: #c5dcf5; color: #1877f2; }
.social-chip--compact.social-chip--platform-instagram { background: #fbeaf2; border-color: #f5c5d8; color: #d62976; }
.social-chip--compact.social-chip--platform-tiktok    { background: #efefef; border-color: #d0d0d0; color: #111; }
.social-chip--compact .social-chip-time { color: inherit; opacity: 0.85; }

/* Status glyph rendered between the platform icon and the time on
 * calendar chips. Tiny — meant to be a quick at-a-glance signal
 * without crowding the chip. Color comes from the per-status
 * modifier below so the glyph carries meaning even when the chip
 * is platform-tinted (compact calendar view). */
.social-chip-status-icon {
  font-size: 13px;
  line-height: 1;
  flex-shrink: 0;
  opacity: 0.95;
  /* Right-align inside the chip so the status reads at a glance —
   * Drew's UX call after the first pass placed it inline between
   * the platform icon and time. */
  margin-left: auto;
}
.social-chip-status-icon--published          { color: #2e8b57; font-weight: 700; }
.social-chip-status-icon--scheduled          { color: #1c5fb8; }
.social-chip-status-icon--approved           { color: #2e8b57; }
.social-chip-status-icon--awaiting_approval  { color: #c58a13; font-weight: 700; }
.social-chip-status-icon--revisions_requested { color: #b14a3a; }
.social-chip-status-icon--rejected           { color: #b14a3a; font-weight: 700; }
.social-chip-status-icon--failed             { color: #b14a3a; }
.social-chip-status-icon--review             { /* lightbulb emoji has its own color; nothing to set */ }
.social-chip-status-icon--draft              { color: #6e6e6e; }
.social-chip-status-icon--rough_draft        { color: #6e6e6e; }

/* Status legend below the calendar grid — explains the chip glyphs.
 * Renders on both admin + client side. Compact, wraps on narrow
 * viewports. The .social-chip-status-icon inside a legend item drops
 * the margin-left:auto rule (it's a calendar-chip-only positioning
 * trick) so the icon sits flush next to its label. */
.social-calendar-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px 18px;
  margin: 16px 0 8px;
  padding: 10px 14px;
  background: rgba(0, 0, 0, 0.04);
  border-radius: 6px;
  font-size: 12px;
  color: var(--mx2-stone-700, #221f19);
}
.social-calendar-legend-title {
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-size: 11px;
  opacity: 0.75;
  /* Force the title onto its own line so the icon rows sit below
   * it as a tidy strip. Without this the title sat inline with
   * the first few items and the wrap break landed mid-strip. */
  flex-basis: 100%;
  margin-bottom: 2px;
}
.social-calendar-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.social-calendar-legend-item .social-chip-status-icon {
  margin-left: 0;
  font-size: 14px;
  opacity: 1;
}
.social-calendar-legend-label { white-space: nowrap; }

/* Hover preview for chips on the calendar — driven by
   social-calendar-popover.js. Lives at body level (positioned
   absolute) so it can escape the calendar grid's overflow:hidden
   without getting clipped at cell edges. */
.social-calendar-popover {
  position: absolute;
  z-index: 100;
  width: 280px;
  max-width: calc(100vw - 32px);
  background: #FFFFFF;
  color: var(--ink, #1A1A1A);
  border: 1px solid var(--rule, #d9d2c0);
  border-radius: 6px;
  padding: 12px 14px;
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1.4;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.2);
  pointer-events: auto;
}
.social-calendar-popover[hidden] { display: none; }
/* Mini creative preview at the top of the popover. Negative margins
   pull it to the popover edges so the image hits the corners.
   object-fit: contain (not cover) keeps the full image visible — the
   prior 'cover' crop would chop heads off portraits and edges off
   landscape shots. Letterboxing on the dark background is acceptable;
   misrepresenting what's about to publish is not. */
/* Client identity strip at the very top of the popover — Mx2 admin
 * views span every client, so the planner needs "which client?" in
 * their first eye-fix. Logo is small (24px) + name sits inline. The
 * logo's onerror hides the <img> so missing logos collapse cleanly. */
.social-calendar-popover__client {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: -12px -14px 10px;
  padding: 8px 14px;
  background: var(--mx2-stone-700, #221f19);
  border-bottom: 1px solid var(--rule, #d9d2c0);
  border-top-left-radius: 6px;
  border-top-right-radius: 6px;
  color: var(--cream, #f3e8d9);
}
.social-calendar-popover__client-logo {
  width: 24px;
  height: 24px;
  object-fit: contain;
  background: rgba(255, 255, 255, 0.04);
  border-radius: 4px;
  flex-shrink: 0;
}
.social-calendar-popover__client-name {
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0.01em;
}
/* When BOTH the client strip and thumb are present, drop the thumb's
 * top corner rounding + top margin since the client strip is now
 * occupying the top of the popover. */
.social-calendar-popover__client + .social-calendar-popover__thumb {
  margin-top: 0;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}
.social-calendar-popover__thumb {
  margin: -12px -14px 10px;
  border-bottom: 1px solid var(--rule, #d9d2c0);
  background: var(--mx2-stone-700, #221f19);
  border-top-left-radius: 6px;
  border-top-right-radius: 6px;
  overflow: hidden;
  max-height: 220px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.social-calendar-popover__thumb img {
  display: block;
  max-width: 100%;
  max-height: 220px;
  width: auto;
  height: auto;
  object-fit: contain;
}
.social-calendar-popover__head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}
.social-calendar-popover__platform {
  font-weight: 600;
  font-size: 13px;
}
.social-calendar-popover__status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: rgba(120, 120, 120, 0.5);
  margin-left: auto;
}
.social-calendar-popover__status-dot.social-chip--draft     { background: rgb(120, 120, 120); }
.social-calendar-popover__status-dot.social-chip--awaiting  { background: rgb(218, 165, 32); }
.social-calendar-popover__status-dot.social-chip--revisions { background: rgb(200, 70, 70); }
.social-calendar-popover__status-dot.social-chip--approved  { background: rgb(70, 150, 90); }
.social-calendar-popover__status-dot.social-chip--scheduled { background: rgb(70, 130, 200); }
.social-calendar-popover__status-dot.social-chip--published { background: rgb(70, 130, 200); }
.social-calendar-popover__status-dot.social-chip--failed    { background: rgb(200, 70, 70); }
.social-calendar-popover__status {
  font-family: var(--font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-soft, #5E5E5E);
}
.social-calendar-popover__org {
  font-size: 12px;
  color: var(--ink-soft, #5E5E5E);
  margin-bottom: 2px;
}
.social-calendar-popover__time {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-soft, #5E5E5E);
  margin-bottom: 8px;
}
.social-calendar-popover__caption {
  font-size: 13px;
  line-height: 1.45;
  display: -webkit-box;
  -webkit-line-clamp: 6;
  -webkit-box-orient: vertical;
  overflow: hidden;
  white-space: pre-wrap;
}

.social-calendar-unscheduled {
  margin-top: 24px;
  padding: 16px;
  border: 1px dashed var(--rule);
  border-radius: 8px;
}
.social-calendar-unscheduled h3 {
  margin: 0 0 4px;
  font-size: 14px;
}
.social-calendar-unscheduled ul {
  list-style: none;
  padding: 0;
  margin: 12px 0 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* "Posts Waiting For Approval" — full-width card list under the
   calendar. Each card: 84px thumbnail rail on the left, caption +
   meta in the middle, "Review post" CTA on the right. Stacks
   responsively on narrow screens (thumb on top, body + CTA below). */
.social-calendar-pending {
  margin-top: 24px;
  padding: 16px;
  border: 1px dashed var(--rule);
  border-radius: 8px;
}
.social-calendar-pending-head {
  margin: 0 0 4px;
  font-size: 14px;
}
.social-calendar-pending-sub { margin: 0 0 12px; }
.social-calendar-pending-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.social-calendar-pending-card {
  display: grid;
  grid-template-columns: 84px 1fr auto;
  gap: 14px;
  align-items: center;
  padding: 12px;
  border: 1px solid var(--rule, #d9d2c0);
  border-radius: 8px;
  background: var(--bg-card, #fff);
}
.social-calendar-pending-thumb {
  width: 84px;
  height: 84px;
  border-radius: 6px;
  overflow: hidden;
  background: var(--bg-muted, #f3eedf);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.social-calendar-pending-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.social-calendar-pending-thumb-fallback {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--dim, #777);
}
.social-calendar-pending-body { min-width: 0; }
.social-calendar-pending-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  font-size: 12px;
  color: var(--dim, #777);
  margin-bottom: 4px;
}
.social-calendar-pending-platform {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-weight: 600;
  color: var(--ink, #1A1A1A);
}
.social-calendar-pending-caption {
  margin: 0;
  font-size: 13px;
  line-height: 1.45;
  /* Clamp to 3 lines to keep cards roughly uniform height. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.social-calendar-pending-caption.dim { color: var(--dim, #777); }
.social-calendar-pending-actions {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

@media (max-width: 640px) {
  .social-calendar-pending-card {
    grid-template-columns: 64px 1fr;
    grid-template-areas:
      "thumb body"
      "actions actions";
  }
  .social-calendar-pending-thumb { width: 64px; height: 64px; grid-area: thumb; }
  .social-calendar-pending-body { grid-area: body; }
  .social-calendar-pending-actions { grid-area: actions; justify-content: flex-end; }
}

@media (max-width: 720px) {
  .social-calendar-cell { min-height: 70px; padding: 4px; }
  .social-chip-caption { display: none; }
  .social-chip-time { font-size: 9px; }
  .social-calendar-month { min-width: 0; font-size: 16px; }
  /* 7 days × 1fr can't fit a chip + time on a phone. Give the grid
     a fixed per-column width and let the whole grid scroll
     horizontally — most calendars on iOS work this way. */
  .social-calendar-grid {
    grid-template-columns: repeat(7, 60px);
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x mandatory;
  }
  .social-calendar-cell { scroll-snap-align: start; }
}

/* ── MaestroX page-header mobile rules ─────────────────────────────────────
   At narrow widths, .page-header-row keeps display: flex with the right
   column flex-shrink: 0 — so actions (toggles, primary CTAs) crush
   the title column and the description wraps a word per line. Stack
   vertically on phones so each region gets full width. Also lets
   page-actions wrap so multiple buttons line up cleanly. */
@media (max-width: 720px) {
  .page-header-row {
    flex-direction: column;
    align-items: stretch;
    gap: 16px;
  }
  .page-header-right {
    flex-shrink: 1;
    flex-wrap: wrap;
    justify-content: flex-start;
  }
  .page-title.xl { font-size: 36px; }
  .page-title.lg { font-size: 28px; }
  .page-title    { font-size: 22px; }
  .page-description { max-width: none; }

  /* Filter forms (.list-filter / .toolbar) — desktop is a single row
     of selects + button. On phones each .field needs full width or
     each control sizes to its own content and the row staircases. */
  .list-filter,
  form.list-filter {
    flex-direction: column;
    align-items: stretch !important;
  }
  .list-filter .field { width: 100%; }
  .list-filter .field select,
  .list-filter__select,
  .list-filter__search { width: 100%; }
  .list-filter > .btn,
  .list-filter > a.btn,
  .list-filter > button { align-self: stretch; }
  .toolbar { flex-direction: column; align-items: stretch; }
  .toolbar-search { width: 100%; }
  .toolbar-spacer { display: none; }

  /* Folder tabs — give the strip a smooth horizontal scroll and let
     the leftmost (active) tab always be the visible default. */
  .tabs {
    padding-left: 0;
    padding-right: 0;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
  }
  .tab { scroll-snap-align: start; }
}

/* ── Social preview (per platform) ───────────────────────────────────────── */
.social-preview-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 16px;
  margin-bottom: 16px;
}
@media (max-width: 1024px) {
  .social-preview-layout { grid-template-columns: 1fr; }
}
.social-preview-pane {
  display: flex;
  flex-direction: column;
}

/* Shared preview bones */
.sp { display: block; }
.sp-card {
  background: #fff;
  color: #111;
  border-radius: 8px;
  border: 1px solid #d8d8d8;
  overflow: hidden;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-size: 14px;
  /* Cap preview width so the post card reads like the real platform
     instead of stretching the full content column. 552px matches
     LinkedIn / Facebook feed width; Instagram + TikTok override
     below with their own narrower / portrait sizes. */
  max-width: 552px;
  margin: 0 auto;
  line-height: 1.45;
}

/* Shared max-height for every 9:16 portrait preview surface — IG
   reel, IG story, FB story, TikTok frame. Keeping them at the same
   token means a client reviewing a 3-platform story group sees three
   identically-sized phone frames stacked, and the full preview fits
   on one screen alongside the page header + status banner +
   schedule line + caption. 540px works on a 13" laptop without
   scroll; smaller viewports use the viewport-relative fallback. */
:root {
  --sp-portrait-max-height: min(540px, 70vh);
}
.sp-head {
  display: flex;
  gap: 10px;
  align-items: center;
  padding: 12px;
}
.sp-avatar {
  width: 48px; height: 48px;
  border-radius: 50%;
  overflow: hidden;
  background: #ddd;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.sp-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sp-avatar-fallback {
  display: none;
  width: 100%; height: 100%;
  align-items: center;
  justify-content: center;
  background: #9f886c;
  color: #fff;
  font-weight: 600;
  font-size: 18px;
}
.sp-head-meta { flex: 1; min-width: 0; }
.sp-name { font-weight: 600; font-size: 15px; color: #111; }
.sp-sub { font-size: 12px; color: #666; }
.sp-body { padding: 4px 12px 12px; white-space: pre-wrap; color: #111; }
.sp-meta { padding: 0 12px 8px; font-size: 13px; color: #444; }
.sp-media { width: 100%; background: #000; }
.sp-media img,
.sp-media video { width: 100%; height: auto; display: block; }

/* Big centered play badge for video previews. Injected by
   social-preview-video.js, which strips the native `controls`
   attribute on load and re-enables it after first play. Sits on
   top of <video> in any of the sp-media* containers. */
.sp-video-play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.18);
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  z-index: 5;
  transition: background 120ms ease;
}
.sp-video-play:hover { background: rgba(0, 0, 0, 0.32); }
.sp-video-play__badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 72px;
  height: 72px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.45);
  transition: transform 120ms ease, background 120ms ease;
  /* Slight right-bias because the play triangle is visually
     left-heavy — without this the icon looks off-center. */
  padding-left: 4px;
}
.sp-video-play:hover .sp-video-play__badge {
  background: rgba(0, 0, 0, 0.75);
  transform: scale(1.05);
}
.sp-video-play:focus-visible {
  outline: 2px solid #fff;
  outline-offset: -4px;
}
.sp-media-empty {
  aspect-ratio: 4 / 3;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #aaa;
  font-family: var(--font-mono);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  background: #f0f0f0;
}
.sp-actions {
  display: flex;
  gap: 16px;
  padding: 8px 12px;
  border-top: 1px solid #ececec;
  font-size: 13px;
  color: #555;
}

/* LinkedIn-specific tweaks */
.sp-linkedin .sp-card { border: 1px solid #e6e6e6; }
/* Pages on LinkedIn use a square avatar with subtle 4px rounding —
   distinct from personal profiles' circular avatars. Override the
   default .sp-avatar circle. */
.sp-avatar-linkedin {
  border-radius: 4px !important;
}
.sp-head-linkedin { align-items: flex-start; }
.sp-follow {
  background: transparent;
  color: #0A66C2;
  border: none;
  font-weight: 600;
  font-size: 14px;
  cursor: default;
}
.sp-globe { font-size: 11px; }
.sp-actions-linkedin { justify-content: space-between; color: #555; }

/* Facebook-specific tweaks */
.sp-facebook .sp-card { border-color: #d8dadf; }
.sp-fb-more { color: #65676b; font-size: 18px; padding: 0 8px; }
.sp-body-fb { font-size: 15px; }
.sp-react-counts { padding: 8px 12px; font-size: 13px; color: #65676b; border-top: 1px solid #ececec; }
.sp-actions-facebook { justify-content: space-around; color: #65676b; font-weight: 600; }

/* Instagram-specific tweaks */
.sp-instagram .sp-card { max-width: 470px; margin: 0 auto; }
.sp-head-ig { padding: 10px 12px; }
.sp-avatar-ig { width: 32px; height: 32px; }
.sp-ig-more { font-size: 18px; color: #262626; }
.sp-media-instagram {
  aspect-ratio: 1 / 1;
  background: #000;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
/* Reel preview swaps to Instagram's vertical 9:16 (1080×1920) so the
   stylized rendering matches what the post will look like in feed.
   Capped via --sp-portrait-max-height (defined globally above .sp-card)
   so every 9:16 surface — IG reel, IG story, FB story, TikTok — reads
   as the same size to reviewers. Drew flagged that TT was rendering
   taller than the others on 2026-05-22; that gap is closed here. */
.sp-media-instagram--reel {
  aspect-ratio: 9 / 16;
  max-height: var(--sp-portrait-max-height, 540px);
  width: auto;
  margin: 0 auto;
}
.sp-media-instagram img,
.sp-media-instagram video { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ── Per-platform multi-photo layouts ─────────────────────────────────────
   Each network renders a 2+ image post differently. We mimic each
   one so the live preview matches what the actual feed shows. All
   grids use object-fit: cover for crisp Retina rendering at any
   container width. */

/* Facebook: 1 / 2 / 3 / 4+ photo grid with overflow overlay. Gaps
   are 2px (FB's signature gutters). */
.sp-media-fb-grid {
  display: grid;
  gap: 2px;
  background: #e4e6eb;
  width: 100%;
  overflow: hidden;
}
.sp-media-fb-grid .sp-media-fb-cell {
  position: relative;
  overflow: hidden;
  background: #f0f2f5;
}
.sp-media-fb-grid .sp-media-fb-cell img,
.sp-media-fb-grid .sp-media-fb-cell video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.sp-media-fb--single {
  grid-template-columns: 1fr;
}
.sp-media-fb--single .sp-media-fb-cell {
  /* Single photo: contain at FB's typical 4:5 max — keeps the post
     from sprawling. Use object-fit: cover to mimic FB's behavior. */
  aspect-ratio: 4 / 5;
}
.sp-media-fb--two {
  grid-template-columns: 1fr 1fr;
}
.sp-media-fb--two .sp-media-fb-cell { aspect-ratio: 1 / 1; }
.sp-media-fb--three {
  grid-template-columns: 1.4fr 1fr;
  grid-template-rows: 1fr 1fr;
}
.sp-media-fb--three .sp-media-fb-cell:nth-child(1) {
  grid-row: span 2;
  aspect-ratio: auto;
}
.sp-media-fb--three .sp-media-fb-cell:nth-child(2),
.sp-media-fb--three .sp-media-fb-cell:nth-child(3) {
  aspect-ratio: 16 / 9;
}
.sp-media-fb--four {
  grid-template-columns: 1fr 1fr;
}
.sp-media-fb--four .sp-media-fb-cell { aspect-ratio: 1 / 1; }
.sp-media-fb-cell--overflow::after {
  /* Slight dim under the +N overlay so the text reads on bright photos. */
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  pointer-events: none;
}
.sp-media-fb-overflow {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-family: var(--font-serif, Georgia);
  font-size: 36px;
  font-weight: 400;
  z-index: 1;
  pointer-events: none;
}

/* ── Facebook story preview ────────────────────────────────────────
 * Full-bleed 9:16 phone frame. Used by social-preview-facebook.ejs
 * when previewMode === 'story'. Mirrors how FB renders stories in
 * the Stories tray: dark background, progress segments + avatar +
 * name overlay at top, single media cover-filling, action chips
 * pinned at the bottom. Distinct from the feed card layout above —
 * stories have no body text, no react counts, no Like/Comment/Share
 * action bar (the actual story UI shows Send message + heart + share).
 */
.sp-story-frame {
  position: relative;
  aspect-ratio: 9 / 16;
  /* Shared height cap with IG reel + TikTok frame so all 9:16
     previews match (see .sp-media-instagram--reel comment). */
  max-height: var(--sp-portrait-max-height, 540px);
  width: auto;
  margin: 0 auto;
  background: #000;
  border-radius: 18px;
  overflow: hidden;
  color: #fff;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.sp-story-progress {
  position: absolute;
  top: 8px;
  left: 12px;
  right: 12px;
  height: 3px;
  display: flex;
  gap: 4px;
  z-index: 3;
}
.sp-story-progress-bar {
  flex: 1;
  background: rgba(255, 255, 255, 0.35);
  border-radius: 2px;
  overflow: hidden;
  position: relative;
}
.sp-story-progress-bar::after {
  content: '';
  position: absolute;
  inset: 0;
  width: 35%;
  background: rgba(255, 255, 255, 0.95);
  border-radius: 2px;
}
.sp-story-head {
  position: absolute;
  top: 24px;
  left: 12px;
  right: 12px;
  display: flex;
  align-items: center;
  gap: 10px;
  z-index: 3;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
.sp-story-head .sp-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1.5px solid #fff;
  flex: 0 0 32px;
}
.sp-story-head-meta {
  flex: 1;
  min-width: 0;
}
.sp-story-name {
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sp-story-sub {
  font-size: 11px;
  opacity: 0.85;
}
.sp-story-more, .sp-story-close {
  font-size: 18px;
  opacity: 0.9;
}
.sp-story-media {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #000;
}
.sp-story-media img,
.sp-story-media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.sp-story-empty {
  padding: 24px;
  color: rgba(255, 255, 255, 0.7);
  font-size: 13px;
  text-align: center;
  line-height: 1.55;
}
.sp-story-empty-sub {
  display: inline-block;
  margin-top: 6px;
  font-size: 11px;
  opacity: 0.7;
}
.sp-story-foot {
  position: absolute;
  bottom: 12px;
  left: 12px;
  right: 12px;
  display: flex;
  align-items: center;
  gap: 10px;
  z-index: 3;
}
.sp-story-reply {
  flex: 1;
  background: rgba(0, 0, 0, 0.4);
  border: 1px solid rgba(255, 255, 255, 0.6);
  color: rgba(255, 255, 255, 0.9);
  border-radius: 999px;
  padding: 8px 14px;
  font-size: 12px;
}
.sp-story-react, .sp-story-share {
  font-size: 22px;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}

/* Instagram carousel — horizontal scroll-snap track with dot
   indicators. The track itself fills the square aspect-ratio
   container; each slide is one viewport-width. */
.sp-media-instagram--carousel {
  position: relative;
  padding: 0;
}
.sp-ig-carousel-track {
  display: flex;
  width: 100%;
  height: 100%;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}
.sp-ig-carousel-track::-webkit-scrollbar { display: none; }
.sp-ig-carousel-slide {
  flex: 0 0 100%;
  scroll-snap-align: start;
  height: 100%;
}
.sp-ig-carousel-slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.sp-ig-carousel-badge {
  position: absolute;
  top: 10px;
  right: 10px;
  color: #fff;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
  pointer-events: none;
}
.sp-ig-carousel-dots {
  display: flex;
  justify-content: center;
  gap: 4px;
  padding: 6px 0 2px;
}
.sp-ig-carousel-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #c8c8c8;
  transition: background 120ms ease;
}
.sp-ig-carousel-dot.is-active { background: #0095f6; }

/* LinkedIn multi-photo grid — similar to FB but with sharper
   gutters and a slightly different 3-photo arrangement (LinkedIn
   stacks the right column instead of FB's two-row right column). */
.sp-media-li-grid {
  display: grid;
  gap: 2px;
  background: #e4e6eb;
  width: 100%;
  overflow: hidden;
}
.sp-media-li-grid .sp-media-li-cell {
  position: relative;
  overflow: hidden;
  background: #f0f2f5;
}
.sp-media-li-grid .sp-media-li-cell img,
.sp-media-li-grid .sp-media-li-cell video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.sp-media-li--single { grid-template-columns: 1fr; }
/* Default single-cell aspect (used by video — LinkedIn always renders
   video at 16:9). Single-image gets overridden below to respect the
   uploaded image's natural shape. */
.sp-media-li--single .sp-media-li-cell { aspect-ratio: 16 / 9; }
/* Single LinkedIn image — render at natural aspect ratio, no
   force-crop. LinkedIn natively displays portrait (4:5), square
   (1:1), and landscape (1.91:1) uncropped; a portrait headshot
   forced into 16:9 cover gets a chunk lopped off top + bottom and
   misrepresents what'll publish. Hard cap height for extreme
   portraits so the preview pane doesn't blow out. Targets only
   cells with no <video> descendant — the video branch keeps 16:9. */
.sp-media-li--single .sp-media-li-cell:not(:has(video)) {
  aspect-ratio: auto;
}
.sp-media-li--single .sp-media-li-cell:not(:has(video)) img {
  width: 100%;
  height: auto;
  max-height: 720px;
  object-fit: contain;
}
.sp-media-li--two { grid-template-columns: 1fr 1fr; }
.sp-media-li--two .sp-media-li-cell { aspect-ratio: 1 / 1; }
.sp-media-li--three {
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 2fr 1fr;
}
.sp-media-li--three .sp-media-li-cell:nth-child(1) {
  grid-column: span 2;
  aspect-ratio: 16 / 9;
}
.sp-media-li--three .sp-media-li-cell:nth-child(2),
.sp-media-li--three .sp-media-li-cell:nth-child(3) {
  aspect-ratio: 2 / 1;
}
.sp-media-li--four { grid-template-columns: 1fr 1fr; }
.sp-media-li--four .sp-media-li-cell { aspect-ratio: 1 / 1; }
.sp-media-li-cell--overflow::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  pointer-events: none;
}
.sp-media-li-overflow {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 32px;
  font-weight: 500;
  pointer-events: none;
}
.sp-actions-instagram {
  border-top: none;
  font-size: 22px;
  padding: 8px 12px;
  color: #262626;
}
.sp-ig-likes { padding: 0 12px; font-weight: 600; font-size: 14px; }
.sp-body-ig { padding: 6px 12px 0; }

/* TikTok-specific tweaks — vertical 9:16 frame */
/* Make the .sp-card wrapper invisible for TikTok — the inner
   .sp-tiktok-frame already has its own black background + rounded
   corners and sits centered on the cream page, matching the
   FB/IG story look exactly. Previously this carried background:
   #000 which created a visible 552px black band when the
   portrait-max-height fix narrowed the inner frame to ~304px
   (Drew, 2026-05-22). A fit-content attempt to shrink the card
   created a width-resolution loop with the frame's width: auto
   and collapsed the layout to 0 — go transparent instead. */
.sp-tiktok .sp-card-tiktok {
  background: transparent;
  border: 0;
  padding: 0;
}
.sp-tiktok-frame {
  position: relative;
  /* Shared portrait cap — see .sp-media-instagram--reel comment.
     Previously had no max-height, so TikTok previews rendered ~980px
     tall (552 × 16/9) and bled below the fold while IG/FB stories
     capped at 75vh. */
  aspect-ratio: 9 / 16;
  max-height: var(--sp-portrait-max-height, 540px);
  width: auto;
  margin: 0 auto;
  background: #000;
  overflow: hidden;
  border-radius: 8px;
}
.sp-tiktok-frame > img,
.sp-tiktok-frame > video,
.sp-tiktok-frame > .sp-media-empty {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
.sp-tiktok-overlay {
  position: absolute;
  bottom: 0; left: 0; right: 60px;
  padding: 16px;
  color: #fff;
  background: linear-gradient(to top, rgba(0,0,0,0.6), transparent);
}
.sp-tiktok-handle { font-weight: 700; font-size: 15px; }
.sp-tiktok-caption {
  font-size: 14px;
  line-height: 1.4;
  margin-top: 6px;
  white-space: pre-wrap;
}
.sp-tiktok-loc { font-size: 12px; opacity: 0.85; margin-top: 6px; }
.sp-tiktok-rail {
  position: absolute;
  right: 8px;
  bottom: 16px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  align-items: center;
  color: #fff;
  font-size: 11px;
  text-align: center;
}
.sp-tiktok-action span { font-size: 11px; opacity: 0.9; }
.sp-tiktok-avatar {
  width: 44px; height: 44px;
  border-radius: 50%;
  border: 2px solid #fff;
  overflow: hidden;
  background: #ddd;
  display: flex; align-items: center; justify-content: center;
}
.sp-tiktok-avatar img { width: 100%; height: 100%; object-fit: cover; }
.sp-tiktok-avatar .sp-avatar-fallback {
  display: none;
  width: 100%; height: 100%;
  align-items: center; justify-content: center;
  background: #9f886c; color: #fff; font-size: 18px;
}

/* ── Recipient checklist on the preview page ────────────────────────────── */
.social-recipient-list {
  list-style: none;
  padding: 0;
  margin: 0 0 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.social-recipient-list li label {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border: 1px solid var(--mx2-stone-200);
  border-radius: 6px;
  background: #fff;
  cursor: pointer;
}
.social-recipient-list li input[type="checkbox"] {
  grid-row: span 2;
  width: 18px; height: 18px;
}
.social-recipient-name { font-weight: 600; }
.social-recipient-email { grid-column: 2; }

/* ── Upload form: radio rows + soft-disable look on the gallery picker ─── */
.social-upload-radio {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
}
.social-upload-radio input[type="radio"] {
  width: 16px; height: 16px;
  accent-color: var(--mx2-gold);
}

/* ── Upload tiles + picker (po-thumbs / po-slot-picker) ───────────────────
   Originally in client.css scoped to client-side surfaces. Promoted to
   base.css so the dark Mx2 layout (campaign uploads, social attachments)
   can render the same "+ Add file" affordance + thumb tile without
   duplicating the markup. White tiles + dashed gold "+ Add file" button
   read fine on both light client and dark Mx2 surfaces.                  */
.po-slot-upload {
  display: flex;
  gap: 10px;
  align-items: center;
  margin-top: 10px;
}
.po-slot-upload input[type='file'] {
  flex: 1;
  font-family: var(--font-body);
  font-size: 13px;
}

/* Auto-upload picker — replaces the bare <input type=file> + Upload
   button with a single styled "Add file" tile that triggers the
   native file picker. The actual <input> is visually hidden but
   reachable via keyboard. */
.po-slot-picker {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  background: #FFFFFF;
  border: 1px dashed var(--mx2-gold, #c9a86a);
  border-radius: 6px;
  cursor: pointer;
  color: var(--mx2-gold-deep, #876e51);
  font-size: 13px;
  font-weight: 600;
  transition: background 120ms ease, border-color 120ms ease;
}
.po-slot-picker:hover {
  background: rgba(212, 175, 110, 0.08);
  border-style: solid;
}
.po-slot-picker input[type='file'] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.po-slot-picker__label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* Thumbnail grid — one tile per uploaded file. Image files preview
   inline; everything else gets a generic doc icon + extension. */
.po-thumbs {
  list-style: none;
  padding: 0;
  margin: 12px 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 12px;
}
.po-thumb {
  position: relative;
  background: #FFFFFF;
  border: 1px solid var(--rule-lt);
  border-radius: 6px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.po-thumb--approved { border-color: var(--status-launched, #4a8c5e); }
.po-thumb--rejected { border-color: var(--mx2-rust, #b14b3a); }
.po-thumb__media {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 96px;
  background: #F6F2E7;
  text-decoration: none;
  color: var(--ink-soft);
  flex-direction: column;
  gap: 4px;
}
.po-thumb__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.po-thumb__icon { display: inline-flex; }
.po-thumb__ext {
  font-family: var(--font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--mx2-gold-deep, #876e51);
}
.po-thumb__meta {
  padding: 8px 10px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
}
.po-thumb__name {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink);
  word-break: break-all;
  line-height: 1.3;
}
.po-thumb__remove {
  position: absolute;
  top: 4px;
  right: 4px;
  margin: 0;
}
.po-thumb__remove button {
  background: rgba(20, 20, 20, 0.65);
  color: #FFFFFF;
  border: 0;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}
.po-thumb__remove button:hover { background: var(--mx2-rust, #b14b3a); }


/* Social-post upload destination picker — compact bar above the thumbs
   grid. Sets where new uploads land; stays visible while the user
   "+ Add file"s repeatedly. */
.social-upload-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  margin: 0 0 12px;
  background: rgba(159, 136, 108, 0.08);
  border: 1px solid var(--mx2-stone-200);
  border-radius: 6px;
}
.social-upload-bar select,
.social-upload-bar input[type='text'] {
  font-size: 13px;
  padding: 6px 10px;
}
.social-upload-bar input[type='text'] {
  flex: 1;
  min-width: 200px;
}

/* Social-post iteration history — each "round" is one Mx2 submit
   followed by the client's decision (approve / request changes /
   reject) plus any in-between Mx2 actions. */
.social-rounds {
  list-style: none;
  padding: 0;
  margin: 12px 0 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.social-round {
  border: 1px solid var(--mx2-stone-200);
  border-radius: 6px;
  padding: 12px 14px;
  background: rgba(255, 255, 255, 0.6);
}
.social-round-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
}
.social-round-number {
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0.02em;
}
.social-round-events {
  list-style: none;
  padding: 0;
  margin: 8px 0 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.social-round-event {
  font-size: 13px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 10px;
  border-left: 3px solid transparent;
  background: rgba(0, 0, 0, 0.02);
  border-radius: 0 4px 4px 0;
}
.social-round-event--success { border-left-color: var(--status-launched, #4a8c5e); }
.social-round-event--warn    { border-left-color: var(--mx2-amber, #eb9b22); }
.social-round-event--danger  { border-left-color: var(--mx2-rust, #b14b3a); }
.social-round-event--neutral { border-left-color: var(--mx2-stone-300, #b8ae93); }
.social-round-event-label    { font-weight: 600; }
.social-round-comment {
  margin: 4px 0 0;
  padding: 6px 10px;
  background: rgba(0, 0, 0, 0.04);
  border-radius: 4px;
  white-space: pre-wrap;
  font-size: 13px;
  font-style: italic;
}

/* Library-picker tiles inside the social post — reuse .po-thumb skin
   but make the tile clickable as a label (checkbox lives at the
   top-left, the rest of the tile is the click target). */
.social-library-tile-label {
  display: contents;
  cursor: pointer;
}
.social-library-tile-label input[type='checkbox'] {
  position: absolute;
  top: 6px; left: 6px;
  width: 18px; height: 18px;
  z-index: 2;
}
.social-library-tile:has(input:checked) {
  outline: 2px solid var(--mx2-gold, #9f886c);
  outline-offset: -2px;
}

/* ── LinkedIn document-carousel preview ──────────────────────────────────
   Renders multi-page PDFs the way LinkedIn shows a "document share":
   one page visible at a time, swipe arrows on the sides, dot row +
   page counter at the bottom. JS in public/js/social-carousel.js
   does the page swaps; this just styles the markup.                       */
.sp-doc-carousel {
  background: #f3f2ef;
  border-top: 1px solid #e6e6e6;
  border-bottom: 1px solid #e6e6e6;
  position: relative;
  padding: 0;
  outline: none;
}
.sp-doc-track-wrap {
  position: relative;
  overflow: hidden;
}
.sp-doc-track {
  display: flex;
  flex-direction: row;
  transition: transform 0.25s ease;
  width: 100%;
}
.sp-doc-slide {
  flex: 0 0 100%;
  min-width: 0;
  background: #FFFFFF;
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 4 / 5;
  overflow: hidden;
}
.sp-doc-slide img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.sp-doc-slide[aria-hidden="true"] {
  /* Stays in the flex row but isn't focusable from keyboard navigation.
     Visually it just sits offscreen via the track translation. */
}
.sp-doc-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 36px; height: 36px;
  border-radius: 50%;
  border: 1px solid rgba(0, 0, 0, 0.15);
  background: rgba(255, 255, 255, 0.95);
  color: #333;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
}
.sp-doc-arrow:hover { background: #FFFFFF; }
.sp-doc-arrow:disabled { opacity: 0.3; cursor: not-allowed; }
.sp-doc-arrow--prev { left: 8px; }
.sp-doc-arrow--next { right: 8px; }
.sp-doc-foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 14px;
  background: #FFFFFF;
  font-size: 13px;
  color: #444;
}
.sp-doc-counter {
  font-family: var(--font-mono);
  font-size: 12px;
  color: #666;
}
.sp-doc-name {
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 75%;
}
.sp-doc-dots {
  display: flex;
  gap: 6px;
  justify-content: center;
  padding: 6px 12px 12px;
  background: #FFFFFF;
  flex-wrap: wrap;
}
.sp-doc-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  border: 0;
  background: rgba(0, 0, 0, 0.2);
  padding: 0;
  cursor: pointer;
  transition: background 0.15s ease, transform 0.15s ease;
}
.sp-doc-dot:hover { background: rgba(0, 0, 0, 0.4); }
.sp-doc-dot.is-current {
  background: #0A66C2;
  transform: scale(1.3);
}

/* ── Asset Library drag-and-drop ──────────────────────────────────────────
   The overlay covers the whole viewport while a drag is over the page;
   the progress card pins to the bottom-right while uploads run. Both are
   created lazily by /public/js/asset-drop.js. Pointer-events: none on the
   overlay so the page underneath still receives the dragover events that
   keep our depth counter accurate. */
/* ── Confirm dialog ──────────────────────────────────────────────────────
   Branded replacement for window.confirm(). Lives at the body level so
   it can sit on top of any layout (mx2 dark or client light) without
   being clipped by overflow: hidden containers. */
/* Confirm + prompt dialog — restyled 2026-05-22 to use the design
   system glass look (cream paper card, gold-tinted border, layered
   shadow). Same chrome as the .glass-modal pattern; we keep the
   .mx2-confirm class names so all existing callers + the data-confirm
   form interception still wire up unchanged. */
.mx2-confirm-backdrop {
  position: fixed;
  inset: 0;
  z-index: 2000;
  /* Soft tint so the page behind dims but the frosted panel can do
     the heavy lifting. The .mx2-confirm itself runs the blur. */
  background: rgba(15, 12, 8, 0.42);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  opacity: 0;
  transition: opacity 140ms ease-out;
}
.mx2-confirm-backdrop[hidden] { display: none; }
.mx2-confirm-backdrop--visible { opacity: 1; }
.mx2-confirm {
  /* Frosted cream — semi-transparent so the canvas behind shows
     through the backdrop-filter blur. This is the same recipe as
     .glass-modal / .move-folder-modal and is what gives the dialog
     its "frosted plate" look. (Drew, 2026-05-22 — the previous
     opaque cream wasn't reading as glass.) */
  background: rgba(255, 252, 244, 0.78);
  backdrop-filter: blur(24px) saturate(1.3);
  -webkit-backdrop-filter: blur(24px) saturate(1.3);
  color: var(--ink, #1A1A1A);
  border: 1px solid rgba(200, 169, 94, 0.28);
  border-radius: 14px;
  box-shadow:
    0 30px 80px rgba(40, 28, 12, 0.32),
    0 10px 30px rgba(40, 28, 12, 0.14),
    inset 0 1px 0 rgba(255, 255, 255, 0.6);
  max-width: 480px;
  width: 100%;
  padding: 28px 30px 22px;
  font-family: var(--font-sans);
  transform: translateY(-6px);
  transition: transform 160ms ease-out;
  position: relative;
}
/* Subtle gold-hairline ring just inside the border — matches the
   .glass.ringed treatment from the design system. */
.mx2-confirm::before {
  content: '';
  position: absolute;
  inset: 1px;
  border-radius: 13px;
  pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(200, 169, 94, 0.10);
}
.mx2-confirm-backdrop--visible .mx2-confirm { transform: translateY(0); }
.mx2-confirm__title {
  font-family: var(--font-display, var(--font-sans));
  font-size: 22px;
  font-weight: 500;
  letter-spacing: 0.005em;
  margin: 0 0 10px;
  color: var(--ink, #1A1A1A);
}
.mx2-confirm__message {
  font-size: 14px;
  line-height: 1.55;
  color: rgba(40, 28, 12, 0.62);
  margin: 0 0 22px;
  white-space: pre-line;
}
.mx2-confirm__actions {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 12px;
}
/* Destructive confirm button — solid rust on the cream surface,
   white text per Drew's button rule. Inherits btn / btn-tertiary
   sizing/typography from base. */
.mx2-confirm__actions .btn.danger,
.mx2-confirm__actions .btn-tertiary.danger {
  background: var(--mx2-rust, #b14a3a);
  color: #FFFFFF;
  border-color: var(--mx2-rust, #b14a3a);
  box-shadow: 0 4px 12px rgba(177, 74, 58, 0.24);
}
.mx2-confirm__actions .btn.danger:hover,
.mx2-confirm__actions .btn-tertiary.danger:hover {
  background: #9a3b2c;
  border-color: #9a3b2c;
}

/* In-page hint near the page hero — sets expectations for the drag-
   and-drop ingest before the user starts dragging. Subtle so it
   doesn't compete with the gallery grid below. */
.asset-drop-hint {
  font-size: 13px;
  line-height: 1.55;
  margin: 12px 0 0;
  max-width: 760px;
}
.asset-drop-hint code {
  font-family: var(--font-mono);
  font-size: 12px;
  background: rgba(255, 255, 255, 0.06);
  padding: 1px 5px;
  border-radius: 2px;
}
body.client-side .asset-drop-hint code {
  background: rgba(0, 0, 0, 0.05);
}

.asset-drop-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(15, 15, 15, 0.78);
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

/* Duplicate-filename prompt for the asset-library upload flow. Sits
   above the drop overlay (z-index 1100) so a drag-and-drop dialog
   never gets obscured. Paper-card surface to match the rest of the
   marketing-side UI. */
.asset-dupe-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1100;
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms ease;
}
.asset-dupe-modal-backdrop.is-visible {
  opacity: 1;
  pointer-events: auto;
}
.asset-dupe-modal {
  background: var(--mx2-paper, #f5efe3);
  color: var(--mx2-stone-800, #1a1a1a);
  border-radius: 10px;
  width: min(480px, 92vw);
  display: flex;
  flex-direction: column;
  box-shadow: 0 14px 44px rgba(0, 0, 0, 0.4);
}
.asset-dupe-modal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px 6px;
}
.asset-dupe-modal__title {
  margin: 0;
  font-size: 16px;
  letter-spacing: 0.02em;
}
.asset-dupe-modal__close {
  background: transparent;
  border: none;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
  color: var(--mx2-text-mid, #5e5042);
}
.asset-dupe-modal__body { padding: 4px 18px 8px; }
.asset-dupe-modal__filename {
  margin: 0 0 6px;
  font-size: 14px;
  word-break: break-all;
}
.asset-dupe-modal__filename code,
.asset-dupe-modal__hint code,
.asset-dupe-modal__list code {
  background: rgba(159, 136, 108, 0.12);
  padding: 2px 6px;
  border-radius: 4px;
  font-family: 'Courier New', monospace;
  font-size: 13px;
}
.asset-dupe-modal__list {
  list-style: none;
  margin: 8px 0 0;
  padding: 0 0 0 0;
  max-height: 140px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
}
.asset-dupe-modal__list li { line-height: 1.4; }
.asset-dupe-modal__hint { margin: 0; }
.asset-dupe-modal__apply {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 18px 0;
  font-size: 13px;
  color: var(--mx2-text-mid, #5e5042);
  cursor: pointer;
}
.asset-dupe-modal__apply input[type="checkbox"] { accent-color: var(--mx2-gold-deep, #876e51); }
.asset-dupe-modal__foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 18px 16px;
  flex-wrap: wrap;
}
.asset-drop-overlay__panel {
  border: 3px dashed var(--mx2-gold, #c9a86a);
  border-radius: 18px;
  padding: 56px 80px;
  text-align: center;
  color: #FFFFFF;
  background: rgba(0, 0, 0, 0.35);
  max-width: 720px;
}
.asset-drop-overlay__icon {
  color: var(--mx2-gold-light, #d4af6e);
  display: inline-flex;
  margin-bottom: 14px;
}
.asset-drop-overlay__title {
  font-family: var(--font-display, var(--font-sans));
  font-size: 36px;
  font-weight: 600;
  margin-bottom: 8px;
}
.asset-drop-overlay__sub {
  font-family: var(--font-mono);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: rgba(255, 255, 255, 0.7);
}

/* Upload queue panel (Google-Drive-style). Pinned bottom-right; one
   row per task. Header shows item count, subtitle shows aggregate ETA
   + a Cancel link, list shows file-icon + filename + circular progress
   per row. Compact + clean — per-row detail (bytes, percent, ETA) lives
   in the row's title attribute (hover tooltip). */
/* Upload progress panel — restyled 2026-05-22 to match the
   design system toast pattern Drew shared (rounded card, color-
   coded icon block on the left, bold title + muted subtitle,
   action button on the right). */
.asset-drop-panel {
  position: fixed;
  right: 24px;
  bottom: 24px;
  z-index: 999;
  width: 420px;
  max-width: calc(100vw - 32px);
  background: rgba(255, 252, 244, 0.98);
  border: 1px solid rgba(200, 169, 94, 0.28);
  border-radius: 14px;
  box-shadow:
    0 18px 40px rgba(40, 28, 12, 0.18),
    0 4px 12px rgba(40, 28, 12, 0.06),
    inset 0 1px 0 rgba(255, 255, 255, 0.6);
  font-family: var(--font-sans);
  color: var(--ink, #1A1A1A);
  overflow: hidden;
}
.asset-drop-panel[hidden] { display: none; }

.asset-drop-panel__head {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  cursor: pointer;
  user-select: none;
}
.asset-drop-panel--collapsed .asset-drop-panel__head { border-bottom: 0; }
/* Color-coded icon block — info-blue for upload activity. Matches
   the design system glass-toast left affordance. */
.asset-drop-panel__icon {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 8px;
  background: #1d6fbf;
  color: #ffffff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow:
    0 4px 10px rgba(29, 111, 191, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.18);
}
/* Failure variant flips the block to rust — applied via JS when the
   panel reaches an all-errored terminal state. */
.asset-drop-panel--error .asset-drop-panel__icon {
  background: #b14a3a;
  box-shadow:
    0 4px 10px rgba(177, 74, 58, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.18);
}
/* Success variant (everything done, no errors) — green. */
.asset-drop-panel--done .asset-drop-panel__icon {
  background: #2e8b57;
  box-shadow:
    0 4px 10px rgba(46, 139, 87, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.18);
}
.asset-drop-panel__main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.asset-drop-panel__title {
  font-size: 14.5px;
  font-weight: 600;
  letter-spacing: 0.005em;
  color: var(--ink, #1A1A1A);
}
.asset-drop-panel__eta {
  font-size: 12px;
  font-family: var(--font-mono);
  color: rgba(40, 28, 12, 0.55);
}
.asset-drop-panel__actions {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}
.asset-drop-panel__cancel {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 6px 10px;
  cursor: pointer;
  font-family: inherit;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mx2-gold-deep, #876e51);
  border-radius: 4px;
}
.asset-drop-panel__cancel:hover {
  background: rgba(200, 169, 94, 0.12);
  color: var(--mx2-gold-deep, #876e51);
}
.asset-drop-panel__chev,
.asset-drop-panel__close {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  width: 28px;
  height: 28px;
  border-radius: 6px;
  cursor: pointer;
  color: rgba(40, 28, 12, 0.55);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 120ms ease, color 120ms ease, transform 180ms ease;
}
.asset-drop-panel__chev:hover,
.asset-drop-panel__close:hover {
  background: rgba(40, 28, 12, 0.06);
  color: var(--ink, #1A1A1A);
}
.asset-drop-panel--collapsed .asset-drop-panel__chev { transform: rotate(-180deg); }

.asset-drop-panel__list {
  max-height: 320px;
  overflow-y: auto;
  border-top: 1px solid rgba(200, 169, 94, 0.16);
}
.asset-drop-panel--collapsed .asset-drop-panel__list { display: none; }

/* Per-task row — file-type icon + filename + circular progress + × on
   hover, with a status line directly underneath showing the task's
   current state ("Extracting & building gallery…", "78% · 432 MB of
   916 MB · 2m 15s left", "Done", etc.). Concurrent uploads each get
   their own line so they never share the panel-level subtitle. */
.asset-drop-task {
  padding: 8px 14px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.04);
  font-size: 13px;
}
.asset-drop-task:last-child { border-bottom: 0; }
.asset-drop-task__head {
  display: flex;
  align-items: center;
  gap: 10px;
}
.asset-drop-task__status {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-soft, #5E5E5E);
  margin-top: 4px;
  margin-left: 38px;            /* aligns under the filename, not the icon */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.asset-drop-task--processing .asset-drop-task__status {
  /* Soft pulse during the server-side phase so it's clear work is
     still happening even though no bytes are flowing. */
  animation: asset-drop-pulse 1.6s ease-in-out infinite;
}
@keyframes asset-drop-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}
.asset-drop-task--done .asset-drop-task__status { color: #4a7c59; }
.asset-drop-task--error .asset-drop-task__status { color: #b8553e; }
.asset-drop-task--cancelled .asset-drop-task__status { color: var(--ink-soft, #5E5E5E); }
.asset-drop-task__icon {
  flex-shrink: 0;
  display: inline-flex;
}
.asset-drop-icon {
  width: 28px;
  height: 28px;
  border-radius: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: #FFFFFF;
}
.asset-drop-icon-image  { background: #E8728F; }   /* Google-Drive-ish pink */
.asset-drop-icon-video  { background: #E66B4D; }
.asset-drop-icon-pdf    { background: #DB4437; }
.asset-drop-icon-zip    { background: #6B7A8F; }
.asset-drop-icon-other  { background: #9AA0A6; }

.asset-drop-task__name {
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.asset-drop-task__progress {
  flex-shrink: 0;
  display: inline-flex;
  color: var(--mx2-gold, #c9a86a);
}
.asset-drop-task__cancel {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  cursor: pointer;
  color: var(--ink-soft, #5E5E5E);
  display: none;             /* hidden until row hover */
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: background 120ms ease, color 120ms ease;
}
.asset-drop-task:hover .asset-drop-task__cancel { display: inline-flex; }
.asset-drop-task__cancel:hover {
  background: rgba(0, 0, 0, 0.06);
  color: var(--ink, #1A1A1A);
}
.asset-drop-task:hover .asset-drop-task__progress { display: none; }
.asset-drop-task--done   .asset-drop-task__progress,
.asset-drop-task--error  .asset-drop-task__progress,
.asset-drop-task--cancelled .asset-drop-task__progress {
  /* Once a row reaches a terminal state, the icon stays visible even
     on hover — no point showing a cancel × on a finished row. */
  display: inline-flex !important;
}
.asset-drop-task--done .asset-drop-task__cancel,
.asset-drop-task--error .asset-drop-task__cancel,
.asset-drop-task--cancelled .asset-drop-task__cancel { display: none !important; }

.asset-drop-circle--done { color: #4a7c59; }
.asset-drop-circle--error { color: #b8553e; }
.asset-drop-circle--cancelled { color: var(--ink-soft, #5E5E5E); }
.asset-drop-circle--processing {
  animation: asset-drop-rotate 2s linear infinite;
}
@keyframes asset-drop-rotate {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* ── Discussions ────────────────────────────────────────────────────────── */

/* @-mention autocomplete dropdown — single shared node attached to
   body, positioned below the active textarea via inline styles set
   by discussions.js. Buttons inside are full-width so the user can
   click anywhere on the row. */
.discussion-mention-dropdown {
  z-index: 10200;
  background: #1a1a1a;
  border: 1px solid var(--mx2-rule, #2a2a2a);
  border-radius: 4px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  max-height: 280px;
  overflow-y: auto;
  padding: 4px;
}
.discussion-mention-dropdown[hidden] { display: none; }
.discussion-mention-item {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  background: transparent;
  border: 0;
  border-radius: 3px;
  color: #f4ead3;
  text-align: left;
  font-family: var(--font-sans);
  font-size: 13px;
  cursor: pointer;
}
.discussion-mention-item:hover,
.discussion-mention-item.is-active {
  background: rgba(244, 234, 211, 0.08);
}
/* Avatar sits inside the dark dropdown — give the fallback a tinted
   background that reads on the near-black surface. */
.discussion-mention-item__avatar {
  flex: none;
  background: rgba(244, 234, 211, 0.08);
}
.discussion-mention-item__avatar .avatar-fallback {
  background: rgba(244, 234, 211, 0.12);
  color: #f4ead3;
}
.discussion-mention-item__name { font-weight: 500; }
.discussion-mention-item__token {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--mx2-gold, #c9a86a);
}
.discussion-mention-item__type {
  margin-left: auto;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: rgba(244, 234, 211, 0.55);
}

/* @-mention token inside a rendered message body. Gold to match the
   composer dropdown's token color; weight is slight bump so it
   reads as a mention without shouting. */
.discussion-mention-token {
  color: var(--mx2-gold, #c9a86a);
  font-weight: 500;
}

/* ── @-mention inline chip in the composer textarea ───────────────────
   Mirror-div overlay technique: a transparent-text div sits behind
   the textarea (positioned identically via setupMentionMirror in
   discussions.js) and paints a soft gold background under each
   recognized @token. The textarea's own background is forced
   transparent so the chip color shows through.

   The mirror's text color is transparent so only the chip
   backgrounds are visible — actual text comes from the live
   textarea on top. Caret behaves natively (textarea is the
   real input), no contenteditable workaround needed. */
.mention-overlay-wrap {
  position: relative;
  display: block;
}
.mention-mirror {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  color: transparent;
  background: transparent;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  word-wrap: break-word;
  box-sizing: border-box;
  /* font/padding/border are copied from the textarea at runtime
     to keep glyph positions in lock-step. */
}
.mention-mirror .mention-chip {
  /* Soft gold pill that reads on both cream (client) and near-black
     (admin) surfaces. Negative inline margin pulls the background
     a touch beyond the token glyphs so it visibly hugs the @text. */
  background: rgba(200, 154, 60, 0.30);
  border-radius: 3px;
  padding: 0 2px;
  margin: 0 -2px;
  color: transparent;
}
/* Textarea on top of the mirror — kill its background so the chip
   colors paint through. The wrap-class qualifier means only opted-in
   composers get the override; other textareas keep their default
   backgrounds. */
.mention-overlay-wrap .mention-overlay-textarea {
  position: relative;
  background-color: transparent !important;
}

/* ── Idle-logout warning modal ───────────────────────────────────────────
   Pops up ~60s before the rolling session cookie expires so the user
   can extend with one click. Same overlay pattern as the other modals
   in this codebase (fixed inset + cream card on top). */
.idle-logout-modal {
  position: fixed;
  inset: 0;
  z-index: 240;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  animation: modal-fade-in 120ms ease-out;
}
.idle-logout-modal[hidden] { display: none; }
.idle-logout-modal__card {
  width: min(420px, 100%);
  background: var(--bg-card, #FFFFFF);
  color: var(--ink, #1A1A1A);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 22px 24px;
  box-shadow: 0 24px 48px rgba(0, 0, 0, 0.30);
  animation: modal-pop 140ms ease-out;
}
.idle-logout-modal__title {
  margin: 0 0 8px;
  font-size: 18px;
  font-weight: 600;
}
.idle-logout-modal__lead {
  margin: 0 0 18px;
  font-size: 14px;
  line-height: 1.5;
}
.idle-logout-modal__actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
body.has-idle-modal-open { overflow: hidden; }

/* ── Impersonation banner ──────────────────────────────────────────────
   Shown across every page while a Mx2 admin is impersonating a client.
   Loud on purpose — the whole point is that the admin (and anyone
   shoulder-surfing) cannot forget they are operating as someone else.
   Sits above all content but below modals so an open modal can still
   be closed without the banner trapping focus. */
.impersonation-banner {
  position: sticky;
  top: 0;
  z-index: 180;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 18px;
  background: var(--mx2-rust, #9a3b1d);
  color: #FFFFFF;
  font-size: 13px;
  line-height: 1.3;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.22);
}
.impersonation-banner__dot {
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: #FFFFFF;
  flex-shrink: 0;
  animation: impersonate-pulse 1.6s ease-in-out infinite;
}
@keyframes impersonate-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}
.impersonation-banner__text { flex: 1; min-width: 0; }
.impersonation-banner__sub {
  margin-left: 8px;
  font-size: 12px;
  opacity: 0.85;
}
.impersonation-banner__form { margin: 0; }
.impersonation-banner__return {
  background: rgba(255, 255, 255, 0.18);
  color: #FFFFFF;
  border: 1px solid rgba(255, 255, 255, 0.45);
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.impersonation-banner__return:hover {
  background: rgba(255, 255, 255, 0.28);
  border-color: #FFFFFF;
}
@media (max-width: 600px) {
  .impersonation-banner__sub { display: none; }
}

/* ── Gallery-download status modal ─────────────────────────────────────
   Pops when a client clicks "Download gallery" — overlay + card + a
   small spinner. The browser is downloading in the background; this
   is just feedback so the user doesn't keep hammering the button.
   Same overlay treatment as the other modals in this codebase. */
.gallery-download-modal {
  position: fixed;
  inset: 0;
  z-index: 220;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  animation: modal-fade-in 120ms ease-out;
}
.gallery-download-modal[hidden] { display: none; }
.gallery-download-modal__card {
  width: min(440px, 100%);
  background: var(--bg-card, #FFFFFF);
  color: var(--ink, #1A1A1A);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 22px 24px 20px;
  box-shadow: 0 24px 48px rgba(0, 0, 0, 0.30);
  animation: modal-pop 140ms ease-out;
  text-align: center;
}
.gallery-download-modal__spinner {
  width: 32px;
  height: 32px;
  margin: 0 auto 14px;
  border-radius: 999px;
  border: 3px solid rgba(159, 136, 108, 0.25);
  border-top-color: var(--mx2-gold-deep, #876e51);
  animation: gallery-download-spin 0.9s linear infinite;
}
@keyframes gallery-download-spin {
  to { transform: rotate(360deg); }
}
.gallery-download-modal__title {
  margin: 0 0 8px;
  font-size: 17px;
  font-weight: 600;
}
.gallery-download-modal__lead {
  margin: 0 0 14px;
  font-size: 14px;
  line-height: 1.5;
  text-align: left;
}
.gallery-download-modal__tips {
  list-style: disc;
  padding-left: 20px;
  margin: 0 0 18px;
  text-align: left;
}
.gallery-download-modal__tips li { margin-bottom: 4px; }
.gallery-download-modal__actions {
  display: flex;
  justify-content: flex-end;
}

/* ── Brand chips (brand-chips.js) ─────────────────────────────────────────
   Multi-value brand/manufacturer input (migration 0116). Used inside the
   asset preview modal's product-tags panel (on a dark #0F0F0F surface) AND
   in the rapid-entry product grid (on light cells). A white bordered box
   with dark ink so it reads in both contexts; chips are gold-tinted pills. */
.bchips {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px;
  padding: 4px 6px; border: 1px solid rgba(0, 0, 0, 0.18); border-radius: 6px;
  background: #fff; min-height: 34px; position: relative; cursor: text;
}
.bchips__list { display: contents; }
.bchips__chip {
  display: inline-flex; align-items: center; gap: 4px;
  background: rgba(184, 134, 47, 0.14); color: #1a1a1a;
  border: 1px solid rgba(184, 134, 47, 0.38); border-radius: 4px;
  padding: 1px 4px 1px 7px; font-size: 12px; line-height: 1.6; max-width: 100%;
}
.bchips__chip-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bchips__chip-x {
  border: none; background: transparent; color: #8a6d28; font-size: 14px;
  line-height: 1; cursor: pointer; padding: 0 2px;
}
.bchips__chip-x:hover { color: #5a4718; }
.bchips__input {
  flex: 1 1 90px; min-width: 90px; border: none; outline: none;
  background: transparent; color: #1a1a1a; font: inherit; font-size: 13px; padding: 2px;
}
.bchips__input::placeholder { color: #8a8a8a; }
.bchips__menu {
  position: absolute; z-index: 60; left: 0; right: 0; top: 100%; margin-top: 2px;
  background: #fff; color: #1f1f1f; border: 1px solid rgba(0, 0, 0, 0.18);
  border-radius: 6px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.22);
  overflow: hidden; max-height: 200px; overflow-y: auto;
}
.bchips__opt {
  display: block; width: 100%; text-align: left; padding: 7px 9px; border: none;
  background: transparent; cursor: pointer; font: inherit; font-size: 13px; color: #1f1f1f;
}
.bchips__opt:hover, .bchips__opt.is-active { background: rgba(184, 134, 47, 0.18); }
/* Grid cell: let the chips box fill the brand column. */
.pgrid__brandwrap .bchips { flex: 1; }
