/* ═══════════════════════════════════════════════
   Slopex — Shared Styles
   Used by index.html and dashboard.html
   ═══════════════════════════════════════════════ */

:root {
  /* old hex palette — kept for quick rollback never delete these old values */
  --bg-old: #000000;
  --bg-card-old: #111111;
  --bg-card-hover-old: #181818;
  --bg-elevated-old: #141414;
  --text-old: #ffffff;
  --text-secondary-old: #999999;
  --text-muted-old: #555555;
  --border-old: rgba(255,255,255,0.08);
  --border-hover-old: rgba(255,255,255,0.18);

  --bg: hsl(0, 0%, 0%);
  --bg-card: hsl(0, 0%, 8%);
  --bg-card-hover: hsl(0, 0%, 11%);
  --bg-raised: hsl(0, 0%, 11%);
  --bg-raised-hover: hsl(0, 0%, 14%);
  --bg-elevated: hsl(0, 0%, 14%);
  --white: #ffffff;
  --text: hsl(0, 0%, 100%);
  --text-secondary: hsl(0, 0%, 60%); /* never change this row, keep at 60% */
  --text-muted: hsl(0, 0%, 40%); /* 40 percent original value */
  --border: hsla(0, 0%, 100%, 0.08);
  --border-hover: hsla(0, 0%, 100%, 0.21);
  --border-strong: hsla(0, 0%, 100%, 0.18); /* same value as --border-hover, but for static prominent borders (outline buttons, checkbox edges) */
  --divider: hsl(0, 0%, 22%); /* DO NOT USE unless Finn explicitly asks — opt-in only, never the default for new dividers (use --border instead). Current call sites are intentional one-offs. */

  /* ── HOVER OVERLAYS ──
     Translucent white tints applied to :hover backgrounds. Composable across
     any parent surface — the overlay nudges whatever's underneath instead of
     committing to a solid color. */
  --hover-soft:   rgba(255,255,255,0.04);  /* base — rows, secondary buttons, dropdown items */
  --hover-med:    rgba(255,255,255,0.06);  /* mid — sidebar items, modal close, menu items */
  --hover-strong: rgba(255,255,255,0.08);  /* strong — primary CTAs, copy buttons, prominent emphasis */

  /* ── STATUS COLORS ── */
  --success:       #22c55e;
  --success-soft:  rgba(34,197,94,0.12);
  --warning:       #f59e0b;
  --warning-soft:  rgba(245,158,11,0.12);
  --danger:        #ef4444;
  --danger-soft:   rgba(239,68,68,0.1);
  --info:          #3b82f6;
  --info-soft:     rgba(59,130,246,0.12);
  --planned:       #a855f7;
  --planned-soft:  rgba(168,85,247,0.12);

  /* ── MOTION ── */
  --ease-out:      cubic-bezier(0.22, 1, 0.36, 1);

  /* ── BLUR ── */
  --blur:          blur(24px);
  --blur-soft:     blur(12px);

  /* ── RADII ── */
  --radius-xs:   8px;
  --radius-sm:   14px;
  --radius:      22px;   /* cards */
  --radius-lg:   30px;
  --radius-pill: 9999px; /* buttons, badges, pill inputs — size-agnostic */

  /* ── SPACING ── */
  --space-1:  4px;
  --space-2:  8px;
  --space-3:  12px;
  --space-4:  16px;
  --space-5:  20px;
  --space-6:  24px;
  --space-8:  32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;

  /* ── FONT WEIGHTS ──
     Six numeric weights matching the Sora subset loaded from Google Fonts
     (300/400/500/600/700/800). Keep names matching standard CSS keywords
     so call sites stay self-documenting. */
  --weight-light:     300;   /* rarely used — large display only */
  --weight-regular:   400;   /* body prose, long-form copy */
  --weight-medium:    500;   /* secondary labels, meta lines */
  --weight-semibold:  600;   /* default UI labels, buttons, row titles */
  --weight-bold:      700;   /* headings, pill text, emphasis */
  --weight-extrabold: 800;   /* hero/display, marketing headlines */

  /* ── TYPOGRAPHY (sub-1rem scale) ── */
  --text-2xs:  0.68rem;   /* sidebar section labels, micro pills */
  --text-xs:   0.72rem;   /* pills, micro-labels */
  --text-sm:   0.78rem;   /* captions, helper text, secondary meta */
  --text-md:   0.82rem;   /* default body in dense UI (dashboard/admin rows) */
  --text-base: 0.85rem;   /* buttons, standard labels */
  --text-lg:   0.88rem;   /* titles inside cards, summary lines */
  --text-xl:   0.95rem;   /* row/card titles, checkout totals, price suffix */

  /* ── MOTION DURATIONS ── */
  --motion-fast: 0.15s;   /* hover, color/bg swaps */
  --motion-base: 0.2s;    /* default UI transitions */
  --motion-slow: 0.35s;   /* larger layout shifts, modals */

  /* ── Z-INDEX SCALE ──
     Semantic layers from background decoration up to the page-load gate.
     Nav (100) and sidebar (101) are load-bearing — modals nested inside
     .content-frame render below them (see CLAUDE.md tripwire). The modal
     ladder is capped at 4 (max simultaneous stacked modals in the app).
     Tooltip arrow uses calc(var(--z-tooltip) + 1). */
  --z-behind:        -1;     /* decorative bgs, blur layers behind content */
  --z-raised:        1;      /* content-frame, sticky table headers, row-relative children */
  --z-raised-2:      2;      /* content-fade strips above the content-frame */
  --z-sticky:        10;     /* page-local sticky, dropdowns, popovers */
  --z-fade-mask:     90;     /* fog/fade gradient overlays under nav */
  --z-nav:           100;    /* fixed top nav */
  --z-sidebar:       101;    /* glass sidebar — must beat nav */
  --z-modal:         200;    /* base modal/overlay */
  --z-modal-2:       220;    /* 2nd modal stacked */
  --z-modal-3:       240;    /* 3rd modal stacked */
  --z-modal-4:       260;    /* 4th modal stacked (max) */
  --z-tooltip:       1000;   /* canonical tooltip body */
  --z-page-loader:   99999;  /* page-load gate over everything */

  /* ── TOOLTIP DELAY ──
     `--tooltip-delay` is the active value the canonical tooltip transition
     reads. Default = customer-facing (500ms). Admin surfaces opt in to the
     slower admin tier by setting `body.admin-ui` (or any scope) — heavier
     hover discovery on dense admin UI. */
  --tooltip-delay:        500ms;
  --tooltip-delay-admin:  1000ms;

  /* ── ELEVATION / SHADOWS ── */
  --shadow-sm: 0 4px 12px rgba(0,0,0,0.35);
  --shadow-md: 0 6px 20px rgba(0,0,0,0.6);
  --shadow-lg: 0 20px 60px rgba(0,0,0,0.7);
  --shadow-dropdown: 0 8px 24px rgba(0,0,0,0.4);
}

/* ── STATUS PILL (shared base) ──
   ONE pill family for every status/state indicator across the app. The
   geometry comes from this base; color comes from a `--<severity>` modifier
   (or a page-local color override class like .fb-status / .status-badge).

   Variants:
   - `.has-dot`         — adds a leading 6×6px dot in currentColor and
                          tightens the left padding to 10px.
   - `.is-transparent`  — drops the background fill (the pill outline still
                          claims its space) so the pill can sit inline with
                          neighboring row text (e.g. "ACTIVE  9,293 runs"
                          on the dashboard's automation rows). When combined
                          with .has-dot the dot bumps to 7×7px since it's
                          doing more of the visual lifting without a fill
                          behind it.
   - `.pending`         — 50% opacity for transient/skeleton states.

   Text-nudge wrapper:
   - Labels should be wrapped in `<span class="sp-text">…</span>`. This is
     a transparent inline-block that adds 1px of top padding (2px when the
     pill has a dot) for optical centering. Without the wrapper the text
     sits at the mathematical center, which reads as ~0.5–1px high for
     uppercase status labels.

   Markup (filled, no dot):
     <span class="status-pill status-pill--success">
       <span class="sp-text">Success</span>
     </span>

   Markup (filled, with dot):
     <span class="status-pill has-dot status-pill--info">
       <span class="sp-dot"></span>
       <span class="sp-text">In Progress</span>
     </span>

   Markup (transparent, for inline-with-text contexts):
     <span class="status-pill has-dot is-transparent status-pill--success">
       <span class="sp-dot"></span>
       <span class="sp-text">Active</span>
     </span>
*/
.status-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 26px;
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1;
  padding: 0 14px;
  white-space: nowrap;
  /* Smooth color transitions when a pill changes severity (e.g. Active →
     Paused). CSS only animates on property changes, so initial renders
     aren't affected. */
  transition: background-color 0.2s ease, color 0.2s ease, opacity 0.2s ease;
}

/* Color modifiers — apply on top of .status-pill. */
.status-pill--success { background: var(--success-soft); color: var(--success); }
.status-pill--warning { background: var(--warning-soft); color: var(--warning); }
.status-pill--danger  { background: var(--danger-soft);  color: var(--danger); }
.status-pill--info    { background: var(--info-soft);    color: var(--info); }
.status-pill--planned { background: var(--planned-soft); color: var(--planned); }
.status-pill--neutral { background: rgba(255,255,255,0.06); color: var(--text-secondary); }
.status-pill--muted   { background: rgba(255,255,255,0.04); color: var(--text-muted); }

/* Text wrapper — optical nudge so uppercase labels don't sit visually high. */
.status-pill .sp-text {
  display: inline-block;
  padding-top: 1px;
}
.status-pill.has-dot .sp-text {
  padding-top: 2px;
}

/* Leading dot — 6×6px in currentColor, sits before the label. Rendered via
   a real `<span class="sp-dot"></span>` child (not a pseudo) so `::before`
   stays free for `[data-tooltip]`'s arrow when both apply to the same pill. */
.status-pill.has-dot {
  padding-left: 10px;
}
.status-pill > .sp-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
  transition: background-color 0.2s ease;
}

/* Transparent variant — invisible pill, same hitbox + same dot placement. */
.status-pill.is-transparent {
  background: transparent;
}
.status-pill.has-dot.is-transparent > .sp-dot {
  width: 7px;
  height: 7px;
}

/* Pending / loading state. */
.status-pill.pending {
  opacity: 0.5;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scrollbar-width: thin;
  scrollbar-color: #333 transparent;
  scrollbar-gutter: stable;
}

body {
  font-family: 'Sora', sans-serif;
  background: var(--bg);
  color: var(--text);
  line-height: 1.6;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

/* ── NAV ── */
nav {
  position: fixed; top: 0; width: 100%; z-index: var(--z-nav);
  padding: 20px 40px;
  display: flex; justify-content: space-between; align-items: center;
  background: rgba(0, 0, 0, 0.7);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  border-bottom: 1px solid var(--border);
  transition: background 0.3s ease;
}

.logo {
  font-size: 1.6rem;
  line-height: 36px;
  font-weight: var(--weight-extrabold);
  letter-spacing: -0.04em;
  color: var(--white);
  text-decoration: none;
}
.logo span {
  color: var(--text-secondary);
  font-weight: var(--weight-extrabold);
}

.nav-links { display: flex; gap: 32px; align-items: center; }
.nav-links a {
  color: var(--text-secondary);
  text-decoration: none;
  font-size: 0.85rem;
  font-weight: var(--weight-medium);
  letter-spacing: -0.01em;
  transition: color 0.2s;
}
.nav-links a:hover { color: var(--white); }

.nav-signin {
  color: var(--white) !important;
  font-weight: var(--weight-semibold) !important;
}

.nav-cta {
  padding: 10px 24px !important;
  background: var(--white) !important;
  color: var(--bg) !important;
  border-radius: var(--radius-pill);
  font-weight: var(--weight-bold) !important;
  font-size: 0.85rem !important;
  transition: all 0.25s ease !important;
}
.nav-cta:hover {
  opacity: 0.85;
  transform: translateY(-1px);
}

/* ── BUTTONS ── */
.btn-primary {
  padding: 16px 36px;
  background: var(--white);
  color: var(--bg);
  border: none; border-radius: var(--radius-pill);
  font-size: 0.9rem;
  font-weight: var(--weight-bold);
  cursor: pointer;
  transition: all 0.25s ease;
  text-decoration: none;
  display: inline-flex; align-items: center; gap: 8px;
  font-family: 'Sora', sans-serif;
  letter-spacing: -0.02em;
}
.btn-primary:hover {
  opacity: 0.85;
  transform: translateY(-2px);
}

.btn-secondary {
  padding: 16px 36px;
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  font-size: 0.9rem;
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: all 0.25s ease;
  text-decoration: none;
  font-family: 'Sora', sans-serif;
  letter-spacing: -0.02em;
}
.btn-secondary:hover {
  border-color: var(--white);
  background: var(--hover-soft);
}
.btn-sm {
  height: 40px;
  padding: 0 22px;
  font-size: var(--text-md);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* ── ACTION BUTTON ──
   Full-width modal CTA. Variants:
     .primary — raised surface, used for confirm/submit
     .danger  — destructive (remove/delete)
   Pair with .action-btn always; modifier alone has no styling. */
.action-btn {
  width: 100%;
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  font-family: 'Sora', sans-serif;
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  cursor: pointer;
  transition: background 0.2s, border-color 0.2s, color 0.2s;
  text-align: center;
}
.action-btn.primary {
  background: var(--bg-raised);
  border: 1px solid var(--border);
  color: var(--white);
}
.action-btn.primary:hover {
  background: var(--bg-raised-hover);
  border-color: rgba(255,255,255,0.3);
}
.action-btn.danger {
  background: none;
  border: 1px solid rgba(239,68,68,0.2);
  color: rgba(239,68,68,0.7);
}
.action-btn.danger:hover {
  background: rgba(239,68,68,0.08);
  border-color: rgba(239,68,68,0.4);
  color: var(--danger);
}
.action-btn:disabled { opacity: 0.4; }

/* ── FORM ELEMENTS ── */
.form-group { display: flex; flex-direction: column; gap: 6px; }

.form-group label {
  font-size: 0.78rem;
  font-weight: var(--weight-semibold);
  color: var(--text-secondary);
  letter-spacing: 0.01em;
}

.form-group input,
.form-group select,
.form-group textarea {
  padding: 14px 18px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: 'Sora', sans-serif;
  font-size: 0.88rem;
  transition: border-color 0.2s;
  outline: none;
  font-weight: var(--weight-regular);
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  border-color: var(--white);
}

/* Custom select arrow — native browser arrow sits at the far right with
   too much empty space on short options. Mirror the .checkout-select
   pattern so the chevron sits 16px from the edge across the site. */
.form-group select {
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23999' stroke-width='1.5' fill='none'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 16px center;
  padding-right: 40px;
}


/* Unified disabled state for form fields: opacity-based dimming matches
   the rest of the site. Default cursor (no `not-allowed`) — the dimming
   already conveys disabled, and the cursor change reads as punitive. */
.form-group input:disabled,
.form-group select:disabled,
.form-group textarea:disabled {
  opacity: 0.7;
  color: var(--text-muted);
}

.form-group input::placeholder,
.form-group textarea::placeholder {
  color: var(--text-muted);
}

.form-group select { cursor: pointer; }
.form-group select option { background: var(--bg); }
.form-group textarea { resize: vertical; min-height: 100px; }

.form-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; }

.form-submit {
  height: 48px;
  padding: 0 24px;
  background: var(--white);
  color: var(--bg);
  border: none; border-radius: var(--radius-pill);
  font-family: 'Sora', sans-serif;
  font-size: 0.9rem;
  font-weight: var(--weight-bold);
  line-height: 1.6;
  cursor: pointer;
  transition: all 0.25s ease;
  margin-top: 8px;
  letter-spacing: -0.02em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.form-submit:hover {
  opacity: 0.85;
  transform: translateY(-2px);
}
.btn-primary:disabled,
.btn-secondary:disabled,
.form-submit:disabled,
.btn-primary:disabled:hover,
.btn-secondary:disabled:hover,
.form-submit:disabled:hover {
  opacity: 0.5;
  transform: none;
  cursor: default;
}

.form-note {
  font-size: 0.75rem;
  color: var(--text-muted);
  text-align: center;
  font-weight: var(--weight-regular);
}

/* ── TOGGLE SWITCH (canonical) ──
   ONE iOS-style slider for the whole app. 48×26 track, 20px circle, white-on
   when checked, dimmed when disabled. Use this on every settings row and
   any inline on/off control. Do NOT redefine; if a row needs a slight
   vertical nudge (e.g. flex-start–aligned containers), scope `margin-top`
   on the parent rule.

   Markup:
     <label class="toggle-switch">
       <input type="checkbox">
       <span class="toggle-slider"></span>
     </label>

   Modifier:
     .toggle-no-anim — kills the slider transition (used when hydrating a
     row from a server fetch so the knob doesn't visibly slide on first
     paint). Toggle the class off after one tick to restore animation.
*/
.toggle-switch {
  position: relative;
  display: inline-block;
  width: 48px;
  height: 26px;
  flex-shrink: 0;
}
.toggle-switch input { opacity: 0; width: 0; height: 0; }
.toggle-slider {
  position: absolute;
  cursor: pointer;
  inset: 0;
  background: var(--text-muted);
  border-radius: var(--radius-pill);
  transition: background 0.25s;
}
.toggle-slider::before {
  content: '';
  position: absolute;
  height: 20px;
  width: 20px;
  left: 3px;
  top: 3px;
  background: var(--bg);
  border-radius: 50%;
  transition: transform 0.25s;
}
.toggle-switch input:checked + .toggle-slider { background: var(--white); }
.toggle-switch input:checked + .toggle-slider::before { transform: translateX(22px); }
.toggle-switch input:disabled + .toggle-slider { opacity: 0.5; }
.toggle-switch.toggle-no-anim .toggle-slider,
.toggle-switch.toggle-no-anim .toggle-slider::before { transition: none; }

/* ── SETTINGS ROW (canonical) ──
   ONE row pattern for any "title + description + right-aligned control"
   inside a `.settings-card` / `.admin-card`. Top-aligned so 2-line
   descriptions still keep the control next to the title.

   Markup:
     <div class="settings-row">
       <div class="settings-row-info">
         <div class="settings-row-title">Two-factor authentication</div>
         <div class="settings-row-desc">Require a code every sign-in</div>
       </div>
       <label class="toggle-switch">...</label>
     </div>

   Notes:
   - Nested "sub-row" patterns (e.g. dashboard's email-notification sub-rows
     under a disclosure) intentionally keep their own scoped class — they
     have a smaller text scale and tighter padding that doesn't belong in
     the canonical row. */
.settings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 24px 32px;
  border-bottom: 1px solid var(--border);
}
.settings-row:last-child { border-bottom: none; }
.settings-row-info { flex: 1; min-width: 0; }
.settings-row-title {
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  letter-spacing: -0.01em;
  color: var(--white);
  margin-bottom: 4px;
}
.settings-row-desc {
  font-size: var(--text-base);
  color: var(--text-secondary);
  line-height: 1.5;
  font-weight: var(--weight-regular);
}
.settings-row-desc code {
  font-family: 'JetBrains Mono', monospace;
  font-size: var(--text-sm);
  background: rgba(255,255,255,0.06);
  padding: 1px 5px;
  border-radius: 3px;
  color: var(--white);
}

/* ── CRED MODAL (canonical) ──
   Standard small centered overlay+modal used for invites, credential
   capture, confirmations, sub-flows. Per-modal width / padding overrides
   live with their #overlayId in the page that owns them — those use
   higher specificity (`#myOverlay .cred-modal { ... }`) so they win.

   Markup:
     <div class="cred-overlay" id="myOverlay">
       <div class="cred-modal">
         <button class="modal-close" onclick="...">...</button>
         <div class="cred-title">...</div>
         <div class="cred-subtitle">...</div>
         <!-- form body -->
       </div>
     </div>

   Modifiers:
   - .cred-overlay.stacked — z-index 240, sits above another active overlay
   - .cred-header-with-back — flex row pairing a back-arrow with the title block
*/
.cred-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  background: rgba(0,0,0,0.85);
  backdrop-filter: var(--blur-soft);
  -webkit-backdrop-filter: var(--blur-soft);
  display: flex;
  justify-content: center;
  align-items: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s;
}
.cred-overlay.stacked { z-index: var(--z-modal-3); }
.cred-overlay.active {
  opacity: 1;
  pointer-events: auto;
}
.cred-modal {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 36px 36px 32px;
  transform: scale(0.96) translateY(8px);
  transition: transform 0.2s;
  width: 90%;
  max-width: 480px;
  position: relative;
}
.cred-overlay.active .cred-modal {
  transform: scale(1) translateY(0);
}
.cred-header-with-back {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 6px;
}
.cred-header-with-back .cred-title,
.cred-header-with-back .cred-subtitle { margin: 0; padding: 0; }
.cred-title {
  font-size: 1.1rem;
  font-weight: var(--weight-extrabold);
  letter-spacing: -0.02em;
  margin-bottom: 6px;
}
.cred-subtitle {
  font-size: var(--text-lg);
  color: var(--text-secondary);
  margin-bottom: 24px;
  font-weight: var(--weight-regular);
  line-height: 1.4;
}
/* Wraps .cred-title (or .cred-header-with-back) + .cred-subtitle as a unit
   so the title→subtitle gap stays 6px and the subtitle→body gap stays 24px
   across every cred-modal. Wrapper owns the bottom margin; inner subtitle's
   margin is zeroed so scoped overrides don't stack on top. */
.cred-header-group { margin-bottom: 20px; }
.cred-header-group > .cred-subtitle { margin-bottom: 0; }
.cred-header-group > .cred-title,
.cred-header-group > .cred-header-with-back { margin-bottom: 4px; }
.cred-error {
  font-size: var(--text-md);
  color: var(--danger);
  margin-bottom: 12px;
  min-height: 1.2em;
}

/* ── OTP CODE INPUT (canonical) ──
   6-digit code field used by sign-in OTP and the 2FA / sensitive-action
   confirmation modal. Empty-state drops letter-spacing because the
   trailing space pushes content 0.3em left of the cursor center; use
   word-spacing on the placeholder for the same visual gap without the
   offset. */
.form-group .otp-code-input {
  width: 100%;
  height: 52px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  padding: 0 16px;
  font-family: 'Sora', sans-serif;
  font-size: 22px;
  font-weight: var(--weight-bold);
  letter-spacing: 0.6em;
  text-align: center;
  text-indent: 0.3em;
  outline: none;
  transition: border-color 0.15s;
}
.form-group .otp-code-input:focus { border-color: var(--white); }
.form-group .otp-code-input:placeholder-shown {
  letter-spacing: 0;
  word-spacing: 0.4em;
  text-indent: 0;
}
.otp-resend-row {
  margin-top: 14px;
  text-align: center;
  font-size: var(--text-sm);
  color: var(--text-muted);
}
.otp-resend-row button {
  background: none;
  border: none;
  color: var(--text);
  font-family: inherit;
  font-size: inherit;
  font-weight: var(--weight-semibold);
  text-decoration: underline;
  cursor: pointer;
  padding: 0;
  font-variant-numeric: tabular-nums;
}
.otp-resend-row button:disabled {
  color: var(--text-muted);
  text-decoration: none;
}

/* Auth modals — the sign-in modal + the two OTP modals share a tighter
   sub-modal padding + raised background so the OTP layered above
   signinOverlay reads as elevated, and the marketing sign-in reads as
   a focused auth panel rather than a wide cred-form. */
#signinOverlay .cred-modal,
#twoFactorOtpOverlay .cred-modal,
#signinOtpOverlay .cred-modal {
  max-width: 420px;
  padding: 28px 32px;
  background: var(--bg-raised);
}
/* OTP error/divider styling lives in the canonical .form-error / .form-divider
   rules above — both OTP modals use those classes. */

/* ── FILTER DROPDOWN (canonical) ──
   "Click chip → reveal menu with checkboxes/sort options" pattern, used
   by admin's automation-request filters and dashboard's add-connection
   filters. Menu opens below the button, left-aligned by default; add
   `.align-right` to anchor it to the right edge of the button (useful
   when the chip sits near a container's right edge).

   Markup:
     <div class="filter-dropdown">
       <button class="filter-dropdown-btn" onclick="...">Filter
         <svg>...chevron...</svg>
       </button>
       <div class="filter-dropdown-menu">
         <label class="filter-dropdown-item">
           <input type="checkbox"> Option
         </label>
         OR for sort items:
         <div class="filter-dropdown-item filter-dropdown-sort-item active">
           Most popular <span class="filter-dropdown-check">&#10003;</span>
         </div>
       </div>
     </div>

   Modifiers:
   - .filter-dropdown.open → reveals the menu, rotates the chevron
   - .filter-dropdown-menu.align-right (or on the wrap) → right-aligned menu
   - .filter-dropdown-menu-sm → narrower min-width (200px) for short sort lists
*/
.filter-dropdown { position: relative; }
.filter-dropdown-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-family: 'Sora', sans-serif;
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: border-color 0.2s, color 0.2s;
  white-space: nowrap;
}
.filter-dropdown-btn:hover,
.filter-dropdown.open .filter-dropdown-btn {
  border-color: var(--border-hover);
  color: var(--white);
}
.filter-dropdown-btn svg { flex-shrink: 0; transition: transform 0.2s; }
.filter-dropdown.open .filter-dropdown-btn svg { transform: rotate(180deg); }

.filter-dropdown-menu {
  display: none;
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  min-width: 220px;
  background: #111;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 6px 0;
  z-index: var(--z-sticky);
  overflow-y: auto;
}
.filter-dropdown-menu.align-right,
.filter-dropdown.align-right .filter-dropdown-menu { left: auto; right: 0; }
.filter-dropdown-menu::-webkit-scrollbar { width: 4px; }
.filter-dropdown-menu::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 4px; }
.filter-dropdown.open .filter-dropdown-menu { display: block; }
.filter-dropdown-menu-sm { min-width: 200px; }

.filter-dropdown-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  font-size: var(--text-md);
  color: var(--text-secondary);
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
  font-family: 'Sora', sans-serif;
}
.filter-dropdown-item:hover { background: var(--hover-soft); color: var(--white); }
.filter-dropdown-item input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 16px; height: 16px; min-width: 16px;
  padding: 0;
  border: 1.5px solid var(--border-strong);
  border-radius: 4px;
  background: transparent;
  cursor: pointer;
  position: relative;
  transition: all 0.15s;
}
.filter-dropdown-item input[type="checkbox"]:checked {
  background: var(--white);
  border-color: var(--white);
}
.filter-dropdown-item input[type="checkbox"]:checked::after {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  width: 4px; height: 8px;
  border: solid #000;
  border-width: 0 2px 2px 0;
  transform: translate(-50%, -60%) rotate(45deg);
}

.filter-dropdown-check { display: none; color: var(--white); font-size: var(--text-base); margin-left: auto; }
.filter-dropdown-sort-item.active .filter-dropdown-check { display: inline; }
.filter-dropdown-sort-item.active { color: var(--white); }

/* ── SECRET-INPUT WRAP + EYE TOGGLE (canonical) ──
   Reveal-on-click eye button anchored to the right edge of a credential-style
   input. Use for any password / API key / OAuth secret field. The input gets
   `padding-right: 44px` so its text never collides with the eye button.

   Markup:
     <div class="secret-input-wrap">
       <input type="password" placeholder="Slack API key">
       <button type="button" class="secret-input-toggle" aria-label="Show value">
         <svg class="icon-eye">...</svg>
         <svg class="icon-eye-off">...</svg>
       </button>
     </div>

   JS toggles the `.shown` class on the button (and a `.unmask` class on the
   input when it's a type="text" CSS-masked field — see .cred-masked). */
.secret-input-wrap { position: relative; }
.secret-input-wrap > input { padding-right: 44px; }
.secret-input-toggle {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  color: var(--text-muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  transition: color 0.15s, background 0.15s;
}
.secret-input-toggle:hover {
  color: var(--white);
  background: var(--hover-med);
}
.secret-input-toggle .icon-eye-off { display: none; }
.secret-input-toggle.shown .icon-eye { display: none; }
.secret-input-toggle.shown .icon-eye-off { display: inline-block; }

/* ── TAB BAR (canonical) ──
   Buttons sit in a row; the active one is marked by a 2px bottom border.
   Default layout is gap-based (sized to text width). Add the `.equal`
   modifier to the bar for an equal-width flex:1 split — useful when the
   bar is acting as a primary section nav with a small fixed number of tabs.

   Markup:
     <div class="tab-bar">
       <button class="tab active" data-tab="active">Active <span class="tab-count">12</span></button>
       <button class="tab" data-tab="archived">Archived</button>
     </div>

     <div class="tab-bar equal">
       <button class="tab active">Submit</button>
       <button class="tab">FAQ</button>
       <button class="tab">History</button>
     </div>

   Note: this is NOT the right pattern for a "segmented control" (rounded
   pill background, no underline). For that, use the page-local
   .addapp-tabs / .cd-tab patterns. */
.tab-bar {
  display: flex;
  gap: 4px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--border);
}
.tab-bar.equal { gap: 0; }
.tab-bar.equal .tab { flex: 1; padding: 10px 0; text-align: center; justify-content: center; }

.tab {
  padding: 10px 18px;
  background: none;
  border: none;
  color: var(--text-muted);
  font-family: 'Sora', sans-serif;
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  cursor: pointer;
  transition: color 0.2s, border-color 0.2s;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.tab:hover { color: var(--text-secondary); }
.tab.active {
  color: var(--white);
  border-bottom-color: var(--white);
}

/* Tab counts — pill badges that sit inside a .tab button. The .new modifier
   is the red unread bubble used on sidebar Leads/Feedback chips. */
.tab-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 20px;
  padding: 0 7px;
  border-radius: var(--radius-pill);
  background: rgba(255,255,255,0.06);
  border: 1px solid var(--border);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  color: var(--text-muted);
}
.tab.active .tab-count {
  background: rgba(255,255,255,0.1);
  color: var(--white);
}
.tab-count.new {
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border: none;
  font-size: 12px;
  font-weight: var(--weight-extrabold);
  line-height: 1;
  letter-spacing: 0;
  text-align: center;
  font-variant-numeric: tabular-nums;
  background: #e11d1d;
  color: #000;
  box-shadow: 0 1px 2px rgba(0,0,0,0.35), inset 0 0 0 0.5px rgba(255,255,255,0.12);
}

/* ── COPY BUTTONS (canonical) ──
   One family for every "copy this value to clipboard" button across the
   app. Add the `.copied` class for the green success flash (typically
   toggled in JS for ~1.5s after a successful copy).

   Variants:
   - .copy-btn                 — default standalone pill (replaces .webhook-copy-btn)
   - .copy-btn.copy-btn--sm    — compact button for table cells (replaces .btn-copy);
                                  uses --radius-xs (8px) corners
   - .copy-btn.copy-btn--attached — borderless variant that welds to the right
                                     edge of an input via .copy-attached-wrap
                                     (replaces .gmail-copy-btn)

   Markup (default):
     <button type="button" class="copy-btn">Copy URL</button>

   Markup (attached to input):
     <div class="copy-attached-wrap">
       <input value="..." readonly>
       <button type="button" class="copy-btn copy-btn--attached">Copy</button>
     </div>
*/
.copy-btn {
  padding: 8px 16px;
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-family: 'Sora', sans-serif;
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  cursor: pointer;
  transition: background var(--motion-fast), color var(--motion-fast), border-color var(--motion-fast);
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.copy-btn:hover {
  background: var(--hover-strong);
  color: var(--white);
  border-color: rgba(255,255,255,0.3);
}
.copy-btn.copied { color: var(--success); border-color: rgba(34,197,94,0.3); }

.copy-btn.copy-btn--sm {
  padding: 3px 8px;
  font-size: var(--text-2xs);
  border-radius: var(--radius-xs);
}

.copy-btn.copy-btn--attached {
  padding: 0 14px;
  background: transparent;
  border: none;
  border-left: 1px solid var(--border);
  border-radius: 0;
  color: var(--text-secondary);
}
.copy-btn.copy-btn--attached:hover {
  background: var(--hover-soft);
  border-color: var(--border);
}

.copy-attached-wrap {
  display: flex;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.copy-attached-wrap > input {
  flex: 1;
  background: transparent;
  border: none;
  padding: 10px 14px;
  color: var(--white);
  font-family: inherit;
  font-size: var(--text-sm);
  outline: none;
}

/* ── INLINE BANNER (canonical) ──
   One "colored eyebrow + body (+ optional action)" pattern for every
   inline alert/notice — automation-request state, billing-change notice,
   profile-pending banner, gmail security warning. The icon slot is
   OPTIONAL; only wire it up if the source banner already had one.

   Markup (full structure — all children optional except .inline-banner-body):
     <div class="inline-banner is-warning">
       <div class="inline-banner-icon"><svg>...</svg></div>     [optional]
       <div class="inline-banner-body">
         <span class="inline-banner-eyebrow">Heading</span>     [optional]
         <span class="inline-banner-title">The main message.</span>
       </div>
       <a class="inline-banner-action">Action</a>               [optional]
     </div>

   Severity modifiers (none / is-warning / is-success / is-info / is-danger)
   color the eyebrow, icon and any inline <strong> inside the body. The
   default (no modifier) is a neutral surface. */
.inline-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  line-height: 1.5;
}
.inline-banner-icon { flex-shrink: 0; opacity: 0.6; display: inline-flex; }
/* Body defaults to block layout so inline text + <strong> flow naturally
   (used by .gmail-note-warn and the JS-filled .profile-banner). When the
   body holds eyebrow + title spans, the :has() rule below flips it to a
   stacked column so they don't sit on the same line. */
.inline-banner-body {
  flex: 1;
  min-width: 0;
}
.inline-banner-body:has(> .inline-banner-eyebrow),
.inline-banner-body:has(> .inline-banner-title) {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.inline-banner-body strong { color: var(--white); font-weight: var(--weight-semibold); }
.inline-banner-body a {
  color: var(--white);
  text-decoration: underline;
  text-underline-offset: 3px;
  font-weight: var(--weight-semibold);
}
.inline-banner-eyebrow {
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: inherit;
}
.inline-banner-title {
  color: var(--white);
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
}
.inline-banner-action {
  margin-left: auto;
  color: var(--white);
  text-decoration: underline;
  text-underline-offset: 3px;
  font-weight: var(--weight-semibold);
  font-size: var(--text-sm);
  white-space: nowrap;
  cursor: pointer;
  background: transparent;
  border: none;
  font-family: inherit;
  padding: 0;
}

.inline-banner.is-warning {
  background: var(--warning-soft);
  border-color: rgba(245,158,11,0.35);
}
.inline-banner.is-warning .inline-banner-eyebrow,
.inline-banner.is-warning .inline-banner-icon { color: var(--warning); opacity: 1; }
.inline-banner.is-warning .inline-banner-body strong { color: var(--warning); }

.inline-banner.is-success {
  background: var(--success-soft);
  border-color: rgba(34,197,94,0.35);
}
.inline-banner.is-success .inline-banner-eyebrow,
.inline-banner.is-success .inline-banner-icon { color: var(--success); opacity: 1; }

.inline-banner.is-info {
  background: var(--info-soft);
  border-color: rgba(59,130,246,0.35);
}
.inline-banner.is-info .inline-banner-eyebrow,
.inline-banner.is-info .inline-banner-icon { color: var(--info); opacity: 1; }

.inline-banner.is-danger {
  background: var(--danger-soft);
  border-color: rgba(239,68,68,0.35);
}
.inline-banner.is-danger .inline-banner-eyebrow,
.inline-banner.is-danger .inline-banner-icon { color: var(--danger); opacity: 1; }

/* ── BACK ARROW (canonical) ──
   ONE back-arrow style for the whole app. Inline, 30×30 square, no border,
   subtle hover. Use this on every modal/header back button. Do NOT invent
   variants; if a usage needs absolute positioning, wrap it in a positioned
   container instead of forking the class.

   Markup:
     <button type="button" class="back-arrow" aria-label="Back" onclick="...">
       <svg width="18" height="18" viewBox="0 0 24 24" fill="none"
            stroke="currentColor" stroke-width="3"
            stroke-linecap="round" stroke-linejoin="round">
         <path d="M19 12H5"/><path d="M12 5l-7 7 7 7"/>
       </svg>
     </button>
*/
.back-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  background: none;
  border: none;
  padding: 0;
  margin-left: -6px;
  color: var(--text-muted);
  cursor: pointer;
  border-radius: 10px;
  transition: background 0.15s, color 0.15s;
  font-family: 'Sora', sans-serif;
  flex-shrink: 0;
  text-decoration: none;
}
.back-arrow:hover {
  color: var(--white);
  background: var(--hover-med);
}

/* ── MODAL CLOSE (canonical) ──
   ONE X-close style for every top-right modal close. 30×30 square, SVG X,
   subtle hover background. Use this on every modal close; do NOT invent
   variants. Back-arrow sub-modals keep `.back-arrow` instead.

   Markup:
     <button type="button" class="modal-close" aria-label="Close" onclick="...">
       <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"
            stroke-linecap="round" stroke-linejoin="round">
         <line x1="6" y1="6" x2="18" y2="18"/>
         <line x1="18" y1="6" x2="6" y2="18"/>
       </svg>
     </button>
*/
.modal-close {
  position: absolute;
  top: 12px;
  right: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  background: none;
  border: none;
  padding: 0;
  color: var(--text-muted);
  cursor: pointer;
  border-radius: 10px;
  transition: background 0.15s, color 0.15s;
  flex-shrink: 0;
}
.modal-close:hover {
  color: var(--white);
  background: var(--hover-med);
}
.modal-close svg { width: 14px; height: 14px; }

/* ── PAYMENT-STYLE FORM FIELDS (shared) ──
   Canonical input/select/card-mount + submit styles used by checkout.html
   and the dashboard "Update payment method" modal. Pages can compose these
   into either single-column stacks or 2-column rows (.checkout-row).

   Markup:
     <div class="checkout-field">
       <label>...</label>
       <input class="checkout-input" type="text">
     </div>
     <div class="checkout-field">
       <label>Card</label>
       <div class="checkout-card-element" id="card-element"></div>
     </div>
     <button class="checkout-submit">...</button>
*/
.checkout-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 14px;
}
.checkout-field label {
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  color: var(--text-secondary);
}
.checkout-input,
.checkout-select {
  padding: 14px 18px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--white);
  font-size: var(--text-lg);
  font-family: inherit;
  transition: border-color 0.2s;
  width: 100%;
  box-sizing: border-box;
}
.checkout-input:focus,
.checkout-select:focus {
  outline: none;
  border-color: var(--white);
}
.checkout-select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23999' stroke-width='1.5' fill='none'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 16px center;
  padding-right: 40px;
}
.checkout-card-element {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 14px 18px;
  height: 48px;
  transition: border-color 0.2s;
}
.checkout-card-element:hover,
.checkout-card-element.StripeElement--focus {
  border-color: var(--white);
}
.checkout-card-element.StripeElement--invalid {
  border-color: rgba(239,68,68,0.5);
}
.checkout-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}
.checkout-error {
  font-size: var(--text-md);
  color: var(--danger);
  min-height: 0;
  text-align: center;
}
.checkout-error:not(:empty) { margin: 14px 0 0; }
.checkout-submit {
  width: 100%;
  padding: 16px;
  margin-top: 20px;
  background: var(--white);
  color: var(--bg);
  border: none;
  border-radius: var(--radius-pill);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  font-family: inherit;
  cursor: pointer;
  transition: all 0.25s ease;
}
.checkout-submit:hover { opacity: 0.85; transform: translateY(-2px); }
.checkout-submit:disabled { opacity: 0.3;transform: none; }
.checkout-submit:disabled:hover { opacity: 0.3; transform: none; }

.checkout-secure {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--divider);
  font-size: var(--text-xs);
  color: var(--text-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}

/* ── EMPTY STATE (shared) ──
   One rule for every "no data" message across dashboard + admin.
   Use class="empty-state" on any "nothing to show" div. */
.empty-state {
  padding: 40px 22px;
  text-align: center;
  color: var(--text-muted);
  font-size: var(--text-lg);
  line-height: 1.5;
}

/* ── SIDEBAR (shared) ──
   Canonical glass sidebar for admin.html and dashboard.html. Floating
   translucent panel with a clickable user block at the bottom whose
   dropdown owns Settings / Billing / Sign-out. Page-specific extras
   (payment-banner offset) stay in-page. */
.sidebar {
  position: fixed;
  top: 93px; left: 16px; bottom: 16px;
  width: 224px;
  background: var(--bg-card);
  border: 1px solid rgba(255,255,255,0.07);
  border-radius: var(--radius);
  padding: 16px 14px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  z-index: var(--z-sidebar);
  box-shadow: 0 24px 60px rgba(0,0,0,0.55), inset 0 1px 0 rgba(255,255,255,0.04);
}
.sidebar-label {
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 4px 14px 8px;
}
.sidebar-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-size: var(--text-lg);
  font-weight: var(--weight-medium);
  text-decoration: none;
  transition: color var(--motion-base) ease, background-color var(--motion-base) ease;
  cursor: pointer;
  isolation: isolate;
}
.sidebar-item[hidden] { display: none; }
.sidebar-item::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(180deg, #ffffff 0%, #ededed 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.85),
    inset 0 0 0 1px rgba(0,0,0,0.04),
    inset 0 -1px 0 rgba(0,0,0,0.05),
    0 2px 6px rgba(0,0,0,0.4),
    0 10px 28px rgba(0,0,0,0.55);
  opacity: 0;
  transition: opacity 0.22s ease;
  z-index: var(--z-behind);
}
.sidebar-item:hover {
  color: var(--white);
  background: var(--hover-med);
}
.sidebar-item.active {
  color: #0a0a0a;
  background: transparent;
  font-weight: var(--weight-semibold);
}
.sidebar-item.active::before {
  opacity: 1;
}
.sidebar-item .si-icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.sidebar-item .si-icon svg {
  width: 17px;
  height: 17px;
  stroke: currentColor;
  stroke-width: 2;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.sidebar-spacer { flex: 1; }

/* ── COUNT BADGES (admin sidebar new-pending counts) ── */
.sidebar-item .tab-count,
.sidebar-count {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 6px;
  border-radius: var(--radius-pill);
  background: rgba(255,255,255,0.06);
  border: 1px solid var(--border);
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
  color: var(--text-muted);
  line-height: 1;
}
.sidebar-item .tab-count.new,
.sidebar-count.new {
  background: #e11d1d;
  border-color: transparent;
  color: #000;
}
.sidebar-item.active .tab-count,
.sidebar-item.active .sidebar-count {
  color: #0a0a0a;
  background: rgba(0,0,0,0.08);
  border-color: rgba(0,0,0,0.1);
}
.sidebar-item.active .tab-count.new,
.sidebar-item.active .sidebar-count.new {
  background: #e11d1d;
  border-color: transparent;
  color: #000;
}

/* ── USER BLOCK + DROPDOWN ── */
.sidebar-user-wrap {
  position: relative;
  margin-top: 2px;
}
.sidebar-user {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--border);
  color: var(--white);
  text-align: left;
  cursor: pointer;
  font-family: inherit;
  transition: background var(--motion-fast) ease, border-color var(--motion-fast) ease;
}
.sidebar-user:hover {
  background: var(--hover-med);
  border-color: var(--border-hover);
}
.sidebar-user.open {
  background: rgba(255,255,255,0.07);
  border-color: var(--border-hover);
}
.sidebar-avatar {
  width: 30px; height: 30px;
  border-radius: 50%;
  background: linear-gradient(135deg, #555 0%, #2a2a2a 100%);
  border: 1px solid var(--border);
  flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--text);
  font-size: 0.78rem;
  font-weight: var(--weight-bold);
  letter-spacing: -0.01em;
}
.sidebar-user-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.sidebar-user-name {
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  color: var(--white);
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sidebar-user-meta {
  font-size: var(--text-xs);
  color: var(--text-muted);
  font-weight: var(--weight-medium);
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sidebar-user-chev {
  width: 14px; height: 14px;
  color: var(--text-muted);
  flex-shrink: 0;
  transition: transform var(--motion-fast) ease, color var(--motion-fast) ease;
}
.sidebar-user-chev svg {
  width: 14px; height: 14px;
  stroke: currentColor; stroke-width: 2; fill: none;
  stroke-linecap: round; stroke-linejoin: round;
}
.sidebar-user.open .sidebar-user-chev {
  transform: rotate(180deg);
  color: var(--white);
}
.sidebar-menu {
  position: absolute;
  left: 0; right: 0;
  bottom: calc(100% + 6px);
  background: var(--bg-card);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: var(--radius-sm);
  padding: 6px;
  box-shadow: 0 16px 40px rgba(0,0,0,0.6);
  opacity: 0;
  transform: translateY(4px);
  pointer-events: none;
  transition: opacity 0.16s ease, transform 0.16s ease;
}
.sidebar-user.open + .sidebar-menu {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.sidebar-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 10px;
  border-radius: var(--radius-xs);
  color: var(--text-secondary);
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
  text-decoration: none;
  background: none;
  border: none;
  width: 100%;
  text-align: left;
  cursor: pointer;
  font-family: inherit;
  transition: background 0.14s ease, color 0.14s ease;
}
.sidebar-menu-item:hover {
  background: var(--hover-med);
  color: var(--white);
}
.sidebar-menu-item svg {
  width: 14px; height: 14px;
  stroke: currentColor; stroke-width: 2; fill: none;
  stroke-linecap: round; stroke-linejoin: round;
  flex-shrink: 0;
}
.sidebar-menu-sep {
  height: 1px;
  background: rgba(255,255,255,0.06);
  margin: 4px 6px;
}

/* Hide redundant top-nav user controls on desktop — the sidebar now owns
   the avatar + sign-out via the user block. The DOM stays so legacy JS that
   writes navAvatar.textContent doesn't crash. On mobile (<=768px) the
   sidebar collapses to a bottom-bar that can't fit the user block, so the
   nav controls re-appear there. */
@media (min-width: 769px) {
  .nav-avatar,
  .nav-signout { display: none !important; }
}

/* ── DASH LAYOUT ──
   Page shell shared by admin + dashboard. .dash-wrap is the flex row
   (sidebar | main); .dash-main is the right column; .dash-section is
   one SPA tab; .dash-section-header is its title row. Page-specific
   overrides (admin's 1080px content-frame, dashboard's referrals-only
   padding) live in their respective HTML files. */
.dash-wrap {
  display: flex;
  min-height: 100%;
}
.dash-main {
  flex: 1;
  padding: 15px 0 0;
}
.dash-section {
  margin-bottom: 0;
  scroll-margin-top: 90px;
}
.dash-section-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 32px;
  gap: 16px;
  flex-wrap: wrap;
}
.dash-section-header h2 {
  font-size: 1.6rem;
  font-weight: var(--weight-extrabold);
  letter-spacing: -0.03em;
}
.dash-section-header p {
  font-size: var(--text-lg);
  color: var(--text-secondary);
  margin-top: 4px;
  font-weight: var(--weight-regular);
}

/* ── CONTENT FRAME ──
   The right-side content lives inside a fixed rounded container that
   scrolls internally — like a modal's scrollable card body. Symmetric
   with the sidebar's 16px gutters on right/bottom, flush with the nav
   on top. The 22px border-radius matches the sidebar so the two read as
   parallel framed surfaces. */
.content-frame {
  position: fixed;
  top: 93px;
  left: 288px;
  right: 32px;
  bottom: 16px;
  max-width: 1000px;
  border-radius: var(--radius);
  overflow-y: auto;
  background: var(--bg);
  z-index: var(--z-raised);
  scrollbar-width: none;
}
.content-frame::-webkit-scrollbar { display: none; }

/* Banner-aware: drop the frame top to sit below the payment banner. */
body.has-payment-banner .content-frame { top: 149px; }

/* ── CONTENT FADE ──
   Fixed black-gradient strips at the top and bottom edges of the
   content-frame so content fades into the void (body bg) as it scrolls
   past the visible area. Solid black at the edge, transparent at the
   inner side. */
.content-fade {
  position: fixed;
  left: 288px;
  right: 32px;
  height: 48px;
  pointer-events: none;
  z-index: var(--z-raised-2);
}
.content-fade-top {
  top: 93px;
  border-top-left-radius: var(--radius);
  border-top-right-radius: var(--radius);
  background: linear-gradient(to bottom, var(--bg), transparent);
  transition: opacity 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.content-fade-bottom {
  bottom: 16px;
  border-bottom-left-radius: var(--radius);
  border-bottom-right-radius: var(--radius);
  background: linear-gradient(to top, var(--bg), transparent);
  transition: opacity 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
body.has-payment-banner .content-fade-top { top: 149px; }
@media (max-width: 768px) {
  .content-fade { display: none; }
}

/* Mobile: sidebar collapses to a bottom bar; the frame becomes flow content. */
@media (max-width: 768px) {
  .content-frame {
    position: static;
    top: auto; left: auto; right: auto; bottom: auto;
    border-radius: 0;
    overflow: visible;
  }
}

/* ── FOOTER ── */
footer {
  background: var(--bg);
  border-top: 1px solid var(--border);
}

.footer-grid {
  max-width: 1148px;
  margin: 0 auto;
  padding: 56px 24px 40px;
  display: grid;
  grid-template-columns: 2.5fr 1fr 1fr 1fr;
  gap: 40px;
}

.footer-brand .logo { display: inline-block; margin-bottom: 14px; }
.footer-brand p {
  color: var(--text-secondary);
  font-size: var(--text-md);
  max-width: 280px;
  margin-bottom: 18px;
  line-height: 1.6;
}

.footer-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: 10px; /* literal — small icon tile, matches .back-arrow / .modal-close pin */
  transition: color var(--motion-base), border-color var(--motion-base);
}
.footer-x:hover {
  color: var(--white);
  border-color: var(--border-hover);
}

.footer-col h4 {
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin: 0 0 16px;
}
.footer-col ul {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 0;
  margin: 0;
}
.footer-col a {
  color: var(--text);
  font-size: var(--text-md);
  text-decoration: none;
  transition: color 160ms ease;
}
.footer-col a:hover { color: var(--text-secondary); }

.footer-bottom {
  border-top: 1px solid var(--border);
}
.footer-bottom-inner {
  max-width: 1148px;
  margin: 0 auto;
  padding: 18px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: var(--text-muted);
  font-size: var(--text-xs);
  letter-spacing: 0.02em;
}

@media (max-width: 760px) {
  .footer-grid { grid-template-columns: 1fr; gap: 32px; padding: 40px 24px 28px; }
  .footer-bottom-inner { flex-direction: column; gap: 8px; text-align: center; }
}

/* ── FORM FEEDBACK TEXT ── */
/* Generic error/success message lines for modal forms (admin invite, request
   apps, edit automation, etc.) and the marketing sign-in modal. */
.form-error {
  font-size: 0.82rem;
  color: var(--danger);
  text-align: center;
  min-height: 0;
  margin-bottom: 12px;
}
.form-error:empty { display: none; }

/* Divider that sits before the submit button on modal forms (signin modal,
   OTP modals). When followed by a non-empty .form-error, the bottom margin
   shrinks to 12px so the error nests symmetrically between divider and
   button (12px gap above + .form-error's 12px margin-bottom below). */
.form-divider {
  border-top: 1px solid var(--divider);
  margin: 20px 0;
}
.form-divider:has(+ .form-error:not(:empty)) {
  margin-bottom: 12px;
}

.form-success {
  font-size: 0.82rem;
  color: var(--success);
  text-align: center;
  min-height: 0;
}
.form-success:empty { display: none; }

/* ── RESPONSIVE (shared) ── */
@media (max-width: 768px) {
  nav { padding: 16px 20px; }
  .nav-links a:not(.nav-cta):not(.nav-signin) { display: none; }
  .form-row { grid-template-columns: 1fr; }
}

/* ── SCROLL ANIMATIONS ── */
.reveal {
  opacity: 0;
  transform: translateY(30px);
  transition: all 0.7s var(--ease-out);
}
.reveal.visible {
  opacity: 1;
  transform: translateY(0);
}

/* ── CONFIGURE-YOUR-PLAN SHELL (checkout / plan-change / downgrade) ── */
/* Used by checkout.html, plan-change.html, downgrade.html. Per-page
   deltas (e.g. checkout's grid + sticky summary) live in those files. */
.cfg-page {
  position: relative;
  background: var(--bg);
  padding: 64px 0 40px;
  overflow: hidden;
  min-height: 100vh;
  box-sizing: border-box;
}
.cfg-inner {
  max-width: 1080px;
  margin: 0 auto;
  padding: 0 32px;
}
.cfg-logo {
  position: absolute;
  top: 20px;
  left: 40px;
  z-index: var(--z-sticky);
}
.cfg-header {
  display: flex; align-items: center;
  gap: 12px;
  margin: 32px 0 20px;
}
.cfg-header .back-arrow {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-sm);
  margin-left: -52px;
}
.cfg-header h1 {
  font-size: 1.6rem;
  font-weight: var(--weight-extrabold);
  letter-spacing: -0.025em;
  margin: 0;
  color: var(--white);
}
.cfg-summary {
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 32px;
}
.cfg-summary h2 {
  font-size: 1.6rem;
  font-weight: var(--weight-extrabold);
  letter-spacing: -0.025em;
  margin: 0 0 22px;
  color: var(--white);
}
.cfg-summary-features-label {
  font-size: var(--text-lg);
  font-weight: var(--weight-medium);
  color: var(--white);
  margin: 0 0 14px;
}
.cfg-features {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 14px;
}
.cfg-features li {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: var(--text-lg);
  font-weight: var(--weight-medium);
  color: var(--text);
  line-height: 1.4;
}
.cfg-feature-icon {
  width: 22px; height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--white);
  flex-shrink: 0;
}
.cfg-feature-icon svg { width: 16px; height: 16px; }
.cfg-summary-divider {
  border-top: 1px solid var(--divider);
  margin: 22px 0 18px;
}
.cfg-summary-row {
  display: flex; justify-content: space-between;
  padding: 3px 0;
  font-size: var(--text-sm);
  color: var(--text-secondary);
}
.cfg-summary-row .v { color: var(--white); font-weight: var(--weight-regular); }
.cfg-summary-row.total {
  margin-top: 3px;
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  color: var(--white);
}
.cfg-summary-row.total .v { font-weight: var(--weight-bold); }
.cfg-submit {
  width: 100%;
  margin-top: 4px;
  padding: 20px 22px 20px 14px;
  background: var(--white);
  color: var(--bg);
  border: none;
  border-radius: var(--radius-pill);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  font-family: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  transition: opacity var(--motion-base), transform var(--motion-base);
}
.cfg-submit:hover:not(:disabled) { opacity: 0.9; transform: translateY(-1px); }
.cfg-submit:disabled { opacity: 0.4; }
.cfg-error {
  font-size: var(--text-sm);
  color: var(--danger);
  margin-top: 14px;
  text-align: center;
  min-height: 0;
}
.cfg-error:not(:empty) { margin-top: 14px; }
.cfg-legal {
  margin: 22px 0 0;
  padding: 0 16px;
  font-size: var(--text-xs);
  color: var(--text-secondary);
  line-height: 1.65;
}
.cfg-legal a {
  color: var(--white);
  text-decoration: underline;
  text-underline-offset: 2px;
  white-space: nowrap;
}
.cfg-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  color: var(--text-secondary);
  font-size: var(--text-lg);
}

@media (max-width: 880px) {
  .cfg-page { padding: 56px 0 32px; }
  .cfg-inner { padding: 0 20px; }
  .cfg-logo { left: 20px; }
  .cfg-header .back-arrow { margin-left: 0; }
}

/* ─────────────────────────────────────────────────────────
   REMOVE APP MODAL — shared by dashboard + admin
   Scoped to #removeAppOverlay (dashboard) and #adminRemoveAppOverlay
   (admin). The .confirm-warning-row class is also used by admin's
   reusable confirmDialog (scoped under #confirmOverlay) — both
   selectors produce identical styling, so no conflict.
   ───────────────────────────────────────────────────────── */
#removeAppOverlay .cred-modal,
#adminRemoveAppOverlay .cred-modal { max-width: 460px; }
#removeAppOverlay .confirm-warning-row,
#adminRemoveAppOverlay .confirm-warning-row {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--warning);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: 10px;
}
#removeAppOverlay .confirm-warning-row svg,
#adminRemoveAppOverlay .confirm-warning-row svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}
.remove-app-deps {
  border: 1px solid var(--border);
  background: rgba(255,255,255,0.02);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
  margin-bottom: 20px;
}
.remove-app-deps-label {
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin-bottom: 10px;
}
.remove-app-deps-empty {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.4;
}
.remove-app-deps-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 180px;
  overflow-y: auto;
}
.remove-app-deps-item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--text-sm);
  color: var(--white);
}
.remove-app-deps-item .dep-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--text-muted);
  position: relative;
  top: -1px;
}
.remove-app-deps-item.dep-active .dep-dot { background: var(--success); }
.remove-app-deps-item.dep-paused .dep-dot { background: var(--warning); }
.remove-app-actions {
  display: flex;
  gap: 8px;
}
.remove-app-cancel,
.remove-app-confirm {
  flex: 1;
  padding: 11px 14px;
  border-radius: var(--radius-xs);
  font-family: 'Sora', sans-serif;
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  cursor: pointer;
  transition: all 0.2s;
  text-align: center;
}
.remove-app-cancel {
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  font-weight: var(--weight-semibold);
}
.remove-app-cancel:hover {
  background: var(--hover-strong);
  color: var(--white);
  border-color: rgba(255,255,255,0.15);
}
.remove-app-confirm {
  background: var(--danger);
  border: none;
  color: var(--white);
}
.remove-app-confirm:hover { background: #dc2626; }
.remove-app-confirm .cd-slot {
  display: inline-block;
  min-width: 0.7em;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.remove-app-confirm:disabled {
  opacity: 0.5;
  cursor: default;
}
.remove-app-confirm:disabled:hover { background: var(--danger); }

/* ── PLAN-CHANGE / DOWNGRADE FLOW ── (plan-change.html, downgrade.html) */
.pc-center {
  max-width: 480px;
  margin: 0 auto;
}
.pc-error-msg {
  color: var(--text-secondary);
  font-size: var(--text-lg);
  text-align: center;
  margin: 12px 0 20px;
}
.pc-back-link {
  display: block;
  text-align: center;
  color: var(--text-secondary);
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.pc-renewal-note {
  margin: 18px 0 4px;
  padding: 12px 14px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  display: flex;
  gap: 10px;
  align-items: flex-start;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.5;
}
.pc-renewal-note svg { flex-shrink: 0; margin-top: 2px; opacity: 0.6; }
.pc-renewal-note strong { color: var(--white); font-weight: var(--weight-semibold); }

/* ── PAGE LOADER ── (client-facing pages only; opt-in via markup) */
/* Self-hosted Sora 800 so the loader logo renders instantly without
   waiting on Google Fonts. Pair with <link rel="preload"> in each page. */
@font-face {
  font-family: 'Sora Loader';
  font-style: normal;
  font-weight: var(--weight-extrabold);
  font-display: block;
  src: url('/assets/fonts/sora-800.woff2') format('woff2');
}
.page-loader {
  position: fixed;
  inset: 0;
  background: #000;
  z-index: var(--z-page-loader);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity 220ms ease-out;
}
.page-loader.is-hiding { opacity: 0; pointer-events: none; }
.page-loader-logo {
  font-family: 'Sora Loader', sans-serif;
  font-size: 2.4rem;
  font-weight: var(--weight-extrabold);
  letter-spacing: -0.04em;
  will-change: background-position, transform;
  background: linear-gradient(90deg,
    #7a7a80 0%,
    #7b7b80 2.5%,
    #7d7d83 5%,
    #818187 7.5%,
    #87878c 10%,
    #8d8d93 12.5%,
    #95959a 15%,
    #9e9ea3 17.5%,
    #a8a8ac 20%,
    #b2b2b5 22.5%,
    #bdbdc0 25%,
    #c7c7c9 27.5%,
    #d1d1d3 30%,
    #dbdbdc 32.5%,
    #e4e4e5 35%,
    #ececed 37.5%,
    #f2f2f3 40%,
    #f7f7f8 42.5%,
    #fcfcfc 45%,
    #fefefe 47.5%,
    #ffffff 50%,
    #fefefe 52.5%,
    #fcfcfc 55%,
    #f7f7f8 57.5%,
    #f2f2f3 60%,
    #ececed 62.5%,
    #e4e4e5 65%,
    #dbdbdc 67.5%,
    #d1d1d3 70%,
    #c7c7c9 72.5%,
    #bdbdc0 75%,
    #b2b2b5 77.5%,
    #a8a8ac 80%,
    #9e9ea3 82.5%,
    #95959a 85%,
    #8d8d93 87.5%,
    #87878c 90%,
    #818187 92.5%,
    #7d7d83 95%,
    #7b7b80 97.5%,
    #7a7a80 100%);
  background-size: 600% 100%;
  background-repeat: repeat;
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
  animation:
    page-loader-sweep 4.617s linear infinite,
    page-loader-scale 0.9234s ease-in-out infinite;
}
.page-loader-logo span {
  font-weight: var(--weight-extrabold);
  color: var(--text-secondary);
  -webkit-text-fill-color: transparent;
}
@keyframes page-loader-sweep {
  0%   { background-position: 710% 0; }
  100% { background-position: 110% 0; }
}
@keyframes page-loader-scale {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.06); }
}
@media (prefers-reduced-motion: reduce) {
  .page-loader-logo { animation: none; }
}

/* ── TOOLTIP (canonical) ──
   Hover-delay explainer pill (500ms customer / 1000ms admin via
   `--tooltip-delay`) with rotated-square arrow, positioned ABOVE the anchor.
   Add `data-tooltip="..."` for a centered tip (max 280px), or
   `data-tooltip-pre="..."` for preformatted multi-line left-aligned (max 360px).
   `\n` in the attribute value renders as a line break (both variants use
   `white-space: pre-line`).

   Uses `position: fixed` so the tooltip escapes ancestor overflow clipping
   (.content-frame, scrolling modals). Anchor coords are computed on hover by
   js/tooltip.js, which writes `--tip-top` / `--tip-left` on the element (and
   compensates for transformed ancestors that act as fixed containing blocks). */
/* Empty attribute (e.g. `data-tooltip=""`) renders no bubble — common pattern
   for "show tip when there's something to say, otherwise nothing." */
[data-tooltip=""]::before,
[data-tooltip=""]::after,
[data-tooltip-pre=""]::before,
[data-tooltip-pre=""]::after { content: none !important; }

[data-tooltip]::before,
[data-tooltip]::after,
[data-tooltip-pre]::before,
[data-tooltip-pre]::after {
  pointer-events: none;
  opacity: 0;
  z-index: var(--z-tooltip);
  transition: opacity 150ms ease, transform 150ms ease;
}
[data-tooltip]::after,
[data-tooltip-pre]::after {
  position: fixed;
  left: var(--tip-left, -9999px);
  top: var(--tip-top, -9999px);
  transform: translate(-50%, calc(-100% - 6px));
  background: #1a1a1a;
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: var(--radius-sm);
  color: rgba(255,255,255,0.85);
  font-family: 'Sora', sans-serif;
  font-size: var(--text-sm);
  font-weight: var(--weight-regular);
  letter-spacing: 0;
  text-transform: none;
  width: max-content;
  box-shadow: var(--shadow-md);
}
[data-tooltip]::after {
  content: attr(data-tooltip);
  padding: 8px 12px;
  line-height: 1.4;
  white-space: pre-line;
  max-width: 280px;
  text-align: center;
}
[data-tooltip-pre]::after {
  content: attr(data-tooltip-pre);
  padding: 10px 16px;
  line-height: 1.7;
  white-space: pre-line;
  max-width: 360px;
  text-align: left;
}
[data-tooltip]::before,
[data-tooltip-pre]::before {
  content: '';
  position: fixed;
  left: var(--tip-left, -9999px);
  top: var(--tip-top, -9999px);
  transform: translate(-50%, calc(-100% - 1px)) rotate(45deg);
  width: 10px;
  height: 10px;
  background: #1a1a1a;
  border-right: 1px solid rgba(255,255,255,0.15);
  border-bottom: 1px solid rgba(255,255,255,0.15);
  z-index: calc(var(--z-tooltip) + 1);
}
[data-tooltip]:hover::after,
[data-tooltip-pre]:hover::after {
  transform: translate(-50%, calc(-100% - 10px));
}
[data-tooltip]:hover::before,
[data-tooltip-pre]:hover::before {
  transform: translate(-50%, calc(-100% - 5px)) rotate(45deg);
}
[data-tooltip]:hover::after,
[data-tooltip]:hover::before,
[data-tooltip-pre]:hover::after,
[data-tooltip-pre]:hover::before {
  opacity: 1;
  transition: opacity 150ms ease var(--tooltip-delay), transform 150ms ease var(--tooltip-delay);
}

/* Admin-facing surfaces use the slower delay tier. */
body.admin-ui { --tooltip-delay: var(--tooltip-delay-admin); }
