/* ============================================================
   Edit — portal.css
   Mobile-first. Tokens + utilities + components.
   Phase 0.5: waitlist landing + admin pages.
   Phase 1A onward will extend this stylesheet — never inline
   styles, never duplicate patterns. Add classes here first.
   ============================================================ */

/* ---------- DESIGN TOKENS ---------- */
:root {
    /* Brand */
    --brand:            #0a0a0a;
    --brand-soft:       #1a1a1a;
    --brand-accent:        #ff6b35;   /* warm accent — fills, icons, focus rings */
    --brand-accent-strong: #c2410c;   /* accent for TEXT on white (AA contrast) */
    --brand-accent-tint:   #fff2ec;   /* light accent surface — active rows/tabs */
    --ink:              #0a0a0a;   /* darkest ink — fills, outlines, cover borders */

    /* Text */
    --text:             #0a0a0a;
    --text-med:         #4a4a4a;
    --text-light:       #777;
    --text-inverse:     #fff;

    /* Surfaces */
    --bg:               #fff;
    --bg-soft:          #fafafa;
    --bg-muted:         #f3f3f3;
    --bg-card:          #fff;
    --bg-dark:          #0a0a0a;

    /* Borders */
    --border:           #e5e5e5;
    --border-soft:      #f0f0f0;
    --border-strong:    #c8c8c8;

    /* Status */
    --success:          #15803d;
    --success-bg:       #f0fdf4;
    --success-border:   #bbf7d0;
    --warn:             #92400e;
    --warn-bg:          #fffaf0;
    --warn-border:      #fed7aa;
    --danger:           #991b1b;
    --danger-bg:        #fef2f2;
    --danger-border:    #fecaca;
    --info:             #1e40af;
    --info-bg:          #eff6ff;
    --info-border:      #bfdbfe;

    /* Typography */
    --font-sans:        "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
    --font-display:     "Inter", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
    --font-mono:        ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, monospace;

    /* Spacing scale */
    --sp-1: .25rem;  --sp-2: .5rem;   --sp-3: .75rem;
    --sp-4: 1rem;    --sp-5: 1.25rem; --sp-6: 1.5rem;
    --sp-7: 2rem;    --sp-8: 2.5rem;  --sp-9: 3rem;
    --sp-10: 4rem;   --sp-11: 5rem;   --sp-12: 6rem;

    /* Radius */
    --radius-sm: 4px;
    --radius:    8px;
    --radius-lg: 12px;
    --radius-pill: 999px;

    /* Shadow */
    --shadow-sm: 0 1px 2px rgba(0,0,0,.04);
    --shadow:    0 2px 8px rgba(0,0,0,.06);
    --shadow-lg: 0 8px 24px rgba(0,0,0,.08);
}

/* ---------- RESET ---------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
    font-family: var(--font-sans);
    color: var(--text);
    background: var(--bg);
    line-height: 1.5;
    font-size: 16px; /* iOS: prevents auto-zoom on input focus */
    -webkit-text-size-adjust: 100%;
    -webkit-font-smoothing: antialiased;
}
a { color: inherit; }
img, svg { max-width: 100%; display: block; }

/* ---------- TYPE UTILITIES (port from TP CLAUDE) ---------- */
.t-xs   { font-size: .78rem; }
.t-sm   { font-size: .85rem; }
.t-md   { font-size: .92rem; }
.t-lg   { font-size: 1.05rem; }
.t-muted   { color: var(--text-light); }
.t-med     { color: var(--text-med); }
.t-success { color: var(--success); }
.t-danger  { color: var(--danger); }
.t-mono    { font-family: var(--font-mono); }
.t-wrap    { white-space: pre-wrap; word-break: break-word; }
.text-center { text-align: center; }
.text-right  { text-align: right; }

/* ---------- SPACING UTILITIES ---------- */
.mt-0 { margin-top: 0; }
.mt-2 { margin-top: var(--sp-2); }
.mt-3 { margin-top: var(--sp-3); }
.mt-4 { margin-top: var(--sp-4); }
.mt-5 { margin-top: var(--sp-5); }
.mt-6 { margin-top: var(--sp-6); }
.mt-7 { margin-top: var(--sp-7); }
.mb-0 { margin-bottom: 0; }
.mb-2 { margin-bottom: var(--sp-2); }
.mb-3 { margin-bottom: var(--sp-3); }
.mb-4 { margin-bottom: var(--sp-4); }
.mb-5 { margin-bottom: var(--sp-5); }
.mb-6 { margin-bottom: var(--sp-6); }
.mb-7 { margin-bottom: var(--sp-7); }

/* ---------- PADDING UTILITIES ---------- */
.p-0 { padding: 0; }
.p-3 { padding: var(--sp-3); }
.p-4 { padding: var(--sp-4); }
.p-5 { padding: var(--sp-5); }
.p-6 { padding: var(--sp-6); }
.pt-0 { padding-top: 0; }
.mb-1 { margin-bottom: var(--sp-1); }

/* ---------- LAYOUT UTILITIES ---------- */
.action-row {
    display: flex;
    gap: var(--sp-3);
    flex-wrap: wrap;
    align-items: center;
}
.action-row-end     { justify-content: flex-end; }
.action-row-between { justify-content: space-between; }
.action-row > form  { display: contents; } /* let nested <form> not break flex layout */
.stack-col {
    display: flex;
    flex-direction: column;
    gap: var(--sp-4);
}
.stack-col-tight { gap: var(--sp-2); }
.stack-col-loose { gap: var(--sp-6); }

/* ---------- BRAND ----------
   Mark and wordmark both use Inter weight 900. The mark is just the
   letter "E" in a dark rounded square so it pairs visually with the
   wordmark and works as a favicon. */

.brand {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-3);
    color: var(--brand);
    text-decoration: none;
}

/* Mark: letter "E" in a dark square, white-on-dark.
   Size variants scale font-size proportionally to the square. */
.brand-mark,
.brand-mark-lg,
.brand-mark-xl {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--brand-accent);
    color: #fff;
    font-family: var(--font-display);
    font-weight: 900;
    line-height: 1;
    text-align: center;
    flex-shrink: 0;
    user-select: none;
    /* nudge the letter optically — sans-serif caps sit slightly low in their box */
    padding-bottom: .05em;
}
.brand-mark {
    width: 1.6rem;
    height: 1.6rem;
    font-size: 1.1rem;
    letter-spacing: -.04em;
    border-radius: 4px;
}
.brand-mark-lg {
    width: 3rem;
    height: 3rem;
    font-size: 2rem;
    letter-spacing: -.05em;
    border-radius: 8px;
    margin: 0 auto;
}
.brand-mark-xl {
    width: 4rem;
    height: 4rem;
    font-size: 2.75rem;
    letter-spacing: -.05em;
    border-radius: 10px;
}

/* Wordmark: "Edit" text in matching Inter 900. */
.brand-wordmark {
    font-family: var(--font-display);
    font-weight: 900;
    font-size: 1.4rem;
    letter-spacing: -.04em;
    color: var(--brand);
    line-height: 1;
}
.brand-wordmark-lg {
    font-family: var(--font-display);
    font-weight: 900;
    font-size: 2.25rem;
    letter-spacing: -.045em;
    color: var(--brand);
    line-height: 1;
    margin: 0;
}
.brand-wordmark-xl {
    font-family: var(--font-display);
    font-weight: 900;
    font-size: 3rem;
    letter-spacing: -.05em;
    color: var(--brand);
    line-height: .95;
    margin: 0;
}
.brand-lockup-xl {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-4);
    margin: 0 auto;
}

/* Brand toggle: E shown by default, "Edit" wordmark slides out from behind
   it on hover or keyboard focus. Used on marketing surfaces only. */
.brand-toggle {
    display: inline-flex;
    align-items: center;
    color: var(--brand);
    text-decoration: none;
}
.brand-toggle .brand-wordmark {
    max-width: 0;
    margin-left: 0;
    opacity: 0;
    overflow: hidden;
    white-space: nowrap;
    transition:
        max-width 280ms cubic-bezier(.4, 0, .2, 1),
        margin-left 280ms cubic-bezier(.4, 0, .2, 1),
        opacity 180ms ease;
}
.brand-toggle:hover .brand-wordmark,
.brand-toggle:focus-visible .brand-wordmark,
.brand-toggle:focus-within .brand-wordmark {
    max-width: 6rem;
    margin-left: var(--sp-2);
    opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
    .brand-toggle .brand-wordmark {
        max-width: 6rem;
        margin-left: var(--sp-2);
        opacity: 1;
        transition: none;
    }
}

.brand-sublabel {
    font-family: var(--font-mono);
    font-size: .72rem;
    text-transform: uppercase;
    letter-spacing: .12em;
    font-weight: 600;
    color: var(--text-med);
}

/* Logo export page — preview tile + action buttons */
.logo-export {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--sp-6);
}
.logo-preview {
    width: 192px;
    height: 192px;
    background: var(--brand);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-display);
    font-weight: 900;
    font-size: 134px;
    letter-spacing: -.05em;
    line-height: 1;
    user-select: none;
}
.logo-export-actions {
    display: flex;
    gap: var(--sp-3);
    flex-wrap: wrap;
    justify-content: center;
}

/* ---------- BUTTONS ---------- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    padding: .75rem 1.25rem;
    min-height: 44px; /* mobile tap target */
    border-radius: var(--radius);
    border: 1px solid transparent;
    font-family: inherit;
    font-size: 1rem;
    font-weight: 500;
    line-height: 1;
    text-decoration: none;
    cursor: pointer;
    transition: background .15s, border-color .15s, color .15s, box-shadow .15s, transform .1s;
    -webkit-appearance: none;
    appearance: none;
}
.btn-primary {
    background: var(--brand);
    color: var(--text-inverse);
}
/* Tactile hover: the ink button shifts + lifts so it clearly responds. */
.btn-primary:hover {
    background: var(--brand-soft);
    box-shadow: 0 4px 14px rgba(10, 10, 10, .22);
    transform: translateY(-1px);
}
.btn-primary:active {
    transform: translateY(0);
    box-shadow: 0 2px 6px rgba(10, 10, 10, .16);
}

/* Segmented control — the canonical Active/Archived toggle on list pages.
   A muted track with the active segment lifted on white. */
.segmented {
    display: inline-flex;
    gap: 2px;
    padding: 3px;
    background: var(--bg-muted);
    border-radius: var(--radius);
}
.segmented-item {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    padding: .4rem .8rem;
    min-height: 34px;
    border-radius: calc(var(--radius) - 3px);
    font-size: .85rem;
    font-weight: 500;
    color: var(--text-med);
    text-decoration: none;
    white-space: nowrap;
    transition: background .12s, color .12s, box-shadow .12s;
}
.segmented-item:hover { color: var(--text); }
.segmented-item.is-active {
    background: #fff;
    color: var(--text);
    box-shadow: var(--shadow-sm);
}
.segmented-count {
    color: var(--text-light);
    font-variant-numeric: tabular-nums;
    font-size: .8rem;
}
.segmented-item.is-active .segmented-count { color: var(--text-med); }
/* Compact button. Used inline in table rows + cards where the default
   44px tap target overwhelms the surrounding density. Still meets a
   reasonable touch target on mobile (32px) while looking right on
   desktop. */
.btn-sm {
    padding: .375rem .75rem;
    min-height: 32px;
    font-size: .875rem;
    gap: .375rem;
}
/* Action buttons grouped inline at the end of a table row (e.g. View
   + Pay on client invoices). Right-aligned by default — sits inside a
   .text-right td. */
.invoice-row-actions {
    display: inline-flex;
    gap: var(--sp-2);
    justify-content: flex-end;
}
.btn-secondary {
    background: var(--bg);
    color: var(--text);
    border-color: var(--border-strong);
}
.btn-secondary:hover { background: var(--bg-muted); }
.btn-tertiary {
    color: var(--text);
    background: var(--bg-muted);
    border-color: var(--bg-muted);
}
.btn-tertiary:hover {
    background: #e8e8e8;
    border-color: #e8e8e8;
}
.btn-ghost {
    background: transparent;
    color: var(--text-med);
}
.btn-ghost:hover { color: var(--text); background: var(--bg-muted); }
.btn-ghost.t-danger { color: var(--danger); }
.btn-ghost.t-danger:hover { color: var(--danger); background: var(--danger-bg); }
.btn-full { width: 100%; }

/* ---------- FORM CONTROLS ---------- */
.form-control {
    width: 100%;
    padding: .75rem .875rem;
    min-height: 44px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    background: var(--bg);
    font-family: inherit;
    font-size: 1rem;
    line-height: 1.4;
    color: var(--text);
    -webkit-appearance: none;
    appearance: none;
}
.form-control:focus {
    outline: 2px solid var(--brand-accent);
    outline-offset: -1px;
    border-color: var(--brand-accent);
}
/* Client-side validation invalid state — applied by form-validate.js
   after blur if the field fails its data-validate rules. Matches the
   server-side .form-control + .form-error pairing so the visual is the
   same whether the error came from JS or PHP. */
.form-control.is-invalid {
    border-color: var(--danger);
    background: #fef7f7;
}
.form-control.is-invalid:focus {
    outline-color: var(--danger);
    border-color: var(--danger);
}

/* Dropdown chevron — the global appearance:none reset strips the native
   arrow off <select>s, so draw our own. Applies to every select.form-control
   across BOTH the studio and client portals (shared stylesheet). */
select.form-control {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%2364748b' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.875rem center;
    background-size: 12px 8px;
    padding-right: 2.5rem;
    cursor: pointer;
}
/* The .is-invalid background shorthand would wipe the chevron — restore it. */
select.form-control.is-invalid {
    background-color: #fef7f7;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%2364748b' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.form-error {
    display: block;
    color: var(--danger);
    font-size: .8rem;
    margin-top: 6px;
    line-height: 1.4;
}
.form-label {
    display: block;
    font-size: .92rem;
    font-weight: 500;
    color: var(--text);
    margin-bottom: var(--sp-2);
}
/* Label row with a right-aligned inline action (e.g. "+ New client"). */
.form-label-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--sp-2);
    margin-bottom: var(--sp-2);
}
.form-label-row .form-label { margin-bottom: 0; }
.form-label-action {
    border: none;
    background: transparent;
    padding: 0;
    cursor: pointer;
    color: var(--brand-accent-strong);
    font-size: .8rem;
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    white-space: nowrap;
}
.form-label-action:hover { text-decoration: underline; }
.form-label-action i { font-size: .7rem; }
/* Inline field group (e.g. shoot date + optional time side by side). */
.field-inline { display: flex; gap: var(--sp-2); }
.field-inline > input[type="date"] { flex: 1 1 auto; min-width: 0; }
.field-inline-time { flex: 0 0 auto; width: auto; min-width: 6.5rem; }

/* Auto-archive day count (Settings → Preferences) */
.auto-archive-days-field .field-inline { align-items: center; }
.auto-archive-days-field #auto_archive_days { flex: 0 0 auto; width: 6rem; }
.auto-archive-days-field.is-disabled { opacity: .55; }
.late-fee-pct-field { max-width: 14rem; }
.field-note {
    display: flex;
    gap: var(--sp-2);
    margin: var(--sp-4) 0 0;
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--radius);
    font-size: .82rem;
    line-height: 1.5;
    color: var(--text-med);
    background: var(--bg-soft);
    border: 1px solid var(--border-soft);
}
.field-note i { margin-top: 2px; flex: 0 0 auto; }
.field-note-info { border-left: 3px solid var(--brand-accent); }
.field-note-info i { color: var(--brand-accent); }

/* Studio URL slug editor */
.slug-input-group { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.slug-input { flex: 0 1 18rem; min-width: 0; }
.slug-suffix { font-family: var(--font-mono); color: var(--text-med); white-space: nowrap; }
.slug-status { font-size: .8rem; margin: 6px 0 0; min-height: 1.1em; }
.slug-status-checking { color: var(--text-light); }
.slug-status-ok  { color: var(--success, #15803d); font-weight: 600; }
.slug-status-bad { color: var(--danger); font-weight: 600; }
.slug-status-warn { color: var(--brand-accent-strong, #c2410c); font-weight: 600; }

/* Feature gating — locked premium features (visible but grayed) + upgrade modal */
.feature-locked { opacity: .62; }
.feature-lock-row { cursor: pointer; }
.feature-lock-row [disabled] { cursor: pointer; pointer-events: none; }
.feature-lock-badge {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: .7rem; font-weight: 600; color: var(--text-med);
    background: var(--bg-soft); border: 1px solid var(--border);
    border-radius: 999px; padding: 2px 9px; white-space: nowrap;
}
.feature-lock-badge i { color: var(--brand-accent); }
.upgrade-modal-icon {
    width: 48px; height: 48px; margin: 0 auto var(--sp-3);
    display: flex; align-items: center; justify-content: center;
    border-radius: 50%; background: var(--brand-accent-tint, #fff2ec);
    color: var(--brand-accent); font-size: 1.2rem;
}
.upgrade-modal-lead { text-align: center; font-size: 1.02rem; margin: 0 0 var(--sp-2); }
.upgrade-modal .settings-modal-body { text-align: center; }
.upgrade-modal-error { color: var(--danger, #c2410c); margin: var(--sp-3) 0 0; }
/* Single, prominent action — no easy "out" alongside it; cost note beneath. */
.upgrade-modal-foot { flex-direction: column; align-items: stretch; gap: var(--sp-2); }
.upgrade-modal-foot .btn-full { width: 100%; }
.upgrade-modal-cost { margin: 0; text-align: center; font-size: .85rem; color: var(--text-med); }

/* Billing & Plan page */
.billing-summary {
    display: flex; flex-wrap: wrap; gap: var(--sp-5); align-items: center;
    background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-lg);
    padding: var(--sp-5) var(--sp-6); margin-bottom: var(--sp-7);
}
.billing-summary-main { display: flex; flex-direction: column; }
.billing-summary-eyebrow { font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: var(--text-light); }
.billing-summary-tier { font-size: 1.5rem; font-weight: 800; }
.billing-summary-meter { flex: 1 1 240px; min-width: 200px; }
.billing-summary-meter-head { display: flex; justify-content: space-between; font-size: .8rem; color: var(--text-med); margin-bottom: 6px; }
.billing-meter-track { height: 8px; border-radius: 999px; background: var(--bg-soft); overflow: hidden; }
.billing-meter-fill { height: 100%; background: var(--brand-accent); border-radius: 999px; min-width: 4px; }
.billing-summary-meta { font-size: .82rem; color: var(--text-med); margin-top: 4px; }

/* Subscription status chip */
.billing-status-chip {
    display: inline-block; vertical-align: middle; margin-left: 8px;
    font-size: .7rem; font-weight: 700; letter-spacing: .02em; text-transform: uppercase;
    padding: 3px 9px; border-radius: 999px; line-height: 1.4;
}
.billing-status-chip.is-active    { background: #e7f6ec; color: #1c7a3f; }
.billing-status-chip.is-trial     { background: var(--brand-accent-tint, #fff2ec); color: var(--brand-accent-strong, #c2410c); }
.billing-status-chip.is-canceling,
.billing-status-chip.is-paused    { background: #fef3e2; color: #9a5b00; }
.billing-status-chip.is-pastdue   { background: #fde8e8; color: #b42318; }
.billing-status-chip.is-canceled  { background: var(--bg-soft); color: var(--text-med); }

/* Management panel */
.billing-manage {
    border: 1px solid var(--border); border-radius: var(--radius-lg);
    background: var(--bg); margin-bottom: var(--sp-7); overflow: hidden;
}
.billing-manage-row {
    display: flex; flex-direction: column; gap: var(--sp-3);
    padding: var(--sp-5) var(--sp-6); border-bottom: 1px solid var(--border);
}
.billing-manage-row:last-child { border-bottom: 0; }
.billing-manage-label { display: flex; flex-direction: column; gap: 3px; }
.billing-manage-title { font-weight: 700; font-size: .98rem; }
.billing-manage-sub { font-size: .85rem; color: var(--text-med); }
@media (min-width: 600px) {
    .billing-manage-row { flex-direction: row; align-items: center; justify-content: space-between; gap: var(--sp-5); }
    .billing-manage-row .btn, .billing-manage-row .storage-stepper { flex: 0 0 auto; }
}

/* Storage +/- stepper */
.storage-stepper { display: inline-flex; align-items: center; gap: 10px; }
.storage-step-btn {
    width: 36px; height: 36px; border-radius: 50%; border: 1px solid var(--border);
    background: var(--bg); font-size: 1.1rem; line-height: 1; cursor: pointer; color: var(--text);
    display: inline-flex; align-items: center; justify-content: center; transition: border-color .15s;
}
.storage-step-btn:hover { border-color: var(--brand-accent); }
.storage-step-val { min-width: 24px; text-align: center; font-weight: 700; }
.storage-step-apply:disabled { opacity: .45; cursor: default; }

/* Danger ghost button (cancel) */
.btn-danger-ghost { color: #b42318; }
.btn-danger-ghost:hover { background: #fde8e8; color: #911e12; }

/* Billing history */
.billing-invoices { padding: var(--sp-5) var(--sp-6); }
.billing-invoices-title { font-size: .9rem; font-weight: 700; margin: 0 0 var(--sp-3); }
.billing-invoice-list { list-style: none; margin: 0; padding: 0; }
.billing-invoice-item {
    display: flex; align-items: center; gap: var(--sp-4); padding: 8px 0;
    border-top: 1px solid var(--border); font-size: .85rem;
}
.billing-invoice-item:first-child { border-top: 0; }
.billing-invoice-date { flex: 1 1 auto; color: var(--text-med); }
.billing-invoice-amt { font-weight: 700; }
.billing-invoice-status { font-size: .72rem; text-transform: uppercase; letter-spacing: .02em; color: var(--text-light); }
.billing-invoice-status-paid { color: #1c7a3f; }
.billing-invoice-link { font-weight: 600; }

/* Payment modal */
/* Narrow the dialog itself (not just the form) so the card form fills it edge
   to edge — no dead backdrop gap inside that looked off-center + closed on click. */
.pay-modal { width: min(440px, calc(100vw - 2rem)); }
.pay-element { margin: var(--sp-3) 0 0; }
/* Promo / discount code field in the subscribe modal */
.pay-promo { margin-top: var(--sp-3); }
.pay-promo-row { display: flex; gap: var(--sp-2); }
.pay-promo-row .form-control { flex: 1 1 auto; }
.pay-promo-msg { margin: 6px 0 0; }
.pay-promo-msg.is-ok  { color: #1c7a3f; }
.pay-promo-msg.is-err { color: #b42318; }

/* The Stripe Card Element chrome */
#payment-element {
    border: 1px solid var(--border); border-radius: 10px;
    padding: 14px var(--sp-4); background: var(--bg);
}

/* Cancellation flow */
.cancel-modal { width: min(460px, calc(100vw - 2rem)); }
.cancel-reasons { display: flex; flex-direction: column; gap: var(--sp-2); }
.cancel-reason {
    display: flex; align-items: center; gap: var(--sp-3); cursor: pointer;
    padding: var(--sp-3) var(--sp-4); border: 1px solid var(--border); border-radius: 10px;
    transition: border-color .12s, background .12s;
}
.cancel-reason:hover { border-color: var(--brand-accent); }
.cancel-reason:has(input:checked) { border-color: var(--brand-accent); background: var(--brand-accent-tint, #fff2ec); }
.cancel-reason input { accent-color: var(--brand-accent); }
.cancel-foot { display: flex; flex: 1 1 100%; justify-content: flex-end; gap: var(--sp-3); }
.cancel-foot[hidden] { display: none; }
.cancel-offer { text-align: center; padding: var(--sp-3) 0 var(--sp-2); }
.cancel-offer-badge {
    display: inline-block; font-weight: 800; font-size: .82rem; letter-spacing: .04em;
    color: #fff; background: var(--brand-accent); padding: 5px 12px; border-radius: 999px; margin-bottom: var(--sp-3);
}
.cancel-offer-title { margin: 0 0 var(--sp-2); font-size: 1.3rem; font-weight: 800; }

/* Change-plan itemized breakdown */
.change-plan-modal { width: min(480px, calc(100vw - 2rem)); }
.cp-lines { list-style: none; margin: 0 0 var(--sp-3); padding: 0; }
.cp-line {
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-4);
    padding: 8px 0; border-bottom: 1px solid var(--border); font-size: .9rem;
}
.cp-line:last-child { border-bottom: 0; }
.cp-line-desc { color: var(--text-med); }
.cp-line-amt { font-weight: 700; white-space: nowrap; font-variant-numeric: tabular-nums; }
.cp-line-credit .cp-line-amt { color: #1c7a3f; }   /* credits in green */
.cp-total {
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-4);
    padding-top: var(--sp-3); border-top: 2px solid var(--border); font-size: 1rem;
}
.cp-total strong { font-size: 1.25rem; font-weight: 800; font-variant-numeric: tabular-nums; }

/* Field note — warning variant */
.field-note-warn { color: #9a5b00; }

/* Team page */
.team-seatbar {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-2) var(--sp-4);
    background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-lg);
    padding: var(--sp-5) var(--sp-6); margin-bottom: var(--sp-5);
}
.team-seatbar-count { font-size: 1.6rem; font-weight: 800; }
.team-seatbar-of { font-weight: 600; color: var(--text-light); font-size: 1.1rem; }
.team-seatbar-label { font-size: .9rem; color: var(--text-med); }
.team-seatbar-note { margin-left: auto; font-size: .85rem; color: var(--text-med); }
.team-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-3); }
.team-member {
    display: flex; align-items: center; gap: var(--sp-4);
    background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-lg);
    padding: var(--sp-4) var(--sp-5);
}
.team-avatar {
    flex: 0 0 auto; width: 40px; height: 40px; border-radius: 50%;
    background: var(--brand-accent-tint, #fff2ec); color: var(--brand-accent-strong, #c2410c);
    display: inline-flex; align-items: center; justify-content: center; font-weight: 800;
}
.team-member-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; }
.team-member-name { font-weight: 700; display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.team-member-email { font-size: .85rem; color: var(--text-med); overflow: hidden; text-overflow: ellipsis; }
.team-member-meta { font-size: .8rem; color: var(--text-light); white-space: nowrap; }
.team-chip { font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .02em; padding: 2px 8px; border-radius: 999px; }
.team-chip-owner { background: #e7f6ec; color: #1c7a3f; }
.team-chip-pending { background: #fef3e2; color: #9a5b00; }
@media (max-width: 599px) {
    .team-member-meta { display: none; }
}

/* Billing-state banner (top of every admin page) */
.portal-billing-banner {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-4);
    padding: var(--sp-3) var(--sp-4); border-radius: var(--radius-md, 10px);
    margin-bottom: var(--sp-5); font-size: .9rem; border: 1px solid transparent;
}
.portal-billing-banner.is-info   { background: var(--brand-accent-tint, #fff2ec); color: var(--brand-accent-strong, #c2410c); border-color: #ffd9c7; }
.portal-billing-banner.is-warn   { background: #fef3e2; color: #9a5b00; border-color: #f6d9a8; }
.portal-billing-banner.is-danger { background: #fde8e8; color: #b42318; border-color: #f6c4c0; }
.portal-billing-banner > span { flex: 1 1 240px; }
.portal-billing-banner-cta { font-weight: 700; white-space: nowrap; color: inherit; text-decoration: underline; }

/* Plans header + monthly/annual toggle */
.plans-head { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--sp-3); margin-bottom: var(--sp-5); }
.plans-title { font-size: 1.35rem; font-weight: 800; margin: 0; }
.billing-toggle { display: inline-flex; background: var(--bg-soft); border: 1px solid var(--border); border-radius: 999px; padding: 3px; }
.billing-toggle-opt {
    display: inline-flex; align-items: center; gap: 8px; border: 0; background: transparent; cursor: pointer;
    font-family: inherit; font-size: .85rem; font-weight: 600; color: var(--text-med);
    padding: 7px 16px; border-radius: 999px; transition: background .15s, color .15s;
}
.billing-toggle-opt.is-active { background: var(--bg); color: var(--text); box-shadow: 0 1px 3px rgba(10,10,10,.08); }
.billing-toggle-save { font-size: .66rem; font-weight: 700; color: var(--brand-accent); background: var(--brand-accent-tint, #fff2ec); border-radius: 999px; padding: 2px 8px; }

/* Plan cards — HoneyBook-style: header band + full feature list w/ checks+dashes */
.plans-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(248px, 1fr)); gap: var(--sp-5); align-items: stretch; }
.plan-card {
    position: relative; background: var(--bg); border: 1px solid var(--border);
    border-radius: var(--radius-lg); overflow: hidden;
    display: flex; flex-direction: column;
    transition: box-shadow .15s, border-color .15s;
}
.plan-card:hover { box-shadow: 0 12px 30px rgba(10, 10, 10, .09); }
/* Popular: persistent orange emphasis. No transform — so it never shrinks on hover. */
.plan-card-popular { border-color: var(--brand-accent); box-shadow: 0 12px 30px rgba(255, 107, 53, .16); }
.plan-card-popular:hover { box-shadow: 0 16px 38px rgba(255, 107, 53, .22); }

/* Header band (flush to the top edges) */
.plan-band {
    text-align: center; font-size: .68rem; font-weight: 800; text-transform: uppercase; letter-spacing: .07em;
    padding: 9px; color: #fff;
}
.plan-band-popular { background: var(--brand-accent); }
.plan-band-current { background: var(--ink, #0a0a0a); }
.plan-band.is-empty { background: transparent; }   /* spacer so names align */

.plan-card-inner { padding: var(--sp-6); display: flex; flex-direction: column; flex: 1; }
.plan-card-name { font-size: 1.3rem; font-weight: 800; margin: 0 0 var(--sp-3); }
.plan-price { display: flex; align-items: baseline; gap: 6px; }
.plan-price-amount { font-size: 2.7rem; font-weight: 800; line-height: 1; letter-spacing: -.02em; }
.plan-price-period { font-size: .95rem; font-weight: 600; color: var(--text-light); }
.plan-price-note { font-size: .75rem; color: var(--text-light); margin: 6px 0 var(--sp-4); min-height: 1.1em; }
.plan-card-tagline { font-size: .85rem; line-height: 1.45; color: var(--text-light); margin: 0 0 var(--sp-5); min-height: 2.6em; }

.plan-card-cta { margin-bottom: var(--sp-5); }
.plan-cta-current {
    display: flex; align-items: center; justify-content: center; gap: 7px; width: 100%;
    padding: 11px; border: 1px solid var(--border); border-radius: var(--radius);
    font-size: .9rem; font-weight: 700; color: var(--brand-accent-strong, #c2410c); background: var(--brand-accent-tint, #fff2ec);
}

.plan-features {
    list-style: none; padding: var(--sp-5) 0 0; margin: auto 0 0;
    border-top: 1px solid var(--border-soft);
    display: flex; flex-direction: column; gap: 11px; font-size: .86rem; color: var(--text);
}
.plan-feature { display: flex; align-items: flex-start; gap: 9px; line-height: 1.35; }
.plan-feature i { color: var(--success, #15803d); margin-top: 2px; flex: 0 0 14px; text-align: center; }
.plan-feature-dash { color: var(--text-light); margin-top: 1px; flex: 0 0 14px; text-align: center; font-weight: 700; }
.plan-feature-out { color: var(--text-light); }

/* Signup — "check your inbox" success state */
.signup-sent-icon {
    width: 56px; height: 56px; margin: 0 auto var(--sp-4);
    display: flex; align-items: center; justify-content: center;
    border-radius: 50%; background: var(--brand-accent-tint, #fff2ec);
    color: var(--brand-accent); font-size: 1.4rem;
}
.signup-sent-title { font-size: 1.25rem; font-weight: 800; margin: 0 0 var(--sp-2); }
.dd-action-form { display: inline-block; margin-left: var(--sp-2); }
.form-help {
    display: block;
    font-size: .85rem;
    color: var(--text-light);
    margin-top: var(--sp-2);
}
.form-field {
    margin-bottom: var(--sp-5);
}
.form-control-upper { text-transform: uppercase; }

/* Card-style form container */
.form-card {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--sp-6) var(--sp-6);
}

/* Sectioned form — premium create/edit layout. One bordered card split into
 * labelled sections (label + description on the left, fields on the right at
 * wide widths), with a footer action bar. Constrained width so fields aren't
 * stretched edge-to-edge. */
.form-stack {
    max-width: 940px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
}
/* On Settings pages, the form cards span the full content width so they line up
   with the tab divider + the processor cards (which use .form-card, uncapped),
   instead of sitting cramped at 940px with a gap on the right. Scoped to the
   settings sub-nav so other forms (New project, etc.) keep their tidy cap. */
.settings-subnav ~ .form-stack { max-width: none; }
.settings-subnav ~ .usage-card { max-width: none; }
.form-section {
    padding: var(--sp-6);
    border-bottom: 1px solid var(--border-soft);
}
.form-section-head { margin-bottom: var(--sp-5); }
.form-section-title { font-size: 1rem; font-weight: 700; margin: 0 0 4px; color: var(--text); }
.form-section-sub { font-size: .85rem; line-height: 1.45; color: var(--text-light); margin: 0; }
/* Trim trailing field margin so the section's own padding sets the rhythm. */
.form-section-fields > .form-field:last-child { margin-bottom: 0; }
.form-section-fields > .form-row-2:last-child .form-field { margin-bottom: 0; }
.form-section-fields > .wm-section:last-child { margin-bottom: 0; }
.form-actions {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-5) var(--sp-6);
    background: var(--bg-soft);
}
@media (min-width: 860px) {
    .form-section {
        display: grid;
        grid-template-columns: 248px 1fr;
        gap: var(--sp-7);
        align-items: start;
    }
    .form-section-head { margin-bottom: 0; padding-top: 2px; }
    /* Full-width variant — header stacked on top, content spans the row.
       For wide content like tables that shouldn't be squeezed into a column. */
    .form-section.is-full { display: block; }
    .form-section.is-full .form-section-head { margin-bottom: var(--sp-5); padding-top: 0; }
}

/* Two-column form row that collapses on mobile */
.form-row-2 {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--sp-3) var(--sp-5);
}
@media (min-width: 600px) {
    .form-row-2 { grid-template-columns: 1fr 1fr; }
}

/* Fieldset block (used for grouped address fields) */
.form-fieldset {
    border: 0;
    padding: 0;
    margin: 0 0 var(--sp-5);
}
.form-legend {
    font-size: .92rem;
    font-weight: 500;
    color: var(--text);
    margin: 0 0 var(--sp-3);
    padding: 0;
}

/* Definition-list pattern for read-only detail panels */
.detail-list {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--sp-3);
    margin: 0;
}
.detail-list dt {
    font-size: .8rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text-light);
    margin: 0;
}
.detail-list dd {
    margin: 2px 0 0;
    color: var(--text);
    font-size: .95rem;
}
@media (min-width: 700px) {
    .detail-list { grid-template-columns: 200px 1fr; align-items: baseline; }
    .detail-list dt { margin-top: 4px; }
    .detail-list dd { margin: 0; }
}
/* Description folded into the Project Details card — a labelled block below the
   detail list, separated by a hairline (only shown when there's text). */
.detail-description {
    margin-top: var(--sp-5);
    padding-top: var(--sp-5);
    border-top: 1px solid var(--border-soft);
}
.detail-description-label {
    display: block;
    font-size: .8rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text-light);
    margin-bottom: var(--sp-2);
}

/* Detail grid: cards auto-fit. With one card it takes full width; with two
   or more they split into equal columns down to a 20rem minimum. */
.portal-detail-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--sp-5);
}
@media (min-width: 800px) {
    .portal-detail-grid {
        grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
    }
}

/* Inline search field in toolbars */
.portal-search {
    flex: 1;
    min-width: 12rem;
    margin-left: auto;
}
.portal-search .form-control {
    max-width: 24rem;
    margin-left: auto;
}

/* Whole-row click affordance on data tables */
.row-link { cursor: pointer; }
/* Plain links inside clickable rows pick up body text color (no neon
   blue), but exclude .btn-styled anchors so action buttons keep their
   own color rules (e.g. .btn-primary white-on-brand). Same pattern as
   .data-table tbody a:not(.btn) below. */
.row-link a:not(.btn) { color: var(--text); text-decoration: none; font-weight: 500; }
.row-link a:not(.btn):hover { color: var(--brand); }

/* ---------- STATUS BANNERS ---------- */
.status-banner {
    padding: var(--sp-4) var(--sp-5);
    border-left: 4px solid;
    border-radius: var(--radius);
    font-size: .95rem;
}
.status-banner-success { background: var(--success-bg); border-color: var(--success); color: var(--success); }
.status-banner-warn    { background: var(--warn-bg);    border-color: var(--warn);    color: var(--warn);    }
.status-banner-danger  { background: var(--danger-bg);  border-color: var(--danger);  color: var(--danger);  }
.status-banner-info    { background: var(--info-bg);    border-color: var(--info);    color: var(--info);    }

/* ---------- MARKETING LANDING ---------- */
.marketing {
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    background: var(--bg);
}
.marketing-header {
    padding: var(--sp-5) var(--sp-5);
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.marketing-hero {
    flex: 1;
    padding: var(--sp-7) var(--sp-5) var(--sp-9);
    max-width: 40rem;
    width: 100%;
    margin: 0 auto;
    text-align: center;
}
.marketing-hero-headline {
    font-size: 2.25rem;
    font-weight: 700;
    line-height: 1.1;
    letter-spacing: -.02em;
    margin: 0 0 var(--sp-5);
    color: var(--text);
}
.marketing-hero-sub {
    font-size: 1.1rem;
    color: var(--text-med);
    margin: 0 0 var(--sp-8);
    line-height: 1.5;
}
.marketing-footer {
    padding: var(--sp-7) var(--sp-5) var(--sp-6);
    text-align: center;
    font-size: .85rem;
    color: var(--text-light);
    font-family: var(--font-mono);
}

/* Waitlist form */
.waitlist-form {
    text-align: left;
}

/* ---------- DESKTOP REFINEMENTS ---------- */
@media (min-width: 600px) {
    .marketing-hero-headline { font-size: 3rem; }
    .marketing-hero-sub      { font-size: 1.2rem; }
    .brand-wordmark-xl       { font-size: 3.75rem; }
    .brand-mark-xl           { width: 5rem; height: 5rem; font-size: 3.5rem; }
}
@media (min-width: 900px) {
    .marketing-hero {
        padding-top: var(--sp-12);
        padding-bottom: var(--sp-12);
        max-width: 44rem;
    }
    .marketing-hero-headline { font-size: 3.5rem; }
    .brand-wordmark-xl       { font-size: 4.5rem; }
    .brand-mark-xl           { width: 6rem; height: 6rem; font-size: 4.25rem; }
}

/* ---------- TENANT PORTAL ----------
   Sidebar nav on desktop (>=900px). On mobile, the sidebar collapses;
   a top bar with hamburger toggles a slide-in drawer. */

.portal-body {
    background: var(--bg-soft);
    min-height: 100vh;
    min-height: 100dvh;
}
.portal-shell {
    min-height: 100vh;
    min-height: 100dvh;
}

/* Approximate topbar heights — used to position sticky panels below
 * them. Don't hand-tune without also adjusting the topbar padding /
 * border. */
:root {
    --portal-topbar-h: 3.75rem;     /* mobile portal-topbar (brand + menu) */
    --admin-topbar-h:  3.5rem;      /* page-title topbar (search + bell + profile) */
}

/* Mobile-first defaults: top bar visible, sidebar acts as a full-width drawer
   that slides in BELOW the topbar (the topbar stays visible above it so the
   close button is always reachable). */
.portal-topbar {
    background: var(--bg);
    border-bottom: 1px solid var(--border);
    padding: var(--sp-3) var(--sp-5);
    height: var(--portal-topbar-h);
    display: flex;
    align-items: center;
    justify-content: space-between;
    position: sticky;
    top: 0;
    z-index: 50;
}
.portal-nav-toggle {
    background: transparent;
    border: 0;
    width: 44px;
    height: 44px;
    padding: 0;
    cursor: pointer;
    position: relative;
    display: inline-block;
}
/* CSS-drawn hamburger that animates into an X.
   Three absolute-positioned bars; outer two rotate ±45° and slide to center,
   middle one fades out. Driven by body.portal-nav-open from the toggle JS. */
.portal-nav-toggle-bar {
    display: block;
    position: absolute;
    left: 11px;
    width: 22px;
    height: 2px;
    background: var(--text);
    border-radius: 1px;
    transition: top .25s ease, transform .25s ease, opacity .15s ease;
}
.portal-nav-toggle-bar:nth-child(1) { top: 14px; }
.portal-nav-toggle-bar:nth-child(2) { top: 21px; }
.portal-nav-toggle-bar:nth-child(3) { top: 28px; }

body.portal-nav-open .portal-nav-toggle-bar:nth-child(1) {
    top: 21px;
    transform: rotate(45deg);
}
body.portal-nav-open .portal-nav-toggle-bar:nth-child(2) {
    opacity: 0;
}
body.portal-nav-open .portal-nav-toggle-bar:nth-child(3) {
    top: 21px;
    transform: rotate(-45deg);
}

@media (prefers-reduced-motion: reduce) {
    .portal-nav-toggle-bar { transition: none; }
}

/* Topbar brand: wordmark | sublabel inline, vertically centered around the
   visual midline of the 'E' in 'Edit'. The sublabel needs line-height: 1
   so its line box doesn't add extra whitespace that throws off alignment. */
.brand-topbar {
    align-items: center;
    gap: var(--sp-3);
    min-width: 0;
    flex: 1;
    overflow: hidden;
}
.brand-sublabel-inline {
    line-height: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
/* Thin separator between wordmark and sublabel. Drawn as a 1px line,
   slightly shorter than the wordmark's cap height for visual harmony. */
.brand-separator {
    display: inline-block;
    width: 1px;
    height: 1.1rem;
    background: var(--border-strong);
    flex-shrink: 0;
}

.portal-sidebar {
    background: var(--bg);
    display: flex;       /* always rendered; visibility + transform handle hiding */
    flex-direction: column;
    position: fixed;
    /* Start below BOTH sticky bars — the brand bar (portal-topbar) AND the
     * page-title bar (admin-topbar: search + bell + account), which stays
     * pinned at z:49. Without the admin-topbar offset, that bar overlaps the
     * drawer's first nav item. */
    top: calc(var(--portal-topbar-h) + var(--admin-topbar-h));
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    max-width: none;
    z-index: 40;
    padding: var(--sp-5) var(--sp-4) var(--sp-5);
    overflow-y: auto;

    /* Closed state: slid up out of view behind the topbar, transparent,
       and non-interactive. Visibility hides interaction; transform handles
       the slide; opacity handles the fade. */
    transform: translateY(-8%);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition:
        transform 280ms cubic-bezier(.4, 0, .2, 1),
        opacity 220ms ease,
        visibility 0s linear 280ms;
}
.portal-sidebar.is-open {
    transform: translateY(0);
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition:
        transform 280ms cubic-bezier(.4, 0, .2, 1),
        opacity 220ms ease,
        visibility 0s linear 0s;
}

/* Hide the drawer's own header on mobile — the topbar already shows the brand. */
@media (max-width: 899px) {
    .portal-sidebar-header { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    .portal-sidebar { transition: none; }
}
.portal-sidebar-header { padding: var(--sp-2) var(--sp-3) var(--sp-5); }
.portal-sidebar-header .brand-sublabel { margin: var(--sp-2) 0 0; display: block; }

.portal-sidenav {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    flex: 1;
}
.portal-sidenav-link {
    display: flex;
    align-items: center;
    color: var(--text-med);
    text-decoration: none;
    font-size: .95rem;
    border-radius: var(--radius);
    overflow: hidden;
    transition: background .15s, color .15s;
}
.portal-sidenav-link:hover {
    background: var(--bg-muted);
    color: var(--text);
}
.portal-sidenav-locked { cursor: pointer; opacity: .6; }
.portal-sidenav-locked .portal-sidenav-locktag { margin-left: auto; font-size: .68rem; color: var(--text-light); }
.portal-sidenav-link.is-active {
    background: var(--brand);
    color: var(--text-inverse);
}
.portal-sidenav-link.is-active i { color: var(--text-inverse); }
/* Icon container — its width determines where the icon sits.
   Mobile default; overridden in the desktop media query so it matches the
   collapsed sidebar width and keeps the icon visually centered. */
.portal-sidenav-icon {
    display: inline-block;
    width: 2.5rem;
    text-align: center;
    font-size: 1.05rem;
    line-height: 2.5rem;
    flex-shrink: 0;
}
.portal-sidenav-label {
    padding: 0 var(--sp-3);
    line-height: 2.5rem;
}

.portal-sidebar-footer {
    padding-top: var(--sp-5);
    margin-top: var(--sp-4);
    border-top: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}
.portal-user-card {
    padding: 0 var(--sp-3);
}
.portal-user-name {
    font-size: .95rem;
    font-weight: 500;
    color: var(--text);
}
.portal-user-role {
    margin-top: 2px;
}

/* Plan + storage snapshot in the sidebar footer — ambient usage + soft upsell. */
.portal-usage {
    display: block;
    margin: 0 calc(-1 * var(--sp-2));
    padding: var(--sp-2) var(--sp-3);
    border-radius: var(--radius);
    text-decoration: none;
    color: inherit;
    transition: background-color .15s ease;
}
.portal-usage:hover { background: var(--bg-muted); }
.portal-usage-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--sp-2);
    margin-bottom: 6px;
}
.portal-usage-plan {
    font-size: .72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--brand-accent-strong);
}
.portal-usage-amt {
    font-size: .72rem;
    color: var(--text-light);
    white-space: nowrap;
}
.portal-usage-bar {
    height: 5px;
    border-radius: var(--radius-pill);
    background: var(--bg-muted);
    overflow: hidden;
}
.portal-usage-bar-fill {
    display: block;
    height: 100%;
    min-width: 3px;
    border-radius: var(--radius-pill);
    background: var(--brand-accent);
    transition: width .3s ease;
}
.portal-usage-bar.is-warn .portal-usage-bar-fill { background: #d97706; }
.portal-usage-bar.is-full .portal-usage-bar-fill { background: var(--danger); }

.btn-full > i { margin-right: var(--sp-2); }

.portal-main {
    padding: var(--sp-6) var(--sp-5) var(--sp-9);
    max-width: 1100px;
    margin: 0 auto;
}

/* Lock body scroll when mobile drawer is open */
body.portal-nav-open { overflow: hidden; }

/* Desktop: static sidebar at 16rem, always expanded. Main content takes the
   remaining viewport width up to a 1280px max, centered. */
@media (min-width: 900px) {
    :root { --portal-sidebar-w: 16rem; }

    .portal-topbar { display: none; }

    .portal-shell {
        display: grid;
        grid-template-columns: var(--portal-sidebar-w) 1fr;
        min-height: 100vh;
    }

    .portal-sidebar {
        display: flex;
        position: sticky;
        top: 0;
        left: auto;
        right: auto;
        bottom: auto;
        width: var(--portal-sidebar-w);
        max-width: none;
        height: 100vh;
        border-right: 1px solid var(--border);
        /* No top padding: the "Edit" brand band (below) is sized to the topbar
           height so it and its divider align with the page-title topbar. */
        padding: 0 var(--sp-4) var(--sp-5);
        /* Reset the mobile drawer animation state. */
        transform: none;
        opacity: 1;
        visibility: visible;
        pointer-events: auto;
        transition: none;
    }

    .portal-sidebar-header {
        padding: 0 var(--sp-2) var(--sp-6);
    }
    /* "Edit" sits in a band the same height as the page-title topbar, so the
       wordmark lines up with the topbar title and the divider beneath it lands
       on the same plane as the topbar's bottom border. */
    .portal-sidebar-header .brand-wordmark {
        display: flex;
        align-items: center;
        height: var(--admin-topbar-h);
        margin: 0 calc(var(--sp-2) * -1);
        padding: 0 var(--sp-2);
        border-bottom: 1px solid var(--border);
        box-sizing: border-box;
    }
    /* Stack the brand vertically in the sidebar so long studio names fit:
       "Edit" wordmark on top, then a subtle divider, then the studio name
       in semibold caps as a sublabel. Each line gets full sidebar width. */
    .brand-sidebar {
        flex-direction: column;
        align-items: stretch;
        gap: 0;
        max-width: 100%;
        min-width: 0;
    }
    .brand-sidebar .brand-separator {
        display: none;
    }
    .brand-sidebar .brand-sublabel-inline {
        margin-top: var(--sp-3);
        padding-top: var(--sp-3);
        padding-bottom: var(--sp-3);
        border-top: 1px solid var(--border-soft);
        border-bottom: 1px solid var(--border-soft);
        font-size: .72rem;
        font-weight: 600;
        letter-spacing: .1em;
        text-transform: uppercase;
        color: var(--text);
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        line-height: 1.2;
    }
    /* Default (Edit-branded) sidebar: the studio logo (when uploaded) AND the
       studio name share a divider-framed slot under the "Edit" wordmark.
       Full-width borders match the original text-only treatment. */
    .portal-sidebar-header .brand-id-frame {
        display: flex;
        flex-direction: column;
        gap: var(--sp-2);
        margin-top: 0;
        padding: var(--sp-4) 0;
        border-bottom: 1px solid var(--border-soft);
        min-width: 0;
    }
    .portal-sidebar-header .brand-id-frame .brand-logo {
        align-self: center;        /* logo centered over the studio name, matching the client portal */
        margin: 0;
        max-height: 44px;
    }
    .portal-sidebar-header .brand-id-frame .brand-id-name {
        font-size: .72rem;
        font-weight: 600;
        letter-spacing: .1em;
        text-transform: uppercase;
        color: var(--text);
        line-height: 1.2;
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .portal-sidenav-link {
        padding: var(--sp-3);
        white-space: nowrap;
    }
    .portal-sidenav-icon {
        width: 1.5rem;
        font-size: 1.05rem;
        line-height: 1;
        margin: 0 var(--sp-3) 0 0;
    }
    .portal-sidenav-label {
        padding: 0;
        line-height: 1;
    }

    .portal-main {
        padding: var(--sp-8) var(--sp-8) var(--sp-9);
        width: 100%;
        max-width: 1280px;
        margin: 0 auto;
    }

    body.portal-nav-open { overflow: auto; }
}

/* Portal-section page header */
.portal-page-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--sp-3);
    margin-bottom: var(--sp-6);
}
.portal-page-title {
    font-size: 1.5rem;
    font-weight: 700;
    letter-spacing: -.02em;
    margin: 0;
    color: var(--text);
}
.portal-page-sub {
    color: var(--text-med);
    font-size: .92rem;
    margin: var(--sp-1) 0 0;
}

/* Stat card grid (dashboard) */
.portal-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--sp-4);
}
.portal-stat-card {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--sp-5) var(--sp-5);
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    text-decoration: none;
    color: var(--text);
    transition: border-color .15s, background .15s;
}
.portal-stat-card:hover {
    border-color: var(--border-strong);
    background: var(--bg-soft);
}
.portal-stat-label {
    font-size: .85rem;
    color: var(--text-light);
    text-transform: uppercase;
    letter-spacing: .04em;
}
.portal-stat-value {
    font-size: 2rem;
    font-weight: 700;
    letter-spacing: -.02em;
    color: var(--text);
    line-height: 1;
}
@media (min-width: 600px) {
    .portal-grid { grid-template-columns: repeat(3, 1fr); }
}

/* ---------- ADMIN ---------- */
.admin-page {
    min-height: 100vh;
    min-height: 100dvh; /* iOS Safari: avoid 100vh including the area behind browser chrome */
    display: flex;
    flex-direction: column;
    background: var(--bg-soft);
}
.admin-header {
    background: var(--bg);
    border-bottom: 1px solid var(--border);
    padding: var(--sp-4) var(--sp-5);
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--sp-3);
}
.admin-container {
    width: 100%;
    max-width: 1100px;
    margin: 0 auto;
    padding: var(--sp-6) var(--sp-5);
}

/* God-mode nav */
.admin-nav { display: flex; flex-wrap: wrap; gap: var(--sp-1); flex: 1 1 auto; justify-content: center; }
.admin-nav-link {
    padding: 6px 12px; border-radius: 8px; font-size: .9rem; font-weight: 600; color: var(--text-med);
    text-decoration: none; transition: background .12s, color .12s;
}
.admin-nav-link:hover { background: var(--bg-soft); color: var(--text); }
.admin-nav-link.is-active { background: var(--brand-accent-tint, #fff2ec); color: var(--brand-accent-strong, #c2410c); }
.admin-header .brand-sublabel { color: var(--brand-accent-strong, #c2410c); font-weight: 700; }

/* God-mode metric cards */
.gm-metrics { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-4); margin-bottom: var(--sp-6); }
@media (min-width: 760px) { .gm-metrics { grid-template-columns: repeat(4, 1fr); } }
.gm-metric { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: var(--sp-5); }
.gm-metric-label { font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: var(--text-light); }
.gm-metric-value { font-size: 1.6rem; font-weight: 800; margin-top: 4px; }
.gm-metric-sub { font-size: .8rem; color: var(--text-med); margin-top: 2px; }

/* Legal docs (Terms / Privacy) */
.legal-page { min-height: 100vh; background: var(--bg); }
.legal-header {
    display: flex; align-items: center; justify-content: space-between;
    max-width: 760px; margin: 0 auto; padding: var(--sp-5) var(--sp-5) 0;
}
.legal-nav { display: flex; gap: var(--sp-4); font-size: .9rem; }
.legal-nav a { color: var(--text-med); text-decoration: none; }
.legal-nav a:hover { color: var(--brand-accent); }
.legal-doc { max-width: 760px; margin: 0 auto; padding: var(--sp-5) var(--sp-5) var(--sp-8); line-height: 1.7; color: var(--text); }
.legal-title { font-size: 2rem; font-weight: 800; margin: var(--sp-5) 0 4px; }
.legal-updated { color: var(--text-light); font-size: .85rem; margin: 0 0 var(--sp-6); }
.legal-doc h2 { font-size: 1.2rem; font-weight: 800; margin: var(--sp-6) 0 var(--sp-2); }
.legal-doc p, .legal-doc li { color: var(--text-med); }
.legal-doc ul { padding-left: 1.2em; margin: var(--sp-2) 0; }
.legal-doc li { margin-bottom: 6px; }
.legal-doc a { color: var(--brand-accent-strong, #c2410c); }
.legal-rule { border: 0; border-top: 1px solid var(--border); margin: var(--sp-6) 0 var(--sp-4); }
.legal-foot { font-size: .85rem; color: var(--text-light); }
.lp-footer-legal { font-size: .85rem; }
.lp-footer-legal a { color: inherit; text-decoration: underline; opacity: .85; }
.lp-footer-legal a:hover { opacity: 1; }

/* Settings card (e.g. custom domain) */
.settings-card {
    background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-lg);
    padding: var(--sp-5) var(--sp-6);
}
.settings-card-title { font-size: 1.05rem; font-weight: 700; margin: 0 0 var(--sp-2); }
.cd-state { display: inline-flex; align-items: center; gap: 8px; font-weight: 600; margin: 0 0 var(--sp-3); }
.cd-state-ok { color: #1c7a3f; }
.cd-state-pending { color: #9a5b00; }
.cd-add { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: center; }
.cd-add .form-control { flex: 1 1 280px; max-width: 360px; }
.cd-dns {
    display: flex; flex-direction: column; gap: 6px;
    background: var(--bg-soft); border: 1px solid var(--border); border-radius: 10px;
    padding: var(--sp-3) var(--sp-4); font-family: var(--font-mono, monospace); font-size: .85rem;
    word-break: break-all;
}
.cd-dns-k { display: inline-block; min-width: 64px; color: var(--text-light); font-family: var(--font-sans, inherit); }

/* God-mode storage-by-type rows */
.gm-storagerow { margin-bottom: var(--sp-3); }
.gm-storagerow:last-child { margin-bottom: 0; }
.gm-storagerow-head { display: flex; justify-content: space-between; font-size: .85rem; margin-bottom: 5px; }

/* God-mode profile action panels */
.gm-panel { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: var(--sp-5) var(--sp-6); margin-bottom: var(--sp-5); }
.gm-panel-title { font-size: .95rem; font-weight: 700; margin: 0 0 var(--sp-4); }
.gm-kv { display: grid; grid-template-columns: minmax(140px, max-content) 1fr; gap: 6px var(--sp-5); font-size: .9rem; }
.gm-kv dt { color: var(--text-med); }
.gm-kv dd { margin: 0; font-weight: 600; word-break: break-word; }
.gm-actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: flex-end; }
.gm-actions form { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: flex-end; }
.gm-actions .form-field { margin: 0; }
.gm-input-sm { max-width: 120px; }
.gm-upper { text-transform: uppercase; }

/* God-mode studios filter bar */
.gm-filter { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: flex-end; margin-bottom: var(--sp-5); }
.gm-filter input[type="search"] { max-width: 360px; }
.gm-filter select { max-width: 200px; }

/* God-mode impersonation banner (on the studio's own portal) */
.god-banner {
    position: sticky; top: 0; z-index: 1000;
    display: flex; align-items: center; justify-content: center; gap: var(--sp-3);
    background: #1f2937; color: #fff; padding: 8px var(--sp-4); font-size: .85rem; font-weight: 600;
}
.god-banner a { color: #fff; text-decoration: underline; }
.admin-login-shell {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--sp-7) var(--sp-5);
}
.admin-login-card {
    width: 100%;
    max-width: 22rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--sp-7);
    box-shadow: var(--shadow);
}

/* Data table (port from TP CLAUDE pattern) */
.data-table-wrap {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
}
.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: .92rem;
}
.data-table th, .data-table td {
    text-align: left;
    padding: var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--border-soft);
    vertical-align: top;
}
.data-table th {
    background: var(--bg-soft);
    font-weight: 600;
    color: var(--text-med);
    font-size: .82rem;
    text-transform: uppercase;
    letter-spacing: .02em;
}
.data-table tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr:hover { background: var(--bg-soft); }
.data-table form { display: inline; margin: 0; }

/* Anchors inside table rows get an explicit hover underline so users can
   tell they're a separate clickable target from the whole-row .row-link.
   Without this, hovering a row that ALSO contains an anchor child gives
   no visual cue that the anchor will go somewhere different. */
/* Plain links inside table rows pick up the body text color (no neon-blue
   default), but exclude .btn-styled anchors so an inline action button
   keeps its own color (e.g. .btn-primary white-on-brand). Without the
   :not(.btn), the Pay button on the client invoices list renders as
   black text on black bg = invisible. */
.data-table tbody a:not(.btn) {
    color: var(--text);
    text-decoration: none;
    transition: color .12s ease;
}
.data-table tbody a:not(.btn):hover {
    text-decoration: underline;
    text-underline-offset: 3px;
    text-decoration-thickness: 1px;
}

/* Mobile-friendly card layout for narrow screens */
@media (max-width: 599px) {
    .data-table thead { display: none; }
    .data-table, .data-table tbody, .data-table tr, .data-table td {
        display: block;
        width: 100%;
    }
    .data-table tr {
        border-bottom: 1px solid var(--border);
        padding: var(--sp-3) 0;
    }
    .data-table td {
        border-bottom: none;
        padding: var(--sp-1) var(--sp-4);
    }
    .data-table td::before {
        content: attr(data-label);
        display: block;
        font-size: .72rem;
        text-transform: uppercase;
        color: var(--text-light);
        letter-spacing: .05em;
        margin-bottom: 2px;
    }
}

.empty-state {
    padding: var(--sp-10) var(--sp-5);
    text-align: center;
    color: var(--text-light);
}

/* ---------- STATUS CHIPS ----------
   Reusable pills for status badges in tables AND status filter chips.
   The same class hierarchy works for both — link variants add hover/active
   states on top of the colored backgrounds. */
.status-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-1);
    padding: .3rem .7rem;
    font-size: .78rem;
    font-weight: 500;
    border-radius: var(--radius-pill);
    text-decoration: none;
    white-space: nowrap;
    line-height: 1;
    background: var(--bg-muted);
    color: var(--text-med);
    border: 1px solid transparent;
}
a.status-chip { transition: opacity .15s, box-shadow .15s; }
a.status-chip:hover { opacity: .85; }
a.status-chip.is-active {
    box-shadow: 0 0 0 2px var(--text);
}

/* Per-status colors (used both as table badges and as colored filter chips) */
.status-chip-tentative       { background: #fef3c7;           color: #92400e; } /* amber-100 / amber-800 — pre-booked / scheduling */
.status-chip-booked          { background: var(--info-bg);    color: var(--info); }
.status-chip-proofing        { background: var(--bg-muted);   color: var(--text-light); } /* gray — proofing is a passive "waiting on client" state */
.status-chip-post_processing { background: #f3e8ff;           color: #6b21a8; }
.status-chip-delivered       { background: var(--success-bg); color: var(--success); }
.status-chip-cancelled,
.status-chip-archived        { background: var(--bg-muted);   color: var(--text-light); }

.status-chips {
    flex-wrap: wrap;
    gap: var(--sp-2);
}

/* ---------- ACTIVITY TIMELINE ----------
   Dot + content layout used on project view pages. */
.activity-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--sp-4);
}
.activity-list > li {
    display: flex;
    gap: var(--sp-3);
    align-items: flex-start;
}
.activity-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--brand);
    margin-top: 7px;
    flex-shrink: 0;
}

/* ---------- FLATPICKR OVERRIDES ----------
   Brand-matched theme on top of the vendor base. Selectors use the
   .flatpickr-* class names from the library. */
.flatpickr-calendar {
    font-family: var(--font-sans);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    border: 1px solid var(--border);
    background: var(--bg);
}
.flatpickr-calendar.arrowTop:before,
.flatpickr-calendar.arrowBottom:before {
    border-bottom-color: var(--border);
    border-top-color: var(--border);
}
.flatpickr-calendar.arrowTop:after,
.flatpickr-calendar.arrowBottom:after {
    border-bottom-color: var(--bg);
    border-top-color: var(--bg);
}
.flatpickr-months,
.flatpickr-current-month,
.flatpickr-monthDropdown-months,
.flatpickr-current-month input.cur-year {
    font-family: var(--font-sans);
    color: var(--text);
}
.flatpickr-current-month {
    font-weight: 600;
    font-size: .95rem;
}
.flatpickr-weekday {
    font-family: var(--font-sans);
    font-weight: 500;
    color: var(--text-light);
    font-size: .78rem;
    text-transform: uppercase;
    letter-spacing: .03em;
}
.flatpickr-day {
    border-radius: var(--radius);
    color: var(--text);
    font-weight: 400;
}
.flatpickr-day:hover,
.flatpickr-day:focus {
    background: var(--bg-muted);
    border-color: transparent;
    color: var(--text);
}
.flatpickr-day.today {
    border-color: var(--border-strong);
}
.flatpickr-day.today:hover {
    background: var(--bg-muted);
    color: var(--text);
}
.flatpickr-day.selected,
.flatpickr-day.selected:hover,
.flatpickr-day.selected:focus,
.flatpickr-day.startRange,
.flatpickr-day.endRange {
    background: var(--brand);
    border-color: var(--brand);
    color: var(--text-inverse);
}
.flatpickr-day.prevMonthDay,
.flatpickr-day.nextMonthDay {
    color: var(--text-light);
}
.flatpickr-day.flatpickr-disabled,
.flatpickr-day.flatpickr-disabled:hover {
    color: var(--border-strong);
    background: transparent;
}
.flatpickr-prev-month,
.flatpickr-next-month {
    color: var(--text-med);
    fill: var(--text-med);
}
.flatpickr-prev-month:hover,
.flatpickr-next-month:hover {
    color: var(--text);
    fill: var(--text);
}
.flatpickr-prev-month:hover svg,
.flatpickr-next-month:hover svg {
    fill: var(--text);
}
.empty-state-title {
    font-size: 1.1rem;
    font-weight: 500;
    color: var(--text);
    margin: 0 0 var(--sp-2);
}
.empty-state-desc {
    font-size: .95rem;
    margin: 0;
}

/* ── Invoice status chips ─────────────────────────────────────────────────── */
.status-chip-draft       { background: var(--bg-muted);    color: var(--text-light); }
.status-chip-sent        { background: var(--info-bg);     color: var(--info);       }
.status-chip-viewed      { background: #ede9fe;            color: #5b21b6;           }
.status-chip-paid        { background: var(--success-bg);  color: var(--success);    }
.status-chip-overdue     { background: #fee2e2;            color: #991b1b;           }
.status-chip-void        { background: var(--bg-muted);    color: var(--text-light); text-decoration: line-through; }
.status-chip-refunded    { background: var(--warn-bg);     color: var(--warn);       }
/* Bucket-only (filter chips on the list page; not a real invoice status). */
.status-chip-outstanding { background: #fef3c7;            color: #92400e;           }

/* ── Agreement status chips ───────────────────────────────────────────────── */
/* draft, sent, viewed, void reuse the invoice palette above — same intent. */
.status-chip-signed        { background: var(--success-bg); color: var(--success); }
.status-chip-countersigned { background: #d1fae5;           color: #065f46;        }

/* ── Invoice line items (form) ────────────────────────────────────────────── */
.invoice-line-items {
    border: 1px solid var(--border);
    border-radius: 10px;
    overflow: hidden;
    background: var(--bg-soft);
}
.invoice-line-head,
.invoice-line-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 90px 130px 110px 38px;
    gap: var(--sp-3);
    align-items: center;
    padding: var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--border-soft);
}
.invoice-line-head {
    background: #fff;
    padding-top: var(--sp-2);
    padding-bottom: var(--sp-2);
    font-weight: 500;
}
.invoice-line-row:last-of-type { border-bottom: 1px solid var(--border-soft); }
.invoice-line-row .form-control { margin: 0; }
.invoice-line-amount {
    text-align: right;
    font-size: .95rem;
    color: var(--text);
}
.invoice-line-foot {
    padding: var(--sp-3) var(--sp-4);
    background: #fff;
}

/* Read-only marker on invoice views/PDF: this line was excluded from the % discount. */
.line-no-disc-tag {
    display: inline-block;
    margin-left: 8px;
    font-size: .68rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--text-light);
    background: var(--bg-soft);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 1px 6px;
    vertical-align: middle;
    white-space: nowrap;
}

/* Per-line "exclude from discount" toggle — only shown in % discount mode.
   Green = included in the discount, red = excluded (pass-through fees). */
.line-exempt-toggle, .line-exempt-head { display: none; }
.line-exempt-head { text-align: center; }
.invoice-line-items.discount-percent .line-exempt-head { display: block; }
.invoice-line-items.discount-percent .line-exempt-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    margin: 0 auto;
    border-radius: 8px;
    cursor: pointer;
    border: 1px solid var(--success);
    background: var(--success-bg);
    color: var(--success);
    font-size: .8rem;
    transition: background .15s, color .15s, border-color .15s;
}
.invoice-line-items.discount-percent .line-exempt-toggle:hover { filter: brightness(.97); }
.invoice-line-items.discount-percent .line-exempt-toggle.is-exempt {
    border-color: var(--danger);
    background: var(--danger-bg);
    color: var(--danger);
}
/* Desktop: percent mode adds a 6th column for the toggle (before remove). */
@media (min-width: 701px) {
    .invoice-line-items.discount-percent .invoice-line-head,
    .invoice-line-items.discount-percent .invoice-line-row {
        grid-template-columns: minmax(0, 1fr) 90px 130px 110px 44px 38px;
    }
}

/* Mobile: collapse the grid into a single-column stack per row */
@media (max-width: 700px) {
    .invoice-line-head { display: none; }
    .invoice-line-row {
        grid-template-columns: 1fr 38px;
        grid-template-areas:
            "desc    remove"
            "qty     remove"
            "unit    remove"
            "amount  remove";
        align-items: center;
    }
    .invoice-line-row > input:nth-child(1)         { grid-area: desc;   }
    .invoice-line-row > input[data-line-qty]       { grid-area: qty;    }
    .invoice-line-row > input[data-line-unit]      { grid-area: unit;   }
    .invoice-line-row > [data-line-amount]         { grid-area: amount; }
    .invoice-line-row > [data-line-remove]         { grid-area: remove; align-self: start; }

    /* Percent mode: give the exempt toggle its own slot top-right, × below it. */
    .invoice-line-items.discount-percent .invoice-line-row {
        grid-template-areas:
            "desc    exempt"
            "qty     remove"
            "unit    remove"
            "amount  remove";
    }
    .invoice-line-items.discount-percent .invoice-line-row > [data-line-exempt] {
        grid-area: exempt;
        justify-self: end;
    }
}

.btn-icon {
    padding: 6px 8px;
    line-height: 1;
}

/* ── Invoice summary card (totals: subtotal / tax / discount / total) ─────── */
.invoice-summary {
    margin-top: var(--sp-5);
    width: 100%;
    padding: var(--sp-5);
    border: 1px solid var(--border);
    border-radius: 12px;
    background: #fff;
}
.invoice-summary-title {
    margin: 0 0 var(--sp-3);
    font-size: 1rem;
    font-weight: 600;
    color: var(--text);
    text-transform: uppercase;
    letter-spacing: .04em;
    padding-bottom: var(--sp-3);
    border-bottom: 1px solid var(--border-soft);
}
.invoice-summary-row {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: var(--sp-4);
    padding: var(--sp-3) 0;
}
.invoice-summary-row + .invoice-summary-row {
    border-top: 1px solid var(--border-soft);
}
.invoice-summary-label {
    font-size: .95rem;
    font-weight: 500;
    color: var(--text);
    margin: 0;
}
.invoice-summary-value {
    font-size: 1rem;
    font-weight: 500;
    color: var(--text);
}
.invoice-summary-row-grand {
    border-top: 2px solid var(--text) !important;
    margin-top: var(--sp-2);
    padding-top: var(--sp-4);
    padding-bottom: var(--sp-2);
}
.invoice-summary-row-grand .invoice-summary-label {
    font-size: 1.05rem;
    font-weight: 600;
}
.invoice-summary-row-grand .invoice-summary-value {
    font-size: 1.25rem;
    font-weight: 700;
}

/* Input cluster: symbol + (optional toggle) + number input */
.invoice-summary-input-wrap {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    justify-self: end;
}
/* Computed $ value of a % discount, shown next to the rate input. */
.invoice-summary-discount-amount {
    min-width: 84px;
    text-align: right;
    color: var(--text-light);
    font-size: .95rem;
}
.invoice-summary-discount-amount:empty { min-width: 0; }
/* Optional discount-label input occupying the left cell of the discount row. */
.invoice-summary-row-discount { align-items: center; }
.invoice-discount-label { max-width: 340px; }
.invoice-summary-symbol {
    font-size: .95rem;
    color: var(--text-light);
    font-weight: 500;
    min-width: 14px;
    text-align: right;
}
.invoice-summary-input {
    margin: 0;
    width: 110px;
    text-align: right;
}

/* $/% segmented toggle */
.discount-type-toggle {
    display: inline-flex;
    border: 1px solid var(--border-strong);
    border-radius: 8px;
    overflow: hidden;
    background: var(--bg-muted);
}
.discount-type-btn {
    appearance: none;
    background: transparent;
    border: 0;
    padding: 4px 10px;
    font-size: .9rem;
    font-weight: 600;
    color: var(--text-light);
    cursor: pointer;
    line-height: 1.4;
    transition: background .15s, color .15s;
}
.discount-type-btn + .discount-type-btn {
    border-left: 1px solid var(--border-strong);
}
.discount-type-btn:hover { color: var(--text); }
.discount-type-btn.is-active {
    background: var(--text);
    color: #fff;
}

/* Read-only variant used on the view page */
.invoice-summary-readonly {
    background: var(--bg-soft);
}

/* ── Invoice number display (read-only, replaces the old Title input) ─────── */
.invoice-number-display {
    display: flex;
    align-items: center;
    min-height: 42px;
    padding: 0 var(--sp-4);
    border: 1px dashed var(--border-strong);
    border-radius: 8px;
    background: var(--bg-soft);
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: .03em;
    color: var(--text);
}

/* ─────────────────────────────────────────────────────────────────────────
   Client portal layout
   Mirrors the studio admin layout — uses the same .portal-* classes. The
   client-specific styles below are limited to dashboard cards, page
   headers, and the coming-soon interstitial.
   ───────────────────────────────────────────────────────────────────────── */
.client-page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--sp-5);
    margin-bottom: var(--sp-6);
    flex-wrap: wrap;
}
.client-page-title {
    font-size: 1.6rem;
    font-weight: 700;
    margin: 0 0 var(--sp-2);
    color: var(--text);
    letter-spacing: -0.01em;
}
.client-page-sub {
    margin: 0;
    color: var(--text-light);
    font-size: .95rem;
}

/* Outstanding-balance callout above the dashboard */
.client-callout {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    padding: var(--sp-5);
    border-radius: 12px;
    background: #fff;
    border: 1px solid var(--border);
    margin-bottom: var(--sp-6);
    flex-wrap: wrap;
}
.client-callout-warn {
    border-color: #fed7aa;
    background: #fffbeb;
}
.client-callout-figure {
    font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace);
    font-size: 1.75rem;
    font-weight: 700;
    margin: 0;
    color: var(--text);
}

/* Pay-by-check note inside a .client-callout (icon + stacked text, left-aligned) */
.invoice-check-note {
    display: flex;
    align-items: flex-start;
    gap: var(--sp-3);
}
.invoice-check-note > i {
    color: var(--brand-accent-strong, #c2410c);
    font-size: 1.25rem;
    line-height: 1.4;
    flex-shrink: 0;
}

/* Two-column grid (projects | invoices) on the dashboard */
.client-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--sp-5);
}
@media (min-width: 900px) {
    .client-grid { grid-template-columns: 1fr 1fr; }
}
.client-card {
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: var(--sp-5);
}
/* A rich project table inside a padded client card — bleed it to the card's
   side edges so its rows read edge-to-edge (matching the studio dashboard),
   with the table's own cell padding providing the inset. */
.client-card-table {
    margin: var(--sp-3) calc(var(--sp-5) * -1) 0;
}
.client-card-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--sp-3);
    margin-bottom: var(--sp-4);
    padding-bottom: var(--sp-3);
    border-bottom: 1px solid var(--border-soft);
}
.client-card-title {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    color: var(--text);
}

/* Compact list inside dashboard cards */
.client-list {
    list-style: none;
    margin: 0;
    padding: 0;
}
.client-list li + li {
    border-top: 1px solid var(--border-soft);
}
.client-list-item {
    display: block;
    padding: var(--sp-3) 0;
    color: inherit;
    text-decoration: none;
}
.client-list-item:hover { background: var(--bg-soft); }
.client-list-main {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    margin-bottom: 2px;
}

/* ─────────────────────────────────────────────────────────────────────────
   Project detail two-column layout
   Main content on the left, sticky-ish right rail with client info +
   comments + internal notes. Stacks to one column on tablet/mobile.
   ───────────────────────────────────────────────────────────────────────── */
.project-layout {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--sp-5);
}
@media (min-width: 1000px) {
    .project-layout { grid-template-columns: 1fr 360px; align-items: start; }
}
.project-main {
    display: flex;
    flex-direction: column;
    gap: var(--sp-5);
    min-width: 0;
}
.project-aside {
    display: flex;
    flex-direction: column;
    gap: var(--sp-5);
    min-width: 0;
}

/* ─────────────────────────────────────────────────────────────────────────
   Card section — native <details>/<summary> accordion. JS animates the
   body's height (inline style) to/from scrollHeight on toggle. Native
   browser handles state (the [open] attribute), JS only does the
   animation. Bulletproof: works with or without JS (snaps without).
   ───────────────────────────────────────────────────────────────────────── */
.card-section {
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 12px;
    overflow: hidden;
    /* Avoid the default details margin-bottom in some browsers */
}
.card-section-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-4) var(--sp-5);
    color: var(--text);
    cursor: pointer;
    /* Remove the native disclosure triangle */
    list-style: none;
    user-select: none;
}
.card-section-head::-webkit-details-marker { display: none; }
.card-section-head::marker { display: none; }
.card-section[open] .card-section-head { border-bottom: 1px solid var(--border-soft); }
.card-section-head:hover { background: var(--bg-soft); }
.card-section-title {
    margin: 0;
    font-size: .98rem;
    font-weight: 600;
    color: var(--text);
    display: inline-flex;
    align-items: center;
    gap: 10px;
}
.card-section-title i { color: var(--text); font-size: .9rem; }
.card-section-chevron {
    color: var(--text-light);
    font-size: .8rem;
    transition: transform .22s ease;
}
.card-section[open] .card-section-chevron { transform: rotate(0deg); }
.card-section:not([open]) .card-section-chevron { transform: rotate(180deg); }

/* Per-card inline edit affordances (project page). */
.card-section-head-actions {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-3);
    flex: 0 0 auto;
}
/* Quiet, borderless edit affordance — no more bordered "cheap button" look.
   It appears only once a card is open (collapsed cards stay clean). */
.card-edit-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border: none;
    background: transparent;
    color: var(--text-light);
    font-size: .8rem;
    font-weight: 500;
    line-height: 1;
    padding: 5px 9px;
    border-radius: 7px;
    cursor: pointer;
    transition: color .15s ease, background .15s ease;
}
.card-edit-btn:hover {
    color: var(--text);
    background: var(--bg-muted);
}
.card-edit-btn i { font-size: .75rem; color: var(--text-light); transition: color .15s ease; }
.card-edit-btn:hover i { color: var(--text); }
/* Gate to open cards only — the affordance lives "inside" the card. */
.card-section:not([open]) .card-edit-btn { display: none; }
.card-edit-form { animation: cardEditFadeIn .18s ease; }
@keyframes cardEditFadeIn { from { opacity: 0; transform: translateY(-2px); } to { opacity: 1; transform: none; } }

/* ── Settings modal (native <dialog>) ─────────────────────────────────────────
   Centered card on a dimmed backdrop, title + close, scrolling body, footer
   with the single ink primary on the right. Reusable beyond gallery settings. */
.settings-modal {
    width: min(560px, calc(100vw - 2rem));
    /* dvh so iOS excludes the browser chrome / home bar; vh first as fallback. */
    max-height: 85vh;
    max-height: 85dvh;
    padding: 0;
    border: none;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    background: #fff;
    color: var(--text);
    overflow: hidden;
}
.settings-modal[open] { animation: settingsModalIn .16s ease; }
@keyframes settingsModalIn {
    from { opacity: 0; transform: translateY(8px) scale(.99); }
    to   { opacity: 1; transform: none; }
}
.settings-modal::backdrop {
    background: rgba(10, 10, 10, .45);
    animation: settingsBackdropIn .16s ease;
}
@keyframes settingsBackdropIn { from { opacity: 0; } to { opacity: 1; } }
.settings-modal-form {
    display: flex;
    flex-direction: column;
    max-height: 85vh;
    max-height: 85dvh;
}
.settings-modal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    padding: var(--sp-5) var(--sp-6);
    border-bottom: 1px solid var(--border);
    flex: 0 0 auto;
}
.settings-modal-title { margin: 0; font-size: 1.05rem; font-weight: 700; color: var(--text); }
.settings-modal-close {
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    border: none;
    background: transparent;
    color: var(--text-light);
    border-radius: var(--radius);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.05rem;
    transition: background .15s, color .15s;
}
.settings-modal-close:hover { background: var(--bg-muted); color: var(--text); }
.settings-modal-body {
    padding: var(--sp-6);
    overflow-y: auto;
    flex: 1 1 auto;
    min-height: 0;            /* let the body scroll instead of overflowing the modal */
}
.settings-modal-foot {
    display: flex;
    justify-content: flex-end;
    gap: var(--sp-3);
    padding: var(--sp-4) var(--sp-6);
    /* Clear the iOS home indicator so the buttons stay tappable. */
    padding-bottom: max(var(--sp-4), env(safe-area-inset-bottom));
    border-top: 1px solid var(--border);
    background: var(--bg-soft);
    flex: 0 0 auto;
}
@media (prefers-reduced-motion: reduce) {
    .settings-modal[open], .settings-modal::backdrop { animation: none; }
}

/* Client summary card on the STUDIO project page. Namespaced pclient-* so it
   never collides with the CLIENT portal's own .client-card* header styles. */
.pclient-head {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    text-decoration: none;
    padding-bottom: var(--sp-4);
    border-bottom: 1px solid var(--border-soft);
}
.pclient-id { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pclient-name {
    font-weight: 700;
    font-size: 1.05rem;
    color: var(--brand);
    line-height: 1.2;
}
.pclient-head:hover .pclient-name { text-decoration: underline; }
.pclient-company { font-size: .9rem; color: var(--text-light); }
.pclient-contact {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
    padding-top: var(--sp-4);
}
.pclient-line {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    font-size: .95rem;
    color: var(--text);
    text-decoration: none;
    line-height: 1.45;
}
a.pclient-line { color: var(--brand); }
a.pclient-line:hover span { text-decoration: underline; }
.pclient-line i {
    color: var(--text-light);
    font-size: .85rem;
    width: 16px;
    text-align: center;
    margin-top: 3px;
    flex-shrink: 0;
}
.pclient-line-static { color: var(--text); }

/* ── Client detail page (studio): stat strip + billing rollup + actions ── */
.stat-strip {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--sp-4);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--sp-5);
    margin-bottom: var(--sp-5);
}
.stat-strip-cell { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.stat-strip-label { font-size: .68rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--text-light); }
.stat-strip-value { font-size: .95rem; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@media (min-width: 700px)  { .stat-strip { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1100px) { .stat-strip { grid-template-columns: repeat(6, 1fr); } }

.billing-quickref {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--sp-5);
    flex-wrap: wrap;
}
.billing-quickref-value {
    font-size: 2rem;
    font-weight: 800;
    color: var(--text);
    line-height: 1.05;
}
.billing-quickref-control {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 190px;
}

/* Invoices tab quick-reference stats — Outstanding + Paid (with timeframe). */
.invoice-stats {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--sp-4);
    margin-bottom: var(--sp-5);
}
@media (min-width: 700px) { .invoice-stats { grid-template-columns: 1fr 1fr; } }
.invoice-stat {
    display: flex;
    align-items: center;
    gap: var(--sp-4);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--sp-4) var(--sp-5);
}
.invoice-stat-icon {
    flex: 0 0 auto;
    width: 44px; height: 44px;
    border-radius: 12px;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 1.05rem;
}
.invoice-stat-icon-out  { background: var(--danger-bg);  color: var(--danger); }
.invoice-stat-icon-paid { background: var(--success-bg); color: var(--success); }
.invoice-stat-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.invoice-stat-value { font-size: 2.1rem; font-weight: 800; color: var(--text); line-height: 1.05; letter-spacing: -.01em; }
.invoice-stat-label { font-size: .8rem; color: var(--text-light); display: inline-flex; align-items: center; gap: 6px; }
.invoice-stat-select {
    border: 0;
    background: transparent;
    color: var(--text-light);
    font: inherit;
    font-size: .8rem;
    cursor: pointer;
    padding: 0 2px;
    -webkit-appearance: auto;
    appearance: auto;
}

.client-actions { display: flex; flex-direction: column; gap: var(--sp-3); }
.client-actions form { margin: 0; }
.btn-block { display: flex; width: 100%; justify-content: center; }

/* Danger zone card — red-tinted so a destructive delete reads as serious. */
.client-danger { border-color: var(--danger-border); background: var(--danger-bg); }
.client-danger .card-section-title i { color: var(--danger); }
.client-danger .card-section[open] .card-section-head,
.client-danger .card-section-head { border-color: var(--danger-border); }
.client-danger form { margin: 0; }

/* Body — JS sets inline height during the animation. Padding intentionally
   lives on the INNER wrapper, NOT here. If padding were on the body itself,
   it would fight for space as the body shrinks toward 0 (padding has nowhere
   to "go") which produces the jitter at the end of a close animation.
   With padding on inner, the body cleanly shrinks via overflow:hidden and
   the inner gets clipped naturally without any padding gymnastics. */
.card-section-body {
    overflow: hidden;
    transition: height .22s cubic-bezier(.4, 0, .2, 1);
}
.card-section-body-inner {
    padding: var(--sp-5);
}
.card-section-body-inner.p-0 { padding: 0; }

/* Tinted variant for "team-only" sections — distinguishes internal
   stuff from client-facing visually. */
.card-section-internal {
    background: #fefce8;
    border-color: #fde68a;
}
.card-section-internal .card-section-head:hover { background: #fef3c7; }
.card-section-internal .card-section-title i { color: var(--warn); }

/* Internal-note form field (project create) — amber to signal "team only",
   matching the yellow Internal Notes card it feeds into. */
.form-field-internal .form-label i { color: var(--warn); margin-right: 2px; }
.form-field-internal .form-control {
    background: #fefce8;
    border-color: #fde68a;
}
.form-field-internal .form-control:focus {
    border-color: var(--warn);
    outline-color: var(--warn);
}
.form-field-internal .form-control::placeholder { color: #b59a4d; }

/* Module slot grid (placeholder cards inside Modules section) */
.module-slot-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--sp-3);
}
.module-slot {
    padding: var(--sp-4);
    background: var(--bg-soft);
    border: 1px dashed var(--border-strong);
    border-radius: 10px;
}
.module-slot-title {
    margin: 0 0 var(--sp-2);
    font-size: .95rem;
    font-weight: 600;
    color: var(--text);
}

/* ─────────────────────────────────────────────────────────────────────────
   Messages thread (project comments + reactions)
   Used on both /portal/projects/<id> and /client/projects/<id>.
   ───────────────────────────────────────────────────────────────────────── */
.messages-section { display: flex; flex-direction: column; gap: var(--sp-4); }
/* Used to be a section header above each thread; now the card-section
   header handles the title. Keep the rule in case it's referenced elsewhere. */
.messages-section-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--sp-3);
    flex-wrap: wrap;
}
/* In the right-rail context, messages are tighter — even smaller avatars,
   compact compose footer. */
.project-aside .messages-list { gap: var(--sp-4); }
.project-aside .message-avatar { --avatar-size: 24px; }
.project-aside .messages-compose-foot {
    flex-direction: column;
    align-items: stretch;
    gap: var(--sp-2);
}
.project-aside .messages-compose-foot button { align-self: flex-end; }

.messages-empty {
    text-align: center;
    padding: var(--sp-6) var(--sp-4);
    color: var(--text-light);
    border: 1px dashed var(--border);
    border-radius: 10px;
}
.messages-empty i {
    display: block;
    font-size: 1.5rem;
    margin-bottom: var(--sp-2);
    opacity: .6;
}

/* Text-message style: each row aligns to the sender's side. The viewer's
   own messages push to the right edge; everyone else's stay left. Avatar
   tucks right next to the bubble on the appropriate side.

   The list itself is capped so the thread never grows the card past a
   sane height — overflow scrolls within the card. Subtle scrollbar
   styling keeps it from feeling like a textarea. */
.messages-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;   /* default; .message-own overrides via align-self */
    gap: var(--sp-5);
    max-height: 520px;
    overflow-y: auto;
    /* Small inset padding so right-aligned bubbles don't kiss the
       scrollbar / card edge. Avatars get a little air too. */
    padding-right: var(--sp-2);
    /* Firefox */
    scrollbar-width: thin;
    scrollbar-color: var(--border-strong) transparent;
}
.messages-list::-webkit-scrollbar { width: 8px; }
.messages-list::-webkit-scrollbar-track { background: transparent; }
.messages-list::-webkit-scrollbar-thumb {
    background: var(--border-strong);
    border-radius: 8px;
}
.messages-list::-webkit-scrollbar-thumb:hover { background: var(--text-light); }

.message {
    display: flex;
    align-items: flex-end;     /* avatar sits flush with bottom of bubble */
    gap: var(--sp-2);
    max-width: 88%;            /* keep bubbles from spanning full width */
    min-width: 0;
}
.message-avatar { flex-shrink: 0; --avatar-size: 28px; }

.message-body-wrap {
    min-width: 0;
    background: var(--bg-soft);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: var(--sp-3) var(--sp-4);
}

/* Viewer's own messages — flip side + subtle accent. Premium-SaaS feel:
   barely-there background tint, but a clearly-blue border carries the
   "this is you" cue. Anything louder feels like a chat toy, not a tool. */
.message-own {
    align-self: flex-end;
    flex-direction: row-reverse;
}
.message-own .message-body-wrap {
    background: #f5f9ff;       /* near-white, faintest blue wash */
    border-color: #60a5fa;     /* clearly blue but not loud */
}
.message-own .message-meta { justify-content: flex-end; }
.message-own .message-reactions { justify-content: flex-end; }

/* Internal notes always look like internal notes regardless of side. */
.message-internal .message-body-wrap {
    background: #fffbeb;       /* faint warm wash */
    border-color: #fde68a;
}
.message-own.message-internal .message-body-wrap {
    /* Internal-and-mine: keep the warm wash, bump the border so "mine"
       still reads from across the thread. */
    background: #fffbeb;
    border-color: #f59e0b;
}

.message-meta {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    flex-wrap: wrap;
    margin-bottom: var(--sp-2);
    font-size: .85rem;
}
.message-author { font-weight: 600; color: var(--text); }
.message-time { color: var(--text-light); font-family: var(--font-mono); font-size: .78rem; }
.message-internal-badge {
    font-size: .7rem;
    font-weight: 600;
    color: var(--warn);
    background: var(--warn-bg);
    padding: 2px 8px;
    border-radius: 4px;
    text-transform: uppercase;
    letter-spacing: .06em;
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

.message-text {
    color: var(--text);
    line-height: 1.55;
    white-space: pre-wrap;
    word-wrap: break-word;
}

/* Reactions */
.message-reactions {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: var(--sp-3);
    flex-wrap: wrap;
    position: relative;
}
.reaction-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    background: var(--bg-muted);
    border: 1px solid var(--border);
    border-radius: 14px;
    cursor: pointer;
    font-family: inherit;
    font-size: .8rem;
    color: var(--text);
    transition: background .12s, border-color .12s;
}
.reaction-chip:hover {
    background: #fff;
    border-color: var(--border-strong);
}
.reaction-chip.is-mine {
    background: var(--info-bg);
    border-color: var(--info);
    color: var(--info);
}
.reaction-chip.is-mine:hover { background: #dbeafe; }
.reaction-chip:disabled { opacity: .6; cursor: wait; }
.reaction-emoji { font-size: .95rem; line-height: 1; }
.reaction-count { font-weight: 600; }

.reaction-add {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: transparent;
    border: 1px dashed var(--border-strong);
    color: var(--text-light);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background .12s, color .12s;
}
.reaction-add:hover {
    background: var(--bg-muted);
    color: var(--text);
}
.reaction-picker {
    position: absolute;
    top: 100%;
    left: 0;
    margin-top: 4px;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
    padding: 4px;
    display: flex;
    gap: 2px;
    z-index: 10;
}
/* Position is computed by JS as position:fixed relative to the viewport
   (see messages.js positionPickerNear), so no per-side CSS override is
   needed. The CSS values above act as a fallback if JS ever fails. */
.reaction-picker[hidden] { display: none; }
.reaction-picker-emoji {
    width: 32px;
    height: 32px;
    border-radius: 6px;
    background: transparent;
    border: 0;
    cursor: pointer;
    font-size: 1.15rem;
    line-height: 1;
    transition: background .12s, transform .12s;
}
.reaction-picker-emoji:hover {
    background: var(--bg-soft);
    transform: scale(1.15);
}

/* Compose form */
.messages-compose {
    margin-top: var(--sp-4);
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}
.messages-compose textarea {
    resize: vertical;
    min-height: 80px;
}
.messages-compose-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    flex-wrap: wrap;
}
.messages-internal-toggle {
    margin: 0;
    flex: 1;
    min-width: 240px;
}

/* "Coming soon" interstitial for unshipped client modules */
.coming-soon-splash {
    text-align: center;
    padding: var(--sp-7) var(--sp-5);
}
.coming-soon-icon {
    width: 72px;
    height: 72px;
    margin: 0 auto var(--sp-4);
    border-radius: 16px;
    background: var(--bg-soft);
    color: var(--text-light);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.8rem;
}
.coming-soon-title {
    margin: 0 0 var(--sp-3);
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--text);
}
.coming-soon-desc {
    max-width: 520px;
    margin: 0 auto var(--sp-4);
    color: var(--text);
    line-height: 1.5;
}

/* Layout polish on invoice view (meta row across the top) */
.invoice-meta-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: var(--sp-4);
    padding-bottom: var(--sp-5);
    border-bottom: 1px solid var(--border-soft);
    margin-bottom: var(--sp-5);
}

/* ─────────────────────────────────────────────────────────────────────────
   Studio dashboard
   Five stat tiles → action items → recent projects + quick actions sidebar
   ───────────────────────────────────────────────────────────────────────── */
.dashboard-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: var(--sp-4);
    margin-bottom: var(--sp-6);
}
.dashboard-stat {
    display: flex;
    align-items: center;
    gap: var(--sp-4);
    padding: var(--sp-5);
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 12px;
    text-decoration: none;
    color: inherit;
    cursor: pointer;
    transition: background .15s, border-color .15s, box-shadow .15s, transform .15s;
}
.dashboard-stat:hover {
    background: var(--bg-soft);
    border-color: var(--text-light);
    box-shadow: 0 4px 16px rgba(0, 0, 0, .06);
    transform: translateY(-1px);
}
.dashboard-stat:active {
    transform: translateY(0);
    box-shadow: 0 2px 8px rgba(0, 0, 0, .04);
}
.dashboard-stat-icon {
    flex-shrink: 0;
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-muted);
    border-radius: 10px;
    color: var(--text-light);
    font-size: 1.15rem;
}
.dashboard-stat-icon-money {
    background: var(--success-bg);
    color: var(--success);
}
.dashboard-stat-body { min-width: 0; }
.dashboard-stat-value {
    font-size: 1.6rem;
    font-weight: 700;
    line-height: 1.1;
    color: var(--text);
    letter-spacing: -0.01em;
}
.dashboard-stat-label {
    font-size: .72rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-light);
    margin-top: 4px;
    font-weight: 500;
}

/* "You're clear" green banner when there are no action items */
.action-clear {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-3);
    padding: var(--sp-4) var(--sp-5);
    background: var(--success-bg);
    border: 1px solid var(--success-border);
    border-radius: 10px;
    color: var(--success);
    font-weight: 500;
    margin-bottom: var(--sp-6);
}
.action-clear i { font-size: 1.05rem; }

/* Action-items card when there ARE items */
.action-items {
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: var(--sp-5);
    margin-bottom: var(--sp-6);
}
.action-items-list { list-style: none; margin: 0; padding: 0; }
.action-item {
    display: flex;
    align-items: center;
    gap: var(--sp-4);
    padding: var(--sp-3) 0;
    border-top: 1px solid var(--border-soft);
}
.action-item:first-child { border-top: 0; padding-top: 0; }
.action-item:last-child { padding-bottom: 0; }
.action-item-icon {
    width: 38px;
    height: 38px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-muted);
    color: var(--text-light);
    border-radius: 8px;
    flex-shrink: 0;
}
.action-item-warn .action-item-icon {
    background: #fef3c7;
    color: #92400e;
}
.action-item-body { flex: 1; min-width: 0; }
.action-item-text { font-weight: 500; color: var(--text); }
.action-item-sub { margin-top: 2px; }

/* Recent-projects + Quick-actions two-column layout */
.dashboard-main {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--sp-6);
    align-items: start;
}
@media (min-width: 1000px) {
    .dashboard-main { grid-template-columns: minmax(0, 1fr) 340px; }
}
/* Left/right stacks inside the dashboard grid. */
.dashboard-col,
.dashboard-rail {
    display: flex;
    flex-direction: column;
    gap: var(--sp-6);
    min-width: 0;
}
@media (min-width: 1000px) {
    .dashboard-rail { position: sticky; top: var(--sp-6); }
}
.dashboard-section-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-4) var(--sp-5);
    border-bottom: 1px solid var(--border-soft);
    flex-wrap: wrap;
}
.dashboard-section-title {
    margin: 0 0 var(--sp-3);
    font-size: 1rem;
    font-weight: 600;
    color: var(--text);
}

/* Quick-actions sidebar */
.dashboard-quick-actions {
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: var(--sp-5);
    height: fit-content;
}
.dashboard-quick-actions .dashboard-section-title {
    padding-bottom: var(--sp-3);
    border-bottom: 1px solid var(--border-soft);
    margin-bottom: var(--sp-4);
}
.quick-action {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: 10px var(--sp-3);
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 8px;
    color: var(--text);
    text-decoration: none;
    font-weight: 500;
    font-size: .92rem;
    margin-bottom: var(--sp-2);
    transition: background .15s, border-color .15s, transform .1s;
}
.quick-action:last-child { margin-bottom: 0; }
.quick-action:hover {
    background: var(--bg-soft);
    border-color: var(--border-strong);
}
.quick-action i {
    color: var(--text-light);
    width: 16px;
    text-align: center;
}
.quick-action-primary {
    background: var(--text);
    color: #fff;
    border-color: var(--text);
}
.quick-action-primary i { color: rgba(255, 255, 255, .8); }
.quick-action-primary:hover {
    background: #1a1a1a;
    border-color: #1a1a1a;
    color: #fff;
}

/* ── Dashboard panels (This week, Recent activity) ────────────────────────────
   Shares the white-card look of .action-items / .dashboard-quick-actions.
   Seeds the future unified .card primitive (consolidation step 2). */
.dash-panel {
    background: #fff;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--sp-5);
}
.dash-panel-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--sp-3);
    padding-bottom: var(--sp-3);
    border-bottom: 1px solid var(--border-soft);
    margin-bottom: var(--sp-4);
}
.dash-panel-title {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    color: var(--text);
}
.dash-panel-link { text-decoration: none; white-space: nowrap; }
.dash-panel-link:hover { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
.dash-panel-empty { margin: var(--sp-2) 0 0; }

/* This week — chronological shoots + deadlines */
.dash-week-list { list-style: none; margin: 0; padding: 0; }
.dash-week-item {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    border-radius: var(--radius);
    transition: background .12s ease;
}
/* Full-row hover (the tag + action live outside the link, so highlight the
   whole item, not just the link). */
.dash-week-item:hover { background: var(--bg-soft); }
.dash-week-item + .dash-week-item { border-top: 1px solid var(--border-soft); }
.dash-week-link {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: var(--sp-4);
    padding: var(--sp-3) 0;
    text-decoration: none;
    color: var(--text);
}
/* Contextual row action (quiet, on-brand) + the "Reminded" guardrail note. */
.dash-week-action {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    border: none;
    background: transparent;
    color: var(--brand-accent-strong);
    font-size: .74rem;
    font-weight: 600;
    padding: 4px 9px;
    border-radius: var(--radius);
    cursor: pointer;
    white-space: nowrap;
    transition: background .12s ease;
}
.dash-week-action:hover { background: var(--brand-accent-tint); }
.dash-week-action i { font-size: .7rem; }
.dash-week-reminded { flex-shrink: 0; white-space: nowrap; padding-right: var(--sp-1); }
.dash-week-date {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 52px;
    text-align: center;
    line-height: 1.15;
}
.dash-week-day { font-size: .8rem; font-weight: 700; color: var(--text); }
.dash-week-dnum { font-size: .7rem; color: var(--text-light); }
.dash-week-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.dash-week-title {
    font-size: .92rem;
    font-weight: 500;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.dash-week-tag {
    flex-shrink: 0;
    font-size: .68rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    padding: .2rem .5rem;
    border-radius: var(--radius-pill);
    white-space: nowrap;
}
.dash-week-tag-shoot    { background: var(--info-bg); color: var(--info); }
.dash-week-tag-deadline { background: var(--brand-accent-tint); color: var(--brand-accent-strong); }
.dash-week-tag-invoice  { background: var(--danger-bg); color: var(--danger); }

/* Activity feed: the project link under each entry */
.dash-activity-project { color: var(--text-med); text-decoration: none; font-weight: 500; }
.dash-activity-project:hover { color: var(--text); text-decoration: underline; text-underline-offset: 2px; }

/* Scroll affordance: a fading double-chevron shown only while more rows are
   below the fold (toggled via .has-more by the dashboard script). */
.dash-scroll { position: relative; }
.dash-scroll-hint {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 2.6rem;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    padding-bottom: 2px;
    color: var(--text-light);
    background: linear-gradient(to bottom, rgba(255, 255, 255, 0), #fff 78%);
    pointer-events: none;
    opacity: 0;
    transition: opacity .2s ease;
}
.dash-scroll.has-more .dash-scroll-hint { opacity: 1; }
.dash-scroll.has-more .dash-scroll-hint i { animation: dash-hint-bounce 1.5s ease-in-out infinite; }
@keyframes dash-hint-bounce {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(3px); }
}
@media (prefers-reduced-motion: reduce) {
    .dash-scroll.has-more .dash-scroll-hint i { animation: none; }
}

/* Cap the activity feed so it scrolls internally instead of growing forever. */
.dash-activity-scroll {
    max-height: 24rem;
    overflow-y: auto;
    margin-right: calc(var(--sp-3) * -1);   /* let the scrollbar sit in the padding gutter */
    padding-right: var(--sp-3);
    scrollbar-width: thin;
    scrollbar-color: var(--border-strong) transparent;
}
.dash-activity-scroll::-webkit-scrollbar { width: 8px; }
.dash-activity-scroll::-webkit-scrollbar-thumb {
    background: var(--border-strong);
    border-radius: var(--radius-pill);
    border: 2px solid #fff;
}
.dash-activity-scroll::-webkit-scrollbar-track { background: transparent; }

/* ── Password input with eye-icon reveal toggle ───────────────────────────── */
.password-input-wrap {
    position: relative;
    display: block;
}
.password-input-wrap .form-control {
    padding-right: 44px; /* leave room for the toggle button */
}
.password-toggle-btn {
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    background: transparent;
    border: 0;
    color: var(--text-light);
    cursor: pointer;
    padding: 8px 10px;
    border-radius: 6px;
    line-height: 1;
    font-size: .95rem;
    transition: background .15s, color .15s;
}
.password-toggle-btn:hover {
    color: var(--text);
    background: var(--bg-muted);
}
.password-toggle-btn:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: 1px;
}

/* ─────────────────────────────────────────────────────────────────────────
   Admin top bar
   Sits at the top of .portal-main with global search, notification bell,
   and account dropdown. Sticky so it stays visible while scrolling.
   ───────────────────────────────────────────────────────────────────────── */
.admin-topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    padding: var(--sp-3) var(--sp-6);
    background: #fff;
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    /* Above the gallery's sticky action bar (z:40) so the account/notification
     * dropdowns — which live inside this bar's stacking context — aren't
     * clipped behind it. (Matches the mobile override below, which is z:49.) */
    z-index: 45;
    height: var(--admin-topbar-h);
    box-sizing: border-box;
}
/* On mobile, the brand portal-topbar is visible above the admin-topbar.
 * Stack them — admin-topbar sticks at top: portal-topbar-h instead of 0.
 * Without this, BOTH bars stick at viewport top:0; z-index covers most of
 * the conflict, but the admin-topbar's bottom edge can still peek through
 * because the two heights aren't identical. Stacking eliminates the
 * overlap entirely. */
@media (max-width: 900px) {
    .admin-topbar {
        top: var(--portal-topbar-h);
        z-index: 49;     /* below portal-topbar (50), above gallery sticky bar (30) */
    }
}
.topbar-page-title {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    color: var(--text);
    letter-spacing: -0.01em;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Admin pages opt into the topbar layout via portal-main-with-topbar.
   The class strips the default padding + max-width from .portal-main so
   the topbar can span edge-to-edge of the main column. Content centering
   moves to .portal-content. Client portal pages keep the original
   .portal-main behavior (no topbar, content centered inside .portal-main). */
.portal-main-with-topbar {
    padding: 0;
    max-width: none;
    display: flex;
    flex-direction: column;
}
.portal-main-with-topbar .portal-content {
    padding: var(--sp-6) var(--sp-6) var(--sp-7);
    max-width: 1100px;
    margin: 0 auto;
    width: 100%;
}
@media (min-width: 900px) {
    .portal-main-with-topbar .portal-content {
        padding: var(--sp-7) var(--sp-7) var(--sp-9);
        max-width: 1280px;
    }
}

/* Full-bleed override for the client gallery page — photos are the
 * product, give them the full viewport. Body class set by gallery-client.js
 * on init, removed on unload. */
body.is-gallery-page .portal-main-with-topbar .portal-content {
    max-width: none;
    padding-left: var(--sp-4);
    padding-right: var(--sp-4);
}
@media (min-width: 900px) {
    body.is-gallery-page .portal-main-with-topbar .portal-content {
        max-width: none;
        padding-left: var(--sp-5);
        padding-right: var(--sp-5);
    }
}

/* Right-aligned cluster: search trigger + bell + account dropdown.
   All three share the same light-gray-pill resting state for visual
   consistency, with a slightly darker hover state. */
.topbar-right {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}
.topbar-popover { position: relative; }

/* Shared baseline for all three top-bar buttons */
.topbar-search-trigger,
.topbar-icon-btn,
.topbar-account-btn {
    display: inline-flex;
    align-items: center;
    height: 38px;
    background: var(--bg-soft);
    border: 1px solid var(--border);
    color: var(--text);
    cursor: pointer;
    font-family: inherit;
    transition: background .12s, border-color .12s, color .12s;
}
.topbar-search-trigger:hover,
.topbar-icon-btn:hover,
.topbar-account-btn:hover {
    background: var(--bg-muted);
    border-color: var(--border-strong);
}
.topbar-popover.is-open .topbar-icon-btn,
.topbar-popover.is-open .topbar-account-btn {
    background: var(--bg-muted);
    border-color: var(--border-strong);
}

/* Search trigger */
.topbar-search-trigger {
    gap: var(--sp-3);
    padding: 0 14px;
    border-radius: 10px;
    color: var(--text-light);
    font-size: .9rem;
    min-width: 220px;
}
.topbar-search-trigger:hover { color: var(--text); }
.topbar-search-trigger i { font-size: .85rem; }
.topbar-search-trigger span { flex: 1; text-align: left; }
.topbar-kbd {
    font-family: var(--font-mono);
    font-size: .72rem;
    padding: 2px 6px;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 4px;
    color: var(--text-light);
    flex-shrink: 0;
}
@media (max-width: 700px) {
    .topbar-search-trigger {
        min-width: 0;
        padding: 0 12px;
        width: 38px;
        justify-content: center;
    }
    .topbar-search-trigger span,
    .topbar-search-trigger .topbar-kbd { display: none; }
}

/* Bell button — same rounded-rect shape as the search trigger for
   visual cohesion across the topbar cluster. */
.topbar-icon-btn {
    width: 38px;
    justify-content: center;
    border-radius: 10px;
    position: relative;
    color: var(--text-light);
}
.topbar-icon-btn:hover { color: var(--text); }
.topbar-icon-btn i { font-size: .95rem; }
.topbar-badge {
    position: absolute;
    top: -3px;
    right: -3px;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 9px;
    background: var(--danger);
    color: #fff;
    font-size: .65rem;
    font-weight: 700;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    box-shadow: 0 0 0 2px #fff;
    pointer-events: none;
}
/* Explicit hide-when-[hidden] — overrides the display:inline-flex above
   so the JS-controlled "hide when count=0" actually works. */
.topbar-badge[hidden] { display: none !important; }

/* Account button — same 10px rounded-rect as the search trigger and bell */
.topbar-account-btn {
    gap: var(--sp-2);
    padding: 4px 10px 4px 4px;
    border-radius: 10px;
}
.topbar-account-name {
    font-size: .9rem;
    font-weight: 500;
    color: var(--text);
}
.topbar-chevron {
    font-size: .7rem;
    color: var(--text-light);
}
@media (max-width: 700px) {
    .topbar-account-name { display: none; }
}

/* Dropdown panels (notification + account) */
.topbar-dropdown {
    position: absolute;
    top: calc(100% + var(--sp-2));
    right: 0;
    min-width: 280px;
    max-width: 380px;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: 0 10px 40px rgba(0, 0, 0, .12);
    z-index: 30;
    overflow: hidden;
}
.topbar-dropdown[hidden] { display: none; }
.topbar-dropdown-head-actions {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
}
.topbar-dropdown-close {
    background: transparent;
    border: 0;
    width: 28px;
    height: 28px;
    border-radius: 6px;
    color: var(--text-muted);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1rem;
    padding: 0;
    transition: background .12s ease, color .12s ease;
}
.topbar-dropdown-close:hover { background: var(--bg-soft); color: var(--text); }
.topbar-dropdown-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-4);
    border-bottom: 1px solid var(--border-soft);
}
.topbar-dropdown-title {
    margin: 0;
    font-size: .92rem;
    font-weight: 600;
}
.topbar-link-btn {
    background: transparent;
    border: 0;
    color: var(--text-light);
    font-size: .8rem;
    cursor: pointer;
    padding: 4px 8px;
    border-radius: 6px;
    font-family: inherit;
}
.topbar-link-btn:hover { color: var(--text); background: var(--bg-soft); }

/* Account dropdown */
.topbar-account-dropdown { min-width: 260px; }
.topbar-account-head {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-4);
    border-bottom: 1px solid var(--border-soft);
}
.topbar-account-meta { min-width: 0; }
.topbar-account-fullname {
    font-weight: 600;
    color: var(--text);
    font-size: .92rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.topbar-account-email {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.topbar-menu-item {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: 10px var(--sp-4);
    color: var(--text);
    text-decoration: none;
    font-size: .9rem;
    background: transparent;
    border: 0;
    cursor: pointer;
    width: 100%;
    text-align: left;
    font-family: inherit;
}
.topbar-menu-item:hover { background: var(--bg-soft); }
.topbar-menu-item i { width: 16px; color: var(--text-light); text-align: center; }
.topbar-menu-item-danger { color: var(--danger, #b91c1c); }
.topbar-menu-item-danger i { color: var(--danger, #b91c1c); }
.topbar-menu-item-danger:hover { background: var(--danger-bg, #fef2f2); }
.topbar-menu-form { margin: 0; }

/* Notification panel */
.topbar-notif-dropdown {
    min-width: 360px;
    max-height: 70vh;
    display: flex;
    flex-direction: column;
}

/* MOBILE: anchor the notification panel to the viewport instead of the
 * bell button. The 360px min-width was clipping off the left edge on
 * 375px phones because absolute-right-from-bell pushed the panel past
 * viewport left. position:fixed + left/right insets gives us a clean
 * full-width-minus-padding panel that fits any phone.
 *
 * z-index 100 puts the dropdown above the portal-topbar (z:50), the
 * gallery sticky bar (z:30 on mobile), and any other page chrome. It
 * behaves as a modal overlay when open. */
@media (max-width: 600px) {
    .topbar-dropdown.topbar-notif-dropdown {
        position: fixed;
        top: calc(var(--portal-topbar-h) + var(--sp-2));
        left: var(--sp-3);
        right: var(--sp-3);
        min-width: 0;
        max-width: none;
        width: auto;
        max-height: calc(100vh - var(--portal-topbar-h) - var(--sp-4));
        z-index: 100;
    }
}
.topbar-notif-list {
    overflow-y: auto;
    flex: 1;
    max-height: 60vh;
}
.topbar-notif-empty {
    padding: var(--sp-6) var(--sp-4);
    text-align: center;
    color: var(--text-light);
}
.topbar-notif-empty i {
    display: block;
    font-size: 1.5rem;
    margin-bottom: var(--sp-2);
    opacity: .5;
}
/* ── Notification filter tabs (Unread / All) ──────────────────────── */
.topbar-notif-tabs {
    display: flex;
    gap: 0;
    padding: 4px var(--sp-4);
    border-bottom: 1px solid var(--border-soft);
    background: #fff;
}
.topbar-notif-tab {
    appearance: none;
    background: transparent;
    border: 0;
    padding: 8px 12px;
    font: inherit;
    font-size: .85rem;
    font-weight: 500;
    color: var(--text-muted);
    cursor: pointer;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    transition: color .12s, border-color .12s;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.topbar-notif-tab:hover { color: var(--text); }
.topbar-notif-tab.is-active {
    color: var(--text);
    border-bottom-color: var(--brand);
}
.topbar-notif-tab-count {
    background: var(--danger);
    color: #fff;
    font-size: .65rem;
    font-weight: 700;
    line-height: 1;
    padding: 2px 6px;
    border-radius: 9px;
    min-width: 18px;
    text-align: center;
}

/* ── Date-bucket group headers (Today / Yesterday / Earlier) ─────── */
.topbar-notif-group-label {
    padding: var(--sp-3) var(--sp-4) 4px;
    font-size: .65rem;
    font-weight: 700;
    color: var(--text-light);
    text-transform: uppercase;
    letter-spacing: .08em;
}

/* ── Notification item — refined visual hierarchy ─────────────────── */
.topbar-notif-item {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-4);
    color: inherit;
    text-decoration: none;
    border-bottom: 1px solid var(--border-soft);
    background: #fff;
    transition: background .12s;
}
.topbar-notif-item:last-child { border-bottom: 0; }
.topbar-notif-item:hover { background: var(--bg-soft); }

/* Unread state — light background tint + colored left bar.
   Together with the bold title + the right-side dot, three visual
   cues a glance can pick up. */
.topbar-notif-item.is-unread {
    background: #fafafa;
    box-shadow: inset 3px 0 0 var(--brand-accent, #ff6b35);
}
.topbar-notif-item.is-unread:hover { background: #f4f4f4; }

/* Round colored icon badge on the left — replaces the bare dot.
   Tone classes drive the bg color so payment/agreement/etc each get
   their own visual identity. */
.topbar-notif-icon {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: .85rem;
    margin-top: 2px;
}
.topbar-notif-icon-success { background: #d1fae5; color: #065f46; }
.topbar-notif-icon-danger  { background: #fee2e2; color: #991b1b; }
.topbar-notif-icon-info    { background: #dbeafe; color: #1e40af; }
.topbar-notif-icon-neutral { background: var(--bg-soft); color: var(--text-muted); }

/* Unread dot — small accent on the right side of the row */
.topbar-notif-dot {
    position: absolute;
    top: 50%;
    right: 14px;
    transform: translateY(-50%);
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--brand-accent, #ff6b35);
    flex-shrink: 0;
}

.topbar-notif-body-wrap { min-width: 0; flex: 1; padding-right: 18px; }
.topbar-notif-title {
    font-size: .9rem;
    font-weight: 500;
    color: var(--text);
    margin-bottom: 2px;
    line-height: 1.35;
}
.topbar-notif-item.is-unread .topbar-notif-title { font-weight: 600; }
.topbar-notif-body {
    font-size: .8rem;
    color: var(--text-muted);
    line-height: 1.4;
    margin-bottom: 4px;
}
.topbar-notif-time {
    font-size: .7rem;
    color: var(--text-light);
    font-weight: 500;
}

/* Search modal */
.topbar-search-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, .45);
    z-index: 1000;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 10vh var(--sp-4) var(--sp-4);
}
.topbar-search-backdrop[hidden] { display: none; }
body.topbar-search-open { overflow: hidden; }
.topbar-search-modal {
    background: #fff;
    border-radius: 14px;
    width: 100%;
    max-width: 640px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, .25);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    max-height: 70vh;
}
.topbar-search-input-wrap {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-4) var(--sp-5);
    border-bottom: 1px solid var(--border-soft);
}
.topbar-search-input-wrap > i {
    color: var(--text-light);
    font-size: 1rem;
}
.topbar-search-input {
    flex: 1;
    border: 0;
    outline: none;
    background: transparent;
    font-size: 1rem;
    font-family: inherit;
    color: var(--text);
    min-width: 0;
}
.topbar-search-results {
    overflow-y: auto;
    flex: 1;
    padding: var(--sp-2) 0;
}
.topbar-search-hint {
    padding: var(--sp-5) var(--sp-5);
    margin: 0;
    color: var(--text-light);
    text-align: center;
    font-size: .9rem;
}
.topbar-search-group { padding-bottom: var(--sp-2); }
.topbar-search-group-label {
    padding: var(--sp-2) var(--sp-5);
    font-size: .72rem;
    color: var(--text-light);
    letter-spacing: .06em;
    font-weight: 600;
    text-transform: uppercase;
}
.topbar-search-result {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: 10px var(--sp-5);
    color: inherit;
    text-decoration: none;
}
.topbar-search-result:hover,
.topbar-search-result.is-active {
    background: var(--bg-soft);
}
.topbar-search-result > i {
    width: 20px;
    text-align: center;
    color: var(--text-light);
}
.topbar-search-thumb {
    width: 36px;
    height: 36px;
    flex: 0 0 auto;
    object-fit: cover;
    border-radius: 6px;
    background: var(--bg-muted);
}
.topbar-search-result-body { min-width: 0; flex: 1; }
.topbar-search-result-label {
    font-size: .92rem;
    font-weight: 500;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.topbar-search-result-sub {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ── Section divider inside a form-card ───────────────────────────────────── */
.form-divider {
    height: 1px;
    background: var(--border-soft);
    margin: var(--sp-6) calc(var(--sp-6) * -1);
}

/* ─────────────────────────────────────────────────────────────────────────
   Avatars — used in:
     * Sidebar user cards (admin + client)
     * Settings page upload preview
     * Future: activity log, client list, messages
   ───────────────────────────────────────────────────────────────────────── */

/* Base avatar — flexible size via the --avatar-size custom property
   (default 40px; override per usage site). */
.avatar {
    --avatar-size: 40px;
    width: var(--avatar-size);
    height: var(--avatar-size);
    border-radius: 50%;
    background: var(--bg-muted);
    color: var(--text);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: calc(var(--avatar-size) * 0.4);
    overflow: hidden;
    flex-shrink: 0;
    text-transform: uppercase;
    line-height: 1;
}
.avatar img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.avatar-sm  { --avatar-size: 28px; }
.avatar-md  { --avatar-size: 40px; }
.avatar-lg  { --avatar-size: 56px; }
.avatar-xl  { --avatar-size: 80px; }

/* Settings page upload widget */
.avatar-upload-field { margin-bottom: var(--sp-5); }
.avatar-upload-row {
    display: flex;
    align-items: center;
    gap: var(--sp-5);
    flex-wrap: wrap;
}
.avatar-preview {
    width: 80px;
    height: 80px;
    border-radius: 50%;
    background: var(--bg-muted);
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.avatar-preview img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.avatar-preview .avatar-initial {
    font-size: 1.8rem;
    font-weight: 600;
    color: var(--text);
}
.avatar-upload-controls {
    flex: 1;
    min-width: 240px;
}
.avatar-upload-btn { cursor: pointer; }

/* Avatar inside the sidebar user card */
.portal-user-card .avatar {
    --avatar-size: 36px;
    margin-bottom: var(--sp-2);
}

/* ── Avatar crop modal (Cropper.js wrapper) ───────────────────────────────── */
.avatar-cropper-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, .55);
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--sp-4);
}
.avatar-cropper-backdrop[hidden] { display: none !important; }
body.avatar-cropper-open { overflow: hidden; }

.avatar-cropper-modal {
    background: #fff;
    border-radius: 14px;
    overflow: hidden;
    width: 100%;
    max-width: 640px;
    max-height: 90vh;
    display: flex;
    flex-direction: column;
    box-shadow: 0 20px 60px rgba(0, 0, 0, .25);
}
.avatar-cropper-head {
    padding: var(--sp-5) var(--sp-5) var(--sp-4);
    border-bottom: 1px solid var(--border-soft);
}
.avatar-cropper-body {
    padding: var(--sp-5);
    background: var(--bg-soft);
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
    min-height: 0;
}
.avatar-cropper-stage {
    width: 100%;
    aspect-ratio: 1 / 1;
    max-height: 60vh;
    overflow: hidden;
    background: #000;
    border-radius: 10px;
}
.avatar-cropper-stage img {
    display: block;
    max-width: 100%;
}
.avatar-cropper-hint {
    text-align: center;
    margin: 0;
}
.avatar-cropper-foot {
    padding: var(--sp-4) var(--sp-5);
    border-top: 1px solid var(--border-soft);
    display: flex;
    justify-content: space-between;
    gap: var(--sp-3);
}

/* Make the Cropper.js viewbox render as a circle so the user sees the
   actual avatar shape while positioning. */
.avatar-cropper-stage .cropper-view-box,
.avatar-cropper-stage .cropper-face {
    border-radius: 50%;
}
.avatar-cropper-stage .cropper-view-box {
    outline: 2px solid #fff;
    outline-offset: -1px;
}

/* ── Form checkbox row ────────────────────────────────────────────────────── */
.form-check {
    display: flex;
    align-items: flex-start;
    gap: var(--sp-3);
    padding: var(--sp-3) 0;
    cursor: pointer;
}
.form-check input[type="checkbox"] {
    width: 18px;
    height: 18px;
    margin: 2px 0 0;
    accent-color: var(--text);
    cursor: pointer;
    flex-shrink: 0;
}
.form-check-label {
    display: block;
    font-size: .95rem;
    font-weight: 500;
    color: var(--text);
    margin-bottom: 2px;
}
.form-check .form-help {
    margin-top: 2px;
}

/* ── NET preset buttons (under Due date) ──────────────────────────────────── */
.due-date-presets {
    display: flex;
    gap: var(--sp-2);
    margin-top: var(--sp-2);
    flex-wrap: wrap;
}
.due-preset-btn {
    appearance: none;
    background: var(--bg-soft);
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    padding: 4px 12px;
    font-size: .8rem;
    font-weight: 600;
    color: var(--text-light);
    cursor: pointer;
    letter-spacing: .03em;
    transition: background .15s, color .15s, border-color .15s;
}
.due-preset-btn:hover {
    color: var(--text);
    border-color: var(--text-light);
}
.due-preset-btn.is-active {
    background: var(--text);
    color: #fff;
    border-color: var(--text);
}
/* "Custom" calendar pill — opens the date picker. */
.due-preset-cal {
    display: inline-flex;
    align-items: center;
    gap: 5px;
}
.due-preset-cal i { font-size: .78rem; }

/* ── Flush data-table variant (no outer wrap border) ──────────────────────── */
.data-table-flush { border: 0; box-shadow: none; }
.data-table-flush th,
.data-table-flush td { padding-left: 0; padding-right: 0; }


/* ─────────────────────────────────────────────────────────────────────────
   Agreements module
   ───────────────────────────────────────────────────────────────────────── */

/* Status badge variants used in the agreement list. Most of these reuse
   existing palette tokens. */
.badge-purple        { background: #ede9fe; color: #6d28d9; }
.badge-success-strong{ background: #d1fae5; color: #065f46; }
.badge-warn          { background: var(--warn-bg, #fef3c7); color: var(--warn, #92400e); }

/* "Strike-through everywhere" treatment for superseded rows in the list */
.data-table tr.is-superseded td {
    color: var(--text-light);
    text-decoration: line-through;
    text-decoration-color: rgba(0,0,0,.3);
}
/* …and for the rendered doc card */
.is-superseded-doc {
    opacity: .55;
    position: relative;
}
.is-superseded-doc::after {
    content: "SUPERSEDED";
    position: absolute;
    top: 20px;
    right: 20px;
    font-size: .7rem;
    letter-spacing: .15em;
    color: #b91c1c;
    border: 2px solid #b91c1c;
    padding: 4px 10px;
    border-radius: 4px;
    transform: rotate(8deg);
    pointer-events: none;
    font-weight: 700;
}
/* Strike through every line of the actual document body when superseded
   so it's unmistakable visually — opacity-dim alone left the doc still
   reading as authoritative. Apply to paragraphs / headings / list items
   / table cells inside the body so each line gets its own strike rather
   than one long line across blocks. The signer block (signatures, IP,
   timestamps) and the agreement-signers grid below it stay un-struck
   — those record what actually happened and should remain legible. */
.is-superseded-doc .agreement-body p,
.is-superseded-doc .agreement-body h1,
.is-superseded-doc .agreement-body h2,
.is-superseded-doc .agreement-body h3,
.is-superseded-doc .agreement-body h4,
.is-superseded-doc .agreement-body h5,
.is-superseded-doc .agreement-body h6,
.is-superseded-doc .agreement-body li,
.is-superseded-doc .agreement-body td,
.is-superseded-doc .agreement-body th,
.is-superseded-doc .agreement-body blockquote {
    text-decoration: line-through;
    text-decoration-color: rgba(0,0,0,.4);
}

/* Highlight rows in the client list that need action */
.data-table tr.is-needs-action td:first-child {
    box-shadow: inset 3px 0 0 var(--info, #2563eb);
}

/* Two-column signer block under the agreement body */
.agreement-signers {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--sp-5);
    padding-top: var(--sp-4);
    border-top: 1px solid var(--border);
}
.agreement-signer {
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.agreement-signer-label {
    font-size: .7rem;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--text-light);
}
.agreement-signer-name { font-weight: 600; }
.agreement-signature-img {
    display: block;
    margin-top: 6px;
    max-width: 240px;
    max-height: 90px;
    border-bottom: 1px solid var(--border);
    padding-bottom: 4px;
}

/* Signature pad */
.sig-pad {
    position: relative;
    border: 1px solid var(--border-strong);
    border-radius: 8px;
    background: #fff;
    overflow: hidden;
    height: 160px;
}
.sig-pad canvas {
    display: block;
    width: 100%;
    height: 100%;
    cursor: crosshair;
    touch-action: none;
}
.sig-pad.is-empty::before {
    content: "Draw your signature here";
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-light);
    font-size: .9rem;
    pointer-events: none;
    font-style: italic;
}
.sig-clear {
    position: absolute;
    bottom: 6px;
    right: 8px;
    font-size: .75rem;
    color: var(--text-light);
    background: rgba(255,255,255,.85);
    border: 1px solid var(--border);
    padding: 2px 8px;
    border-radius: 4px;
    cursor: pointer;
}
.sig-clear:hover { color: var(--text); }

/* Countersign modal */
.countersign-modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,.45);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    padding: var(--sp-4);
}
.countersign-modal-backdrop[hidden] { display: none; }
.countersign-modal {
    background: #fff;
    border-radius: 12px;
    box-shadow: 0 20px 60px rgba(0,0,0,.25);
    padding: var(--sp-5);
    max-width: 520px;
    width: 100%;
    max-height: 92vh;
    overflow-y: auto;
}
.countersign-modal h2 { margin: 0 0 var(--sp-2); }

/* ── Rendered agreement body (in-app view) ──────────────────────────────────
   Used inside the admin/client agreement detail card. Mirrors the snapshot
   stylesheet at the typography level so an agreement looks identical
   in-app and when printed/downloaded. */
.agreement-body { color: var(--text); line-height: 1.6; }
.agreement-body h2 {
    margin: 0 0 var(--sp-5);
    font-size: 1.05rem;
    text-align: center;
    letter-spacing: .04em;
    font-weight: 700;
}
.agreement-body h3 {
    margin: var(--sp-6) 0 var(--sp-2);
    font-size: .8rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text);
}
.agreement-body p  { margin: var(--sp-2) 0; }
.agreement-body ul { margin: var(--sp-2) 0 var(--sp-2) var(--sp-5); }
.agreement-body strong { font-weight: 600; }

/* Rates table — in-app variant (the snapshot file uses the same class with
   its own literal-value styles so it works standalone). */
.agreement-rates {
    width: 100%;
    border-collapse: collapse;
    margin-top: var(--sp-3);
    border: 1px solid var(--border);
}
.agreement-rates th,
.agreement-rates td {
    padding: var(--sp-3) var(--sp-4);
    border-top: 1px solid var(--border-soft);
    text-align: left;
    font-size: .9rem;
}
.agreement-rates thead th {
    background: var(--bg-soft);
    border-top: 0;
    font-size: .72rem;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-light);
    font-weight: 600;
}
.agreement-rates .price {
    text-align: right;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* ─────────────────────────────────────────────────────────────────────────
   Agreement WYSIWYG editor (templates + new-agreement page)
   ───────────────────────────────────────────────────────────────────────── */
.ag-editor {
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    background: #fff;
    overflow: hidden;
}

.ag-toolbar {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-2);
    background: var(--bg-soft);
    border-bottom: 1px solid var(--border);
    flex-wrap: wrap;
}
.ag-tb-group {
    display: inline-flex;
    border: 1px solid var(--border);
    border-radius: 6px;
    overflow: hidden;
    background: #fff;
}
button.ag-tb {
    appearance: none;
    border: 0;
    background: #fff;
    color: var(--text);
    font-family: inherit;
    font-size: .85rem;
    padding: .35rem .55rem;
    min-width: 32px;
    cursor: pointer;
    line-height: 1.2;
    border-right: 1px solid var(--border);
    transition: background .12s;
}
.ag-tb-group button.ag-tb:last-child { border-right: 0; }
button.ag-tb:hover { background: var(--bg-soft); }
button.ag-tb.is-active {
    background: var(--text);
    color: var(--text-inverse);
}
button.ag-tb.is-active i { color: var(--text-inverse); }

/* Edit/Preview/HTML tabs — pushed to the right side of the toolbar */
.ag-toolbar-tabs {
    margin-left: auto;
    display: inline-flex;
    border: 1px solid var(--border);
    border-radius: 6px;
    overflow: hidden;
    background: #fff;
}
.ag-tab {
    appearance: none;
    border: 0;
    background: #fff;
    color: var(--text-light);
    font-family: inherit;
    font-size: .8rem;
    font-weight: 600;
    padding: .35rem .7rem;
    cursor: pointer;
    border-right: 1px solid var(--border);
    transition: background .12s, color .12s;
}
.ag-tab:last-child { border-right: 0; }
.ag-tab:hover { color: var(--text); }
.ag-tab.is-active {
    background: var(--text);
    color: var(--text-inverse);
}

/* When NOT in rich mode, dim/disable the formatting buttons. They wake up
   the editor automatically if clicked. */
.ag-editor:not(.ag-mode-rich) .ag-tb-group {
    opacity: .45;
}

/* The editing surface. The wrapper has a fixed initial height and can be
   resized vertically only — never horizontally — so the page layout stays
   intact when the user drags the resize handle. */
.ag-stage {
    min-height: 360px;
    height: 480px;
    resize: vertical;
    overflow: hidden;             /* let the child handle its own scrolling */
    position: relative;
}
.ag-rich,
.ag-raw,
.ag-preview {
    width: 100%;
    height: 100%;
    padding: var(--sp-5);
    overflow-y: auto;
    box-sizing: border-box;
    outline: none;
    background: #fff;
    color: var(--text);
}
.ag-rich {
    line-height: 1.6;
    font-size: 15px;
}
.ag-rich:focus { outline: none; }   /* card border carries the focus cue */
.ag-rich h2 {
    margin: 0 0 var(--sp-5);
    font-size: 1.05rem;
    text-align: center;
    letter-spacing: .04em;
    font-weight: 700;
}
.ag-rich h3 {
    margin: var(--sp-6) 0 var(--sp-2);
    font-size: .8rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
}
.ag-rich p  { margin: var(--sp-2) 0; }
.ag-rich ul { margin: var(--sp-2) 0 var(--sp-2) var(--sp-5); }
.ag-rich ol { margin: var(--sp-2) 0 var(--sp-2) var(--sp-5); }
.ag-rich strong { font-weight: 600; }
.ag-rich a { color: var(--info); text-decoration: underline; }

.ag-raw {
    font-family: var(--font-mono);
    font-size: .85rem;
    line-height: 1.55;
    resize: none;
    border: 0;
    background: var(--bg-soft);
}

/* Focused state for the whole editor */
.ag-editor:focus-within {
    border-color: var(--text);
    box-shadow: 0 0 0 3px rgba(0, 0, 0, .06);
}

/* Rates Schedule legend — give it real heading weight, not the default
   tiny fieldset legend look. */
.ag-rates-fieldset {
    border: 0;
    padding: 0;
    margin: var(--sp-6) 0 0;
}
.ag-rates-legend {
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--text);
    padding: 0;
    margin-bottom: var(--sp-2);
}

/* ─────────────────────────────────────────────────────────────────────────
   New-agreement compose: 2-column layout
   ───────────────────────────────────────────────────────────────────────── */
.ag-compose-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 340px;
    gap: var(--sp-5);
    align-items: start;
}
@media (max-width: 1100px) {
    .ag-compose-layout {
        grid-template-columns: 1fr;
    }
}
.ag-compose-main { min-width: 0; }
.ag-compose-aside {
    display: flex;
    flex-direction: column;
    /* Each card is .card; mt-3 between them keeps consistent spacing */
}

.ag-meta-box {
    background: var(--bg-soft);
    border: 1px solid var(--border-soft);
    border-radius: 6px;
    padding: var(--sp-2) var(--sp-3);
    font-size: .82rem;
    margin-bottom: var(--sp-3);
}
.ag-meta-box div { margin: 2px 0; }

/* Settings card checkbox row */
.checkbox-row {
    display: flex;
    align-items: flex-start;
    gap: var(--sp-2);
    font-size: .85rem;
    cursor: pointer;
    margin: var(--sp-2) 0 0;
}
.checkbox-row input { margin-top: .2rem; }

/* Supersedes card — give it a subtle left accent so it reads as a distinct
   "this is the supersession control" without being loud. */
.ag-supersedes-card {
    border-left: 3px solid var(--warn-border, #fed7aa);
}
.ag-supersedes-card .card-title i {
    color: var(--warn, #92400e);
    margin-right: .25rem;
}

/* ─────────────────────────────────────────────────────────────────────────
   Rates Schedule editor (grid layout — no table-cell padding squeezing
   the inputs). Used on both the template editor and the new-agreement
   composer. Function matches TP's compose page; styling is all Edit.
   ───────────────────────────────────────────────────────────────────────── */
.ag-rates-grid {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
}
.ag-rates-head {
    display: grid;
    grid-template-columns: 1fr 180px 40px;
    gap: var(--sp-3);
    padding: 0 var(--sp-2);
    font-size: .7rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--text-light);
    font-weight: 600;
}
.ag-rates-row {
    display: grid;
    grid-template-columns: 1fr 180px 40px;
    gap: var(--sp-3);
    align-items: center;
}
.ag-rates-row .form-control { margin: 0; }
.ag-rates-remove {
    appearance: none;
    border: 1px solid var(--border);
    background: #fff;
    color: var(--text-light);
    width: 40px;
    height: 40px;
    border-radius: 6px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background .12s, color .12s, border-color .12s;
}
.ag-rates-remove:hover {
    background: #fff5f5;
    color: #b91c1c;
    border-color: #fecaca;
}

/* Supersedes panel — tighten internal spacing so it doesn't feel
   unfinished. The orange left accent stays. */
.ag-supersedes-card .card-title {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    margin: 0 0 var(--sp-2);
}
.ag-supersedes-card p {
    line-height: 1.5;
}
.ag-supersedes-card select { margin-top: var(--sp-1); }

/* ─────────────────────────────────────────────────────────────────────────
   Generic .card / .card-padded / .card-title — used throughout the
   agreements module's right-rail panels and elsewhere. Mirrors the
   .client-card visual so admin and client portal cards feel identical.
   ───────────────────────────────────────────────────────────────────────── */
.card {
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 12px;
}
.card-padded { padding: var(--sp-5); }
.card-title {
    margin: 0 0 var(--sp-3);
    font-size: 1rem;
    font-weight: 600;
    color: var(--text);
}

/* Breadcrumb-style nav row above a page header: back-link on the left,
   secondary action(s) on the right. Used on the New Agreement page (and
   reusable wherever a sub-page wants a Back ← / → Action header pair).
   Edge ghost buttons have their horizontal padding stripped so their
   text aligns flush with the page-title heading below. */
.page-subnav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    margin-bottom: var(--sp-3);
}
/* Pull the edge buttons out by their own horizontal padding so their TEXT
   lines up with the content edges, while the hover background stays symmetric
   (not lopsided with the label shoved to one side). */
.page-subnav > .btn-ghost:first-child { margin-left: calc(var(--sp-5) * -1); }
.page-subnav > .btn-ghost:last-child  { margin-right: calc(var(--sp-5) * -1); }

/* ─────────────────────────────────────────────────────────────────────────
   Project view: live "module" lists (agreements + invoices linked to a
   project). Used instead of placeholder "shipping in the X sprint" cards
   once those modules ship.
   ───────────────────────────────────────────────────────────────────────── */
.project-module-list {
    list-style: none;
    margin: 0 0 var(--sp-3);
    padding: 0;
    display: flex;
    flex-direction: column;
    border-top: 1px solid var(--border-soft);
}
.project-module-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-3) 0;
    border-bottom: 1px solid var(--border-soft);
}
.project-module-row-main {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    text-decoration: none;
    color: var(--text);
    flex: 1;
    min-width: 0;
}
.project-module-row-main:hover .project-module-row-title { text-decoration: underline; }
.project-module-row-title { font-weight: 500; }
.project-module-row-meta { flex-shrink: 0; }
.project-module-row.is-superseded .project-module-row-title {
    color: var(--text-light);
    text-decoration: line-through;
}

/* ─────────────────────────────────────────────────────────────────────────
   Public agreement sign page (/sign?t=...)
   Standalone — no admin/client chrome. Lives at the root so non-portal
   signers can use it.
   ───────────────────────────────────────────────────────────────────────── */
.public-sign-body {
    background: var(--bg-soft);
    margin: 0;
    min-height: 100vh;
}
.public-sign-bar {
    background: #fff;
    border-bottom: 1px solid var(--border);
    padding: var(--sp-4) var(--sp-5);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
}
.public-sign-bar-brand {
    font-weight: 700;
    font-size: 1.1rem;
    color: var(--text);
}
.public-sign-shell {
    max-width: 780px;
    margin: 0 auto;
    padding: var(--sp-7) var(--sp-5) var(--sp-9);
}
.public-sign-title {
    margin: 0 0 var(--sp-5);
    font-size: 1.5rem;
    font-weight: 700;
}
.public-sign-form .card-title { margin-top: 0; }
.public-sign-success {
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: var(--sp-7);
    text-align: center;
}
.public-sign-success h1 {
    color: var(--success, #15803d);
    margin: 0 0 var(--sp-3);
    font-size: 1.5rem;
}
.public-sign-success h1 i { margin-right: .5rem; }
.public-sign-success p { margin: var(--sp-2) 0; color: var(--text-med); }
.public-sign-error h1 {
    color: var(--warn, #92400e);
    margin: 0 0 var(--sp-3);
    font-size: 1.25rem;
}
.public-sign-footer {
    margin-top: var(--sp-7);
    text-align: center;
    font-size: .8rem;
    color: var(--text-light);
}
.public-sign-footer a {
    color: var(--text-light);
    text-decoration: underline;
}

/* ── Rates Schedule visual anchor (in the WYSIWYG editor) ─────────────────
   Stands in for the literal {{rates_schedule}} token so the user never
   sees implementation detail. contenteditable=false on the JS side
   prevents accidental cursor-inside-the-block edits. */
.ag-rates-anchor {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    margin: var(--sp-4) 0;
    padding: var(--sp-3) var(--sp-4);
    border: 2px dashed var(--border-strong);
    border-radius: 8px;
    background: var(--bg-soft);
    color: var(--text-light);
    font-size: .85rem;
    user-select: none;
    cursor: default;
}
.ag-rates-anchor i { color: var(--text-light); font-size: 1rem; }

/* Button busy / submit-in-flight state (set by wireSubmitGuards in portal.js).
   Disabled + dimmed + cursor:wait. */
button.is-busy,
input[type="submit"].is-busy {
    opacity: .7;
    cursor: wait;
}

/* Messages inbox: unread thread treatment. Bold text + a small blue dot
   before the project title. Sorts to the top of the list (server-side). */
.data-table tr.is-thread-unread td { font-weight: 600; }
.thread-unread-dot {
    display: inline-block;
    width: 8px; height: 8px;
    background: var(--info, #2563eb);
    border-radius: 50%;
    margin-right: var(--sp-2);
    vertical-align: middle;
}

/* Rectangular variant of the avatar preview (used for studio-logo upload —
   logos aren't necessarily square). */
.avatar-preview-rect {
    width: 120px;
    height: 80px;
    border-radius: 6px;
    background: var(--bg-soft);
    border: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}
.avatar-preview-rect img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

/* Studio logo image inside the brand block (sidebar / topbar) — falls back
   to the wordmark text when no logo_path is set on the tenant. */
.brand-logo {
    max-width: 100%;
    max-height: 48px;
    height: auto;
    width: auto;
    display: block;
    margin-bottom: var(--sp-2);
    object-fit: contain;
}

/* ─────────────────────────────────────────────────────────────────────────
   Settings → Payments → Processor cards
   ───────────────────────────────────────────────────────────────────────── */
.processor-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--sp-4);
}
@media (max-width: 700px) {
    .processor-grid { grid-template-columns: 1fr; }
}
.processor-card {
    background: var(--bg-soft);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: var(--sp-4);
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
    transition: border-color .15s;
}
.processor-card.is-connected {
    background: #fff;
    border-color: var(--success, #15803d);
    box-shadow: inset 3px 0 0 var(--success, #15803d);
}
.processor-card-head {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
}
.processor-card-icon {
    font-size: 1.75rem;
    width: 40px;
    height: 40px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    background: #fff;
    border: 1px solid var(--border);
    color: var(--text);
}
.processor-card-name {
    font-weight: 600;
    font-size: 1rem;
}
.processor-card-sub { margin-top: 1px; }
.processor-card-head .badge { margin-left: auto; }

/* ─────────────────────────────────────────────────────────────────────────
   Client notification preferences — two-column toggle grid
   ───────────────────────────────────────────────────────────────────────── */
.notif-prefs-group { margin-bottom: var(--sp-6); }
.notif-prefs-group:last-of-type { margin-bottom: 0; }
/* When a notif group is also a form-section, the section's padding + divider
   handle spacing — drop the legacy margin so they don't double up. */
.form-section.notif-prefs-group { margin-bottom: 0; }
.notif-prefs-table {
    width: 100%;
    border-collapse: collapse;
    border: 1px solid var(--border);
    border-radius: 10px;
    overflow: hidden;
    background: #fff;
}
.notif-prefs-table thead th {
    background: var(--bg-soft);
    color: var(--text-muted);
    font-weight: 600;
    font-size: .75rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    padding: var(--sp-3);
    text-align: left;
}
.notif-prefs-table thead th.notif-prefs-col-toggle { text-align: center; width: 90px; }
.notif-prefs-table tbody td {
    border-top: 1px solid var(--border);
    padding: var(--sp-3);
    vertical-align: middle;
}
.notif-prefs-table tbody td.notif-prefs-col-toggle { text-align: center; }
.notif-prefs-label { font-weight: 600; display: inline-flex; align-items: center; gap: var(--sp-2); }
.notif-prefs-desc  { margin-top: 4px; }
.notif-prefs-locked { font-size: .65rem; letter-spacing: .04em; text-transform: uppercase; }
.notif-prefs-checkbox input[type="checkbox"] {
    width: 18px; height: 18px;
    accent-color: var(--brand-accent);
    cursor: pointer;
}
.notif-prefs-checkbox input[type="checkbox"]:disabled { cursor: not-allowed; opacity: .5; }

/* Sidebar nav unread count badge + per-row "New" pill. The badge color
   matches the topbar notification bell (--danger) for visual
   consistency — same signal across the chrome. */
.nav-unread-badge {
    margin-left: auto;          /* push to the far right of the .portal-sidenav-link flex row */
    background: var(--danger);
    color: #fff;
    font-size: .7rem;
    font-weight: 700;
    line-height: 1;
    padding: 3px 7px;
    border-radius: 10px;
    min-width: 18px;
    text-align: center;
}
.pill-new {
    display: inline-block;
    background: var(--brand-accent, #ff6b35);
    color: #fff;
    font-size: .6rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    padding: 2px 6px;
    border-radius: 8px;
    vertical-align: 2px;
    margin-left: 6px;
}

/* Receipt print mode — hide sidebar/topbar chrome and the .no-print
   inline header so what prints is just the white receipt card. */
@media print {
    .portal-shell { display: block; }
    .portal-sidebar, .portal-topbar, .topbar, .client-sidebar { display: none !important; }
    .no-print { display: none !important; }
    .printable-receipt { box-shadow: none !important; border: 0 !important; }
    body { background: #fff !important; }
}

/* ─────────────────────────────────────────────────────────────────────────
   Breadcrumb trail — used at the top of deep detail pages
   (agreement detail, client detail, template edit, etc.)
   ───────────────────────────────────────────────────────────────────────── */
.breadcrumbs {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    font-size: .85rem;
    color: var(--text-muted);
    margin-bottom: var(--sp-2);
}
.breadcrumb-link {
    color: var(--text-muted);
    text-decoration: none;
    transition: color .15s;
}
.breadcrumb-link:hover { color: var(--text); text-decoration: underline; text-underline-offset: 2px; }
.breadcrumb-sep { color: var(--text-light); user-select: none; }
.breadcrumb-current { color: var(--text); font-weight: 500; }
.breadcrumb-back {
    display: inline-flex;
    align-items: center;
    color: var(--text-light);
    text-decoration: none;
    font-size: .8rem;
    transition: color .15s;
}
.breadcrumb-back:hover { color: var(--text); }

/* Inline ?-icon field help — emits a tooltip on hover + a native
   title attribute as fallback for keyboard-only users. */
.field-help {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    margin-left: 4px;
    padding: 0;
    background: transparent;
    border: 0;
    color: var(--text-light);
    cursor: help;
    font-size: .8rem;
    vertical-align: middle;
    border-radius: 50%;
    position: relative;
}
.field-help:hover, .field-help:focus { color: var(--brand); outline: none; }
.field-help[data-tooltip]:hover::after,
.field-help[data-tooltip]:focus::after {
    content: attr(data-tooltip);
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%);
    background: var(--brand);
    color: #fff;
    padding: 6px 10px;
    border-radius: 6px;
    font-size: .75rem;
    font-weight: 400;
    line-height: 1.4;
    white-space: nowrap;
    max-width: 280px;
    white-space: normal;
    width: max-content;
    text-align: left;
    z-index: 100;
    pointer-events: none;
}
.field-help[data-tooltip]:hover::before,
.field-help[data-tooltip]:focus::before {
    content: '';
    position: absolute;
    bottom: calc(100% + 2px);
    left: 50%;
    transform: translateX(-50%);
    border: 4px solid transparent;
    border-top-color: var(--brand);
    z-index: 100;
    pointer-events: none;
}

/* ─────────────────────────────────────────────────────────────────────────
   Shortcuts cheatsheet modal — opened by `?`
   ───────────────────────────────────────────────────────────────────────── */
dialog.shortcuts-modal {
    border: 0;
    padding: 0;
    border-radius: 14px;
    background: #fff;
    max-width: 600px;
    width: calc(100% - 32px);
    max-height: 80vh;
    box-shadow: 0 24px 60px rgba(10, 10, 10, 0.22), 0 4px 12px rgba(10, 10, 10, 0.08);
    color: var(--text);
    font-family: inherit;
}
dialog.shortcuts-modal::backdrop {
    background: rgba(10, 10, 10, 0.45);
    backdrop-filter: blur(2px);
}
.shortcuts-modal-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: var(--sp-4) var(--sp-5);
    border-bottom: 1px solid var(--border);
}
.shortcuts-modal-head h2 {
    font-size: 1.05rem;
    font-weight: 600;
    margin: 0;
}
.shortcuts-modal-close {
    appearance: none;
    background: transparent;
    border: 0;
    color: var(--text-muted);
    font-size: 1rem;
    cursor: pointer;
    width: 32px;
    height: 32px;
    border-radius: 8px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.shortcuts-modal-close:hover { background: var(--bg-soft); color: var(--text); }
.shortcuts-modal-body {
    padding: var(--sp-5);
    overflow-y: auto;
    max-height: calc(80vh - 64px);
}
.shortcuts-group { margin-bottom: var(--sp-5); }
.shortcuts-group:last-child { margin-bottom: 0; }
.shortcuts-group-title {
    font-size: .7rem;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: .08em;
    margin: 0 0 var(--sp-3);
}
.shortcuts-list { margin: 0; }
.shortcuts-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 8px 0;
    border-bottom: 1px solid var(--border);
}
.shortcuts-row:last-child { border-bottom: 0; }
.shortcuts-label {
    margin: 0;
    font-size: .9rem;
    color: var(--text);
}
.shortcuts-keys {
    margin: 0;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: .8rem;
}
.shortcuts-keys kbd {
    display: inline-block;
    background: var(--bg-soft);
    border: 1px solid var(--border);
    border-bottom-width: 2px;
    border-radius: 5px;
    padding: 2px 8px;
    font-family: 'SF Mono', Menlo, monospace;
    font-size: .75rem;
    font-weight: 500;
    color: var(--text);
    min-width: 22px;
    text-align: center;
}
.shortcuts-keys-sep { color: var(--text-light); font-size: .7rem; }

/* ─────────────────────────────────────────────────────────────────────────
   Kebab menus — secondary actions on table rows
   ───────────────────────────────────────────────────────────────────────── */
.kebab {
    position: relative;
    display: inline-block;
}
.kebab-btn {
    appearance: none;
    background: transparent;
    border: 0;
    cursor: pointer;
    width: 32px;
    height: 32px;
    border-radius: 6px;
    color: var(--text-muted);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: .95rem;
    transition: background .12s, color .12s;
}
.kebab-btn:hover { background: var(--bg-soft); color: var(--text); }
.kebab-btn[aria-expanded="true"] { background: var(--bg-soft); color: var(--text); }
.kebab-menu {
    /* Default rendering — only ever visible briefly before kebab-menu.js
       portals it to <body>. JS sets explicit position/top/left there. */
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    min-width: 180px;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 12px 28px rgba(10,10,10,.12), 0 2px 6px rgba(10,10,10,.06);
    padding: 6px;
    z-index: 1200;
    text-align: left;
}
/* When portaled to <body>, the menu loses its parent context for
   width (was naturally constrained inside the .kebab inline-block).
   width:max-content makes it shrink-wrap to the widest menu item +
   max-width caps it at a sensible 240px so a long action label
   doesn't blow out the layout. z-index needs to beat the topbar
   (1100) and any drawer backdrops (1000). */
.kebab-menu-portaled {
    width: max-content;
    max-width: 240px;
    z-index: 1200;
}
.kebab-menu a,
.kebab-menu button {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 8px 10px;
    border-radius: 6px;
    background: transparent;
    border: 0;
    color: var(--text);
    font-size: .88rem;
    font-weight: 400;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
    font-family: inherit;
}
.kebab-menu a:hover,
.kebab-menu button:hover { background: var(--bg-soft); }
.kebab-menu a i,
.kebab-menu button i { color: var(--text-muted); font-size: .85rem; width: 14px; }
.kebab-menu .kebab-item-danger { color: var(--danger); }
.kebab-menu .kebab-item-danger i { color: var(--danger); }
.kebab-menu .kebab-item-danger:hover { background: #fef2f2; }
.kebab-divider {
    border: 0;
    border-top: 1px solid var(--border);
    margin: 4px 0;
}
.kebab-menu form { margin: 0; }

/* Tiny inline kbd chip for the account-dropdown shortcuts hint. */
.topbar-menu-kbd {
    margin-left: auto;
    background: var(--bg-soft);
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 1px 6px;
    font-family: 'SF Mono', Menlo, monospace;
    font-size: .7rem;
    color: var(--text-muted);
}

/* ─────────────────────────────────────────────────────────────────────────
   Settings sub-nav — horizontal tab bar at the top of every /portal/
   settings/ page. Same pattern as Stripe / Linear settings.
   ───────────────────────────────────────────────────────────────────────── */
.settings-subnav {
    display: flex;
    gap: 4px;
    /* Spread the tabs across the full width (justified) instead of bunching
       them at the left. On narrow screens they overflow + scroll as before. */
    justify-content: space-between;
    padding-bottom: var(--sp-3);
    margin-bottom: var(--sp-5);
    border-bottom: 1px solid var(--border);
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}
@media (max-width: 720px) {
    /* When the tabs overflow and scroll, justified spacing fights the scroll —
       fall back to natural left-aligned tabs. */
    .settings-subnav { justify-content: flex-start; }
}
.settings-subnav-item {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    border-radius: 8px 8px 0 0;
    font-size: .9rem;
    font-weight: 500;
    color: var(--text-muted);
    text-decoration: none;
    white-space: nowrap;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;  /* sit on top of the parent border so active overlays it */
    transition: color .15s, background .15s, border-color .15s;
}
.settings-subnav-item:hover { color: var(--text); background: var(--bg-soft); border-bottom-color: var(--brand-accent); }
.settings-subnav-item.is-active {
    color: var(--brand-accent-strong);
    border-bottom-color: var(--brand-accent);
    background: transparent;
}
.settings-subnav-item.is-soon {
    color: var(--text-light);
    cursor: not-allowed;
    opacity: .65;
}
.settings-subnav-item.is-soon:hover { background: transparent; color: var(--text-light); border-bottom-color: transparent; }
.settings-subnav-soon {
    font-size: .65rem;
    font-style: normal;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--text-light);
    background: var(--bg-soft);
    padding: 2px 6px;
    border-radius: 4px;
    margin-left: 4px;
}

/* ─────────────────────────────────────────────────────────────────────────
   Custom confirm modal — replaces native window.confirm()
   ───────────────────────────────────────────────────────────────────────── */
dialog.confirm-modal {
    border: 0;
    padding: 0;
    border-radius: 12px;
    background: #fff;
    max-width: 440px;
    width: calc(100% - 32px);
    box-shadow: 0 20px 50px rgba(10,10,10,.22), 0 4px 12px rgba(10,10,10,.08);
    color: var(--text);
    font-family: inherit;
}
dialog.confirm-modal::backdrop {
    background: rgba(10, 10, 10, 0.45);
    backdrop-filter: blur(2px);
}
.confirm-modal-body { padding: var(--sp-5) var(--sp-5) var(--sp-3); }
.confirm-modal-title {
    font-size: 1.1rem;
    font-weight: 600;
    margin: 0 0 var(--sp-2);
    letter-spacing: -.01em;
}
.confirm-modal-text {
    margin: 0;
    color: var(--text-muted);
    font-size: .92rem;
    line-height: 1.5;
}
.confirm-modal-text:empty { display: none; }
.confirm-modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--sp-2);
    padding: var(--sp-3) var(--sp-5) var(--sp-5);
}

/* Danger-tier CTA button — red bg, white text, hover deepens.
   Distinct from .btn-danger (ghost variant) used elsewhere. */
.btn-danger-strong {
    background: var(--danger);
    color: #fff;
    border-color: var(--danger);
}
.btn-danger-strong:hover { background: #7f1d1d; border-color: #7f1d1d; }

/* ─────────────────────────────────────────────────────────────────────────
   Toast notifications — slide-in from bottom-right
   ───────────────────────────────────────────────────────────────────────── */
.toast-host {
    position: fixed;
    bottom: 24px;
    right: 24px;
    z-index: 1100;
    display: flex;
    flex-direction: column;
    gap: 12px;
    pointer-events: none;
    max-width: calc(100vw - 48px);
}
.toast {
    pointer-events: auto;
    display: flex;
    align-items: flex-start;
    gap: 10px;
    background: #fff;
    color: var(--text);
    border: 1px solid var(--border);
    border-left: 4px solid var(--success);
    border-radius: 10px;
    box-shadow: 0 10px 28px rgba(10, 10, 10, 0.12), 0 2px 6px rgba(10, 10, 10, 0.05);
    padding: 12px 14px;
    min-width: 280px;
    max-width: 380px;
    transform: translateX(120%);
    opacity: 0;
    transition: transform .22s cubic-bezier(.21,1.02,.73,1), opacity .22s ease;
}
.toast.is-shown { transform: translateX(0); opacity: 1; }
.toast.toast-error   { border-left-color: var(--danger); }
.toast.toast-info    { border-left-color: var(--info, #2563eb); }
.toast-icon {
    font-size: 1rem;
    color: var(--success);
    margin-top: 2px;
    flex-shrink: 0;
}
.toast-error .toast-icon { color: var(--danger); }
.toast-info  .toast-icon { color: var(--info, #2563eb); }
.toast-message { flex: 1; font-size: .9rem; line-height: 1.4; }
.toast-close {
    appearance: none;
    background: transparent;
    border: 0;
    color: var(--text-muted);
    cursor: pointer;
    padding: 2px 4px;
    border-radius: 4px;
    font-size: .85rem;
    flex-shrink: 0;
}
.toast-close:hover { color: var(--text); background: var(--bg-soft); }
@media (max-width: 600px) {
    .toast-host { left: 16px; right: 16px; bottom: 16px; }
    .toast { max-width: none; min-width: 0; }
}

/* Auto-dismiss + manual-close on status banners (success only). */
.status-banner.is-fading {
    opacity: 0;
    transform: translateY(-4px);
    transition: opacity .25s ease, transform .25s ease;
}
.status-banner .banner-close {
    appearance: none;
    background: transparent;
    border: 0;
    color: inherit;
    opacity: .55;
    cursor: pointer;
    margin-left: auto;
    padding: 2px 6px;
    font-size: .9rem;
    border-radius: 4px;
    align-self: flex-start;
}
.status-banner .banner-close:hover { opacity: 1; }
.status-banner-success { display: flex; align-items: flex-start; gap: 12px; }

/* ─────────────────────────────────────────────────────────────────────────
   Empty states — used inside .data-table-wrap when there are no rows.
   Richer than plain text — icon + title + sub + optional CTA.
   ───────────────────────────────────────────────────────────────────────── */
.empty-state {
    padding: 56px 24px;
    text-align: center;
    color: var(--text-muted);
}
.empty-state-icon {
    width: 56px;
    height: 56px;
    border-radius: 14px;
    background: var(--bg-soft);
    color: var(--text-muted);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.5rem;
    margin: 0 auto var(--sp-3);
}
.empty-state-title {
    font-size: 1rem;
    font-weight: 600;
    color: var(--text);
    margin: 0 0 6px;
}
.empty-state-desc {
    font-size: .9rem;
    max-width: 420px;
    margin: 0 auto var(--sp-4);
    line-height: 1.5;
}
.empty-state-cta { margin-top: 0; }

/* ─────────────────────────────────────────────────────────────────────────
   Invoice payment drawer — right-side slide-out
   Premium-SaaS pattern (HoneyBook, Linear billing, Stripe Dashboard).
   Slides in from the right with a dimmed backdrop over the invoice.
   ───────────────────────────────────────────────────────────────────────── */
.pay-drawer-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(10, 10, 10, 0.45);
    z-index: 1000;
    opacity: 0;
    transition: opacity .22s ease;
    pointer-events: none;
}
body.has-pay-drawer-open .pay-drawer-backdrop {
    opacity: 1;
    pointer-events: auto;
}
.pay-drawer {
    position: fixed;
    top: 0;
    right: 0;
    height: 100vh;
    width: 480px;
    max-width: 100vw;
    background: #fff;
    box-shadow: -16px 0 48px rgba(10, 10, 10, 0.16);
    z-index: 1001;
    display: flex;
    flex-direction: column;
    transform: translateX(100%);
    transition: transform .22s ease;
}
body.has-pay-drawer-open .pay-drawer {
    transform: translateX(0);
}
body.has-pay-drawer-open {
    /* Lock body scroll so only the drawer scrolls. */
    overflow: hidden;
}
.pay-drawer-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: var(--sp-3);
    padding: var(--sp-5);
    border-bottom: 1px solid var(--border);
}
.pay-drawer-title {
    font-size: 1.15rem;
    font-weight: 600;
    margin: 0 0 4px;
    letter-spacing: -0.01em;
}
.pay-drawer-close {
    appearance: none;
    background: transparent;
    border: 0;
    color: var(--text-muted);
    font-size: 1.25rem;
    cursor: pointer;
    width: 32px;
    height: 32px;
    border-radius: 8px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.pay-drawer-close:hover { background: var(--bg-soft); color: var(--text); }
.pay-drawer-body {
    padding: var(--sp-5);
    overflow-y: auto;
    flex: 1;
}
.pay-summary-note {
    padding: var(--sp-3);
    background: var(--bg-soft);
    border-radius: 8px;
}

@media (max-width: 600px) {
    .pay-drawer { width: 100vw; }
}

/* ─────────────────────────────────────────────────────────────────────────
   Client invoice → Square embedded payment form
   The Web Payments SDK iframes the card field into #sq-card; we style
   the surrounding shell, not the iframe itself (Square owns those styles).
   ───────────────────────────────────────────────────────────────────────── */
.sq-pay-shell {
    max-width: 480px;
    margin: 0 auto;
}
.sq-tabs {
    display: flex;
    gap: var(--sp-2);
    margin-bottom: var(--sp-4);
    border-bottom: 1px solid var(--border);
}
.sq-tab {
    appearance: none;
    background: transparent;
    border: 0;
    padding: var(--sp-2) var(--sp-3);
    font: inherit;
    color: var(--text-muted);
    cursor: pointer;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    transition: color .15s, border-color .15s;
}
.sq-tab:hover { color: var(--text); }
.sq-tab.is-active {
    color: var(--text);
    border-bottom-color: var(--primary, #111);
    font-weight: 600;
}
.sq-tab i { margin-right: 6px; }
.sq-mount {
    min-height: 90px;
    padding: var(--sp-2) 0;
}
.sq-breakdown {
    border-top: 1px solid var(--border);
    margin-top: var(--sp-4);
    padding-top: var(--sp-3);
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.sq-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    font-size: .9rem;
}
.sq-row-total {
    padding-top: var(--sp-2);
    border-top: 1px dashed var(--border);
    margin-top: 4px;
    font-weight: 600;
    font-size: 1.05rem;
}
.sq-status {
    margin-top: var(--sp-3);
    padding: var(--sp-3);
    border-radius: 8px;
    font-size: .9rem;
    background: var(--bg-soft);
    border: 1px solid var(--border);
}
.sq-status[data-kind="error"] {
    background: #fef2f2;
    border-color: #fecaca;
    color: #991b1b;
}
.sq-status[data-kind="success"] {
    background: #f0fdf4;
    border-color: #bbf7d0;
    color: #166534;
}
/* Stripe pay-drawer status (class-based; hidden when empty). */
.stripe-status {
    margin-top: var(--sp-3);
    padding: var(--sp-3);
    border-radius: 8px;
    font-size: .9rem;
    background: var(--bg-soft);
    border: 1px solid var(--border);
}
.stripe-status:empty { display: none; }
.stripe-status-error   { background: #fef2f2; border-color: #fecaca; color: #991b1b; }
.stripe-status-success { background: #f0fdf4; border-color: #bbf7d0; color: #166534; }

/* ── Gallery admin ──────────────────────────────────────────────────── */
.gallery-dropzone {
    border: 2px dashed var(--border);
    border-radius: 14px;
    background: var(--bg-soft);
    padding: var(--sp-7) var(--sp-5);
    text-align: center;
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease, transform .15s ease;
    margin-bottom: var(--sp-5);
}
.gallery-dropzone:hover,
.gallery-dropzone:focus-visible {
    border-color: var(--ink);
    background: #fff;
    outline: none;
}
.gallery-dropzone.is-dragover {
    border-color: var(--ink);
    background: #fff;
    transform: scale(1.005);
    box-shadow: 0 8px 24px -10px rgba(0,0,0,.15);
}
.gallery-dropzone-icon {
    font-size: 2.5rem;
    color: var(--muted);
    margin-bottom: var(--sp-2);
    display: block;
}
.gallery-dropzone-title {
    font-size: 1.1rem;
    font-weight: 600;
    margin: 0 0 var(--sp-1);
}
.gallery-dropzone-sub {
    font-size: .9rem;
    color: var(--muted);
    margin: 0;
}
.link-button {
    background: none; border: 0; padding: 0;
    color: var(--ink); text-decoration: underline; cursor: pointer;
    font: inherit;
}

.gallery-queue {
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: var(--sp-4);
    margin-bottom: var(--sp-5);
}
.gallery-queue-head {
    display: flex; justify-content: space-between; align-items: baseline;
    margin-bottom: var(--sp-3);
}
.gallery-queue-title { font-weight: 600; }
.gallery-queue-progress { color: var(--muted); font-variant-numeric: tabular-nums; font-size: .9rem; }
.gallery-queue-list {
    list-style: none; padding: 0; margin: 0;
    display: flex; flex-direction: column; gap: var(--sp-2);
    max-height: 360px;
    overflow-y: auto;
}
/* Collapse completed rows during large batches so the queue stays scannable
 * (300 visible rows would push the grid offscreen). Failures stay expanded. */
.gallery-queue-item.is-collapsed {
    grid-template-columns: 1fr;
    grid-template-rows: auto;
    padding: 4px 0;
    border-top: 1px dashed var(--border);
    margin-top: -4px;
    font-size: .8rem;
    color: #16a34a;
}
.gallery-queue-item.is-collapsed .gallery-queue-bar,
.gallery-queue-item.is-collapsed .gallery-queue-status { display: none; }
.gallery-queue-item.is-collapsed .gallery-queue-name::before {
    content: '\f00c';
    font-family: 'Font Awesome 6 Free'; font-weight: 900;
    margin-right: 6px;
    color: #16a34a;
}
.gallery-queue-item {
    display: grid;
    grid-template-columns: 1fr 200px 60px;
    align-items: center;
    gap: var(--sp-3);
    font-size: .9rem;
}
.gallery-queue-name {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.gallery-queue-bar {
    height: 6px; background: var(--bg-soft); border-radius: 999px; overflow: hidden;
}
.gallery-queue-fill {
    display: block; height: 100%;
    background: var(--ink);
    transition: width .15s ease;
}
.gallery-queue-status {
    text-align: right; color: var(--muted); font-variant-numeric: tabular-nums; font-size: .85rem;
}
.gallery-queue-item.is-done .gallery-queue-fill { background: #16a34a; }
.gallery-queue-item.is-done .gallery-queue-status { color: #16a34a; }
.gallery-queue-item.is-failed .gallery-queue-fill { background: #dc2626; }
.gallery-queue-item.is-failed .gallery-queue-status { color: #dc2626; }

.gallery-bulkbar {
    position: sticky;
    top: 1rem;
    z-index: 50;
    display: flex;
    justify-content: space-between;
    align-items: center;
    background: var(--ink);
    color: #fff;
    border-radius: 12px;
    padding: var(--sp-2) var(--sp-4);
    margin-bottom: var(--sp-4);
    box-shadow: 0 6px 18px -8px rgba(0,0,0,.35);
}
.gallery-bulkbar[hidden] { display: none; }   /* author display:flex would otherwise beat [hidden] */
.gallery-bulkbar-count { font-weight: 600; font-size: .9rem; }
.gallery-bulkbar-actions { display: flex; gap: var(--sp-3); align-items: center; }
.gallery-bulkbar .btn-ghost { color: #fff; }
.gallery-bulkbar .btn-ghost:hover { background: rgba(255,255,255,.1); }
.gallery-bulkbar-link {
    color: #fff;
    text-decoration: underline;
    text-underline-offset: 3px;
    font-size: .85rem;
}
.gallery-bulkbar-link:hover { color: #fff; opacity: .8; }

/* Toolbar above the grid (Select all + shortcut hint + thumb size slider) */
.gallery-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    padding: 0 2px var(--sp-3);
    border-bottom: 1px solid var(--border);
    margin-bottom: var(--sp-3);
    flex-wrap: wrap;
}
.gallery-toolbar-left {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
}
.gallery-toolbar .link-button { font-size: .9rem; font-weight: 500; }

/* Thumbnail size slider — Lightroom-style. Small image icon on the
 * left, large on the right, slider in between. Range 160-500px row
 * height. Preference persists in localStorage. */
.gallery-thumb-size {
    display: flex;
    align-items: center;
    gap: 10px;
    color: var(--muted);
}
.gallery-thumb-size-icon-sm { font-size: .65rem; }
.gallery-thumb-size-icon-lg { font-size: 1rem; }
.gallery-thumb-size-slider {
    -webkit-appearance: none;
    appearance: none;
    width: 140px;
    height: 4px;
    background: var(--border);
    border-radius: 999px;
    outline: none;
    cursor: pointer;
    margin: 0;
}
.gallery-thumb-size-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 16px; height: 16px;
    border-radius: 50%;
    background: var(--ink);
    cursor: grab;
    border: 2px solid #fff;
    box-shadow: 0 1px 3px rgba(0,0,0,.3);
    transition: transform .1s ease;
}
.gallery-thumb-size-slider::-webkit-slider-thumb:hover { transform: scale(1.15); }
.gallery-thumb-size-slider::-webkit-slider-thumb:active { cursor: grabbing; transform: scale(1.25); }
.gallery-thumb-size-slider::-moz-range-thumb {
    width: 16px; height: 16px;
    border-radius: 50%;
    background: var(--ink);
    cursor: grab;
    border: 2px solid #fff;
    box-shadow: 0 1px 3px rgba(0,0,0,.3);
    transition: transform .1s ease;
}
.gallery-thumb-size-slider::-moz-range-thumb:hover { transform: scale(1.15); }
.gallery-thumb-size-slider::-moz-range-thumb:active { cursor: grabbing; transform: scale(1.25); }
@media (max-width: 600px) {
    .gallery-thumb-size { display: none; }
}
.kbd-inline {
    display: inline-block;
    padding: 1px 6px;
    border: 1px solid var(--border);
    border-bottom-width: 2px;
    border-radius: 4px;
    font: 600 .72rem / 1 ui-monospace, SFMono-Regular, Menlo, monospace;
    background: var(--bg-soft);
    color: var(--ink);
    vertical-align: 1px;
}
@media (max-width: 600px) {
    .gallery-toolbar [data-shortcut-hint],
    .gallery-toolbar .kbd-inline { display: none; }
}

/* Justified-rows gallery (Flickr / Google Photos / Unsplash pattern).
 *
 * Every row has a uniform height (target ~200px, allowed to scale ±20%
 * so the row exactly fills the container). Tile widths flex per image's
 * natural aspect ratio. Images are NEVER cropped — the cell IS the image's
 * aspect ratio because we set width = rowHeight × aspectRatio in JS.
 *
 * Width + height are set inline on each tile by layoutJustified() in
 * gallery-admin.js. Uses flex-wrap so the browser handles row breaks
 * naturally once widths are pre-computed. */
.gallery-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-content: flex-start;
}
.gallery-empty { margin-bottom: var(--sp-5); }
.gallery-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    flex: 0 0 auto;       /* width is JS-set; do not flex */
    border-radius: 10px;
    overflow: hidden;
    background: #fff;
    border: 1px solid var(--border);
    cursor: grab;
    transition: box-shadow .12s ease;
}
.gallery-tile:hover { box-shadow: 0 6px 16px -8px rgba(0,0,0,.18); }
.gallery-tile.is-dragging { opacity: .35; cursor: grabbing; }
.gallery-tile.is-drop-target { outline: 2px solid var(--ink); outline-offset: -2px; }
.gallery-tile.is-cover { border-color: var(--ink); box-shadow: 0 0 0 1px var(--ink) inset; }
.gallery-tile.is-selected { border-color: #2563eb; box-shadow: 0 0 0 1px #2563eb inset; }

/* The image well — fills the JS-computed width × height exactly. Image
 * inside fills the cell with object-fit:fill (safe because cell aspect
 * was computed to match image aspect — no distortion). */
.gallery-tile-imgwrap {
    position: relative;
    width: 100%;
    flex: 1 1 auto;
    background: #fafafa;
    overflow: hidden;
}
.gallery-tile-img {
    width: 100%;
    height: 100%;
    display: block;
    pointer-events: none;
    /* cover fills the cell edge-to-edge. The justified layout sizes each
     * cell to the image's aspect, so any mismatch is sub-pixel — cover
     * absorbs it as an invisible hairline crop rather than showing a
     * letterbox strip against the well (which read as "cheap"). The full,
     * uncropped image is always available in the lightbox. */
    object-fit: cover;
}

/* Always-visible filename caption strip under the image. Truncates with
 * ellipsis; full filename in the tooltip. Lightroom-style. */
.gallery-tile-caption {
    padding: 7px 10px;
    font-size: .78rem;
    line-height: 1.3;
    color: var(--ink);
    background: #fff;
    border-top: 1px solid var(--border);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.gallery-tile-caption.is-untitled { color: var(--muted); font-style: italic; }
.gallery-tile-checkbox {
    position: absolute; top: 8px; left: 8px;
    width: 22px; height: 22px;
    cursor: pointer;
    opacity: 0;
    transition: opacity .12s ease;
}
.gallery-tile:hover .gallery-tile-checkbox,
.gallery-tile.is-selected .gallery-tile-checkbox,
.gallery-tile-checkbox input:focus-visible + span { opacity: 1; }
.gallery-tile-checkbox input { position: absolute; opacity: 0; pointer-events: none; }
.gallery-tile-checkbox span {
    display: block; width: 22px; height: 22px;
    border-radius: 6px;
    background: rgba(255,255,255,.95);
    border: 1.5px solid rgba(0,0,0,.25);
    box-shadow: 0 2px 4px rgba(0,0,0,.2);
}
.gallery-tile-checkbox input:checked + span {
    background: #2563eb; border-color: #2563eb;
}
.gallery-tile-checkbox input:checked + span::after {
    content: '\f00c';
    font-family: 'Font Awesome 6 Free'; font-weight: 900;
    color: #fff; font-size: 12px;
    display: flex; align-items: center; justify-content: center;
    height: 100%;
}
.gallery-tile-cover-badge {
    position: absolute; top: 8px; right: 8px;
    background: var(--ink); color: #fff;
    font-size: .7rem; font-weight: 600;
    padding: 3px 8px; border-radius: 999px;
    display: inline-flex; align-items: center; gap: 4px;
}
.gallery-tile-actions {
    position: absolute; bottom: 8px; right: 8px;
    display: flex; gap: 6px;
    opacity: 0;
    transition: opacity .12s ease;
}
.gallery-tile:hover .gallery-tile-actions { opacity: 1; }
.gallery-tile-action {
    width: 30px; height: 30px;
    display: inline-flex; align-items: center; justify-content: center;
    background: rgba(255,255,255,.95);
    border: 1px solid rgba(0,0,0,.1);
    border-radius: 8px;
    cursor: pointer;
    color: var(--ink);
    font-size: .85rem;
    box-shadow: 0 2px 4px rgba(0,0,0,.15);
    transition: background .12s ease, color .12s ease;
}
.gallery-tile-action:hover { background: #fff; }
.gallery-tile-action:disabled { opacity: .5; cursor: not-allowed; background: rgba(255,255,255,.7); }
.gallery-tile-action-danger:hover { background: #fee2e2; color: #991b1b; border-color: #fecaca; }

/* Project list table — cover thumb in the title cell. Fixed-size landscape
 * box (60×40) with the image contained inside — natural aspect, no crop.
 * Falls back to a neutral icon when the project has no photos yet. */
.project-title-cell { padding-top: 8px !important; padding-bottom: 8px !important; }
.project-title-row {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
}
.project-cover-thumb {
    flex: 0 0 60px;
    width: 60px;
    height: 40px;
    border-radius: 6px;
    background: #fafafa;
    border: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    color: #cbd5e1;        /* fallback icon color */
}
.project-cover-thumb img {
    max-width: 100%;
    max-height: 100%;
    width: auto;
    height: auto;
    display: block;
    object-fit: contain;   /* no crop — natural aspect */
}
.project-cover-thumb .fa-image { font-size: 1rem; }
.project-title-link { font-weight: 500; }

/* ── Client gallery viewer ──────────────────────────────────────────── */
:root {
    --gallery-heart: #e11d48;     /* rose-600 — favorites (preference) */
    --gallery-check: #16a34a;     /* green-600 — selections (final cut) */
    --gallery-bg:    #f4f4f5;     /* subtle off-white background */
}

/* Subtle bg on the gallery page only — makes white photos pop without
 * the harsh white-on-white look. Scoped via body.is-gallery-page so
 * other pages stay on the original surface. */
body.is-gallery-page,
body.is-gallery-page .portal-main-with-topbar { background: var(--gallery-bg); }

/* Hero with reflective-blur background — Apple Music / Spotify pattern.
 * Blurred + dimmed version of the cover fills the full width; the actual
 * cover renders centered at its natural aspect on top. Premium feel
 * without being cheesy. */
.gallery-hero {
    position: relative;
    width: 100%;
    height: 440px;
    overflow: hidden;
    border-radius: 16px;
    margin-bottom: var(--sp-5);
    background: #fafafa;
    isolation: isolate;     /* contain z-index stacking */
    display: flex;
    align-items: center;
    justify-content: center;
}
/* Shrink-wraps the centered cover so the watermark overlay covers ONLY the
   photo, not the blurred letterbox bars on either side. */
.gallery-hero-imgwrap {
    position: relative;
    z-index: 1;
    height: 100%;
    max-width: 100%;
    display: flex;
}
.gallery-hero-blur {
    position: absolute;
    inset: -40px;           /* extend beyond edges so blur doesn't show seams */
    background-size: cover;
    background-position: center;
    filter: blur(40px) brightness(.78) saturate(1.2);
    transform: scale(1.1);
    z-index: 0;
}
.gallery-hero-blur::after {
    /* subtle gradient overlay so the centered image has contrast */
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(to bottom, rgba(0,0,0,.08), rgba(0,0,0,.18));
}
.gallery-hero-img {
    position: relative;
    z-index: 1;
    display: block;
    margin: 0 auto;
    height: 100%;
    width: auto;
    max-width: 100%;
    object-fit: contain;
    box-shadow: 0 12px 40px rgba(0,0,0,.35), 0 4px 12px rgba(0,0,0,.2);
    border-radius: 6px;
}
@media (max-width: 700px) {
    .gallery-hero { height: 280px; border-radius: 12px; }
}

/* Status bar above grid — instructions + counter + clear + submit.
 * Sticky variant follows the scroll, with subtle blur backdrop so the
 * gallery photos behind it stay visible-but-soft. */
.gallery-status-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: var(--sp-3) var(--sp-4);
    margin-bottom: var(--sp-3);
    flex-wrap: wrap;
    transition: padding .25s ease, box-shadow .25s ease;
}
.gallery-status-bar-sticky {
    position: sticky;
    /* Stick flush below the admin-topbar (which is itself sticky at top:0
     * with height var(--admin-topbar-h)). Without this offset the filter
     * bar slides up ON TOP of the admin-topbar during scroll, which
     * looks sloppy because the admin-topbar (search + bell + profile)
     * is still meant to be the dominant header. */
    top: var(--admin-topbar-h);
    z-index: 40;     /* below the topbars (admin z:45, portal z:50) — sticks just beneath the header */
    background: rgba(255,255,255,.92);
    -webkit-backdrop-filter: saturate(180%) blur(10px);
    backdrop-filter: saturate(180%) blur(10px);
    box-shadow: 0 2px 12px -4px rgba(0,0,0,.08);
}
/* On mobile both the portal-topbar AND the admin-topbar are visible
 * stacked at the top. The gallery sticky bar sticks BELOW both. Solid
 * opaque background + edge-to-edge to feel like a third deck of the
 * topbar stack (Pixieset / ShootProof pattern). */
@media (max-width: 900px) {
    .gallery-status-bar-sticky {
        top: calc(var(--portal-topbar-h) + var(--admin-topbar-h));
        z-index: 30;
        background: #fff;
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
        border-radius: 0;
        border-left: 0;
        border-right: 0;
        margin-left: calc(var(--sp-4) * -1);
        margin-right: calc(var(--sp-4) * -1);
    }
}
@media (min-width: 900.1px) and (max-width: 900px) {}    /* no-op spacer */
@media (max-width: 900px) {
    /* Counter the negative margin on the gallery-fullbleed pages so the
     * sticky bar still has internal padding aligned with the photo grid. */
    body.is-gallery-page .gallery-status-bar-sticky {
        margin-left: calc(var(--sp-5) * -1);
        margin-right: calc(var(--sp-5) * -1);
    }
}
/* Compact variant — 3-column grid sticky toolbar:
 *   LEFT   = selections/favorites counter
 *   CENTER = filter tabs (truly centered via grid-template-columns 1fr auto 1fr)
 *   RIGHT  = slider + Clear all + Submit
 * Grid is better than flex space-between here because it keeps the
 * center column visually centered relative to the bar, regardless of
 * left/right column width. */
.gallery-status-bar-compact {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-2) var(--sp-3);
    flex-wrap: nowrap;
}
.gallery-status-bar-left  { justify-self: start; min-width: 0; }
.gallery-status-bar-compact .gallery-filter-tabs {
    justify-self: center;
    margin: 0;
}
.gallery-status-bar-compact .gallery-status-bar-right {
    justify-self: end;
    gap: var(--sp-3);
    flex-wrap: nowrap;
}
/* Tighten the slider when it sits inside the bar so it doesn't dominate */
.gallery-status-bar-compact .gallery-thumb-size-slider {
    width: 110px;
}
@media (max-width: 900px) {
    .gallery-status-bar-compact .gallery-thumb-size { display: none; }
    .gallery-status-bar-compact { padding: var(--sp-2); }
}

/* ── Mobile sticky bar layout (≤700px) ──────────────────────────────
 * Collapse the 3-column grid into a stack: counter on top, filter tabs
 * in the middle, action buttons on bottom. Each row is full-width and
 * centered/spread out for ergonomic thumb reach. */
@media (max-width: 700px) {
    .gallery-status-bar-compact {
        grid-template-columns: 1fr;
        grid-template-rows: auto auto auto;
        gap: var(--sp-2);
        padding: var(--sp-2);
    }
    .gallery-status-bar-left,
    .gallery-status-bar-compact .gallery-filter-tabs,
    .gallery-status-bar-compact .gallery-status-bar-right {
        justify-self: center;
        width: 100%;
    }
    .gallery-status-bar-left { text-align: center; }
    .gallery-status-bar-compact .gallery-filter-tabs {
        justify-content: center;
    }
    .gallery-status-bar-compact .gallery-filter-tab {
        padding: 5px 8px;
        font-size: .8rem;
        gap: 5px;
    }
    .gallery-status-bar-compact .gallery-filter-count {
        min-width: 18px;
        padding: 1px 5px;
        font-size: .68rem;
    }
    /* Stop the tabs container from forcing its own background card width —
     * let it shrink with the viewport so it doesn't push past the bar edge. */
    .gallery-status-bar-compact .gallery-filter-tabs {
        max-width: 100%;
        padding: 3px;
    }
    .gallery-status-bar-compact .gallery-status-bar-right {
        justify-content: space-between;
        gap: var(--sp-2);
    }
    .gallery-status-bar-compact .gallery-selcount {
        font-size: .9rem;
    }
    .gallery-status-bar-compact .gallery-selcount strong { font-size: 1.05rem; }
}

/* Touch devices — bigger hit areas for the heart + check icons (iOS
 * recommends 44×44pt minimum). Doubles the safe-tap radius without
 * affecting visual size, since the larger area is invisible/transparent. */
@media (hover: none) {
    .gallery-icon {
        width: 44px;
        height: 44px;
    }
    .gallery-icon-heart { top: 4px; left: 4px; }
    .gallery-icon-check { top: 4px; right: 4px; }
    /* Hover-only filename can't fire on touch — hide it entirely. */
    .gallery-tile-filename { display: none; }
}

/* Topbar logo — shows the studio's uploaded logo in the mobile topbar
 * instead of the truncatable studio name. Capped at the topbar height
 * minus padding so it doesn't blow up the bar. */
.brand-logo-topbar {
    height: calc(var(--portal-topbar-h) - var(--sp-3) * 2);
    width: auto;
    max-width: 160px;
    object-fit: contain;
    display: block;
}

.gallery-instructions {
    display: flex; flex-direction: column; gap: 4px;
    font-size: .9rem;
    color: var(--ink);
}
.gallery-instructions i { width: 16px; margin-right: 4px; }
.gallery-status-bar-right {
    display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
}
.gallery-selcount {
    font-size: 1rem;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
    transition: color .25s ease;
}
.gallery-selcount strong { font-size: 1.2rem; transition: color .25s ease; }
.gallery-selcount [data-sel-count-num].is-bump {
    display: inline-block;
    animation: gallery-count-bump .35s ease;
}
@keyframes gallery-count-bump {
    0%   { transform: scale(1); }
    40%  { transform: scale(1.25); color: var(--gallery-check); }
    100% { transform: scale(1); }
}

/* Near-max emphasis: when client is 1 away from max selections,
 * counter goes slightly amber. At-max: counter goes solid green with
 * a subtle ring. Gentle, not screamy. */
.gallery-selcount[data-near-max="1"] strong { color: #d97706; }
.gallery-selcount[data-at-max="1"]   strong { color: var(--gallery-check); }
.gallery-selcount[data-at-max="1"]::before {
    content: '';
    display: inline-block;
    width: 8px; height: 8px;
    border-radius: 50%;
    background: var(--gallery-check);
    box-shadow: 0 0 0 2px rgba(22,163,74,.2);
    margin-right: 4px;
    animation: gallery-at-max-pulse 2s ease-in-out infinite;
}
@keyframes gallery-at-max-pulse {
    0%, 100% { box-shadow: 0 0 0 2px rgba(22,163,74,.2); }
    50%      { box-shadow: 0 0 0 6px rgba(22,163,74,.05); }
}

/* Save cue — subtle ✓ checkmark next to the counter that flashes in
 * briefly when a server save lands. Single attention cue, NOT a
 * per-click toast (which Tyler said would get annoying with rapid
 * picks). Animation is automatic via .is-saved class toggled by JS. */
.gallery-save-cue {
    display: inline-flex;
    align-items: center;
    color: var(--gallery-check);
    font-size: .9rem;
    opacity: 0;
    transform: translateX(-4px);
    transition: opacity .25s ease, transform .25s ease;
}
.gallery-save-cue.is-saved {
    opacity: 1;
    transform: translateX(0);
}
.gallery-status-bar-locked {
    background: #f0fdf4;
    border-color: #bbf7d0;
    color: #166534;
}
.gallery-status-bar-locked .fa-lock { color: #16a34a; margin-right: 6px; }

/* Per-filter empty state — shown when current filter has 0 matching tiles.
 * Friendlier than an empty grid; explains what the filter does + offers
 * a one-click escape back to All. */
.gallery-filter-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: var(--sp-7) var(--sp-4);
    background: rgba(255,255,255,.6);
    border: 1px dashed var(--border);
    border-radius: 12px;
    margin: var(--sp-3) auto var(--sp-5);
    max-width: 520px;
    gap: var(--sp-3);
}
.gallery-filter-empty[hidden] { display: none; }
.gallery-filter-empty-icon {
    font-size: 2.5rem;
    color: var(--muted);
    opacity: .5;
}
.gallery-filter-empty-text {
    font-size: .95rem;
    color: var(--ink);
    margin: 0;
    line-height: 1.5;
}
.gallery-filter-empty-text i {
    color: var(--gallery-heart);
    margin: 0 2px;
}
.gallery-filter-empty[data-filter-mode="selections"] .gallery-filter-empty-text i {
    color: var(--gallery-check);
}

/* Toolbar row holding filter tabs (centered) + thumb-size slider (right).
 * 3-column grid: spacer | tabs | slider — keeps tabs visually centered
 * while letting the slider live on the right edge. */
.gallery-toolbar-row {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: var(--sp-3);
    margin-bottom: var(--sp-4);
}
.gallery-toolbar-row .gallery-thumb-size { justify-self: end; }
.gallery-toolbar-row-end { grid-template-columns: 1fr; }
.gallery-toolbar-row-end .gallery-thumb-size { justify-self: end; }
.gallery-toolbar-spacer {}    /* invisible left column to balance grid */
@media (max-width: 700px) {
    .gallery-toolbar-row {
        grid-template-columns: 1fr;
        gap: var(--sp-2);
    }
    .gallery-toolbar-row .gallery-thumb-size { justify-self: center; }
    .gallery-toolbar-spacer { display: none; }
}

/* Filter tabs (ALL / FAVES / SELECTS) */
.gallery-filter-tabs {
    display: flex;
    gap: 4px;
    background: var(--bg-soft);
    padding: 4px;
    border-radius: 10px;
    width: fit-content;
    margin: 0 auto;
}
.gallery-filter-tab {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    border: 0;
    background: transparent;
    color: var(--muted);
    font-size: .9rem;
    font-weight: 500;
    border-radius: 6px;
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
}
.gallery-filter-tab:hover { color: var(--ink); }
.gallery-filter-tab.is-active {
    background: #fff;
    color: var(--ink);
    box-shadow: 0 1px 2px rgba(0,0,0,.08);
}
.gallery-filter-count {
    display: inline-block;
    min-width: 22px;
    padding: 1px 6px;
    border-radius: 999px;
    background: rgba(0,0,0,.06);
    font-size: .72rem;
    font-weight: 600;
    text-align: center;
    color: var(--ink);
}
.gallery-filter-tab.is-active .gallery-filter-count {
    background: var(--ink);
    color: #fff;
}

/* Lock-icon button in the filter bar (locked state, per TP spec). Sits
 * next to the filter tabs; click → opens the lockout info modal. */
.gallery-filter-tab-lock {
    color: #16a34a;
    margin-left: 4px;
    padding: 8px 12px;
}
.gallery-filter-tab-lock:hover { background: rgba(22,163,74,.08); color: #166534; }

/* Heart + check icons — TP spec exactly: icon-only with text-STROKE
 * (not shadow) so they're visible on white AND dark backgrounds without
 * the dark-shadow-on-white smudge effect Tyler reported during fast
 * scroll. Active state switches to colored fill. */
.gallery-icon {
    position: absolute;
    width: 38px;
    height: 38px;
    border: 0;
    background: transparent;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    transition: transform .15s ease;
    z-index: 2;
}
/* The icon character itself — color + stroke live on the <i> so we can
 * style independently of the button hit-area. Outline kept light so it
 * doesn't compete visually with the photos behind it. */
.gallery-icon i {
    color: rgba(255,255,255,.6);
    font-size: 22px;
    line-height: 1;
    -webkit-text-stroke: 1px rgba(0,0,0,.22);
    text-shadow: 0 1px 1px rgba(0,0,0,.15);  /* Firefox fallback (lighter than stroke) */
    transition: color .15s ease, -webkit-text-stroke-color .15s ease;
}
.gallery-icon-heart { top: 8px; left: 8px; }
.gallery-icon-check { top: 8px; right: 8px; }
.gallery-icon:hover i { color: rgba(255,255,255,.95); }
.gallery-icon:active { transform: scale(.9); }
.gallery-icon-heart.is-active i {
    color: var(--gallery-heart);
    -webkit-text-stroke: 1px rgba(0,0,0,.18);
}
.gallery-icon-check.is-active i {
    color: var(--gallery-check);
    -webkit-text-stroke: 1px rgba(0,0,0,.18);
}
.gallery-icon.is-locked { cursor: default; pointer-events: none; }

/* Pop animation when a heart/check is toggled — Instagram-style bounce */
@keyframes gallery-icon-pop {
    0%   { transform: scale(1); }
    40%  { transform: scale(1.45); }
    100% { transform: scale(1); }
}
.gallery-icon.just-popped { animation: gallery-icon-pop 350ms cubic-bezier(.34, 1.56, .64, 1); }

/* Tile state outline — kept subtle; selection wins (it's the final cut) */
.gallery-tile.is-favorited       { border-color: var(--gallery-heart); }
.gallery-tile.is-selected-pick   { border-color: var(--gallery-check); box-shadow: 0 0 0 1px var(--gallery-check) inset; }

/* Refined tile hover (TP spec): image scales 1.02× + border darkens.
 * Subtle, tactile, doesn't shift the layout. */
.gallery-grid-client .gallery-tile {
    background: #e5e7eb;        /* gray so icons stay visible during lazy-load
                                  * instead of showing as smudges on white */
    border-color: transparent;
    overflow: hidden;
    transition: transform .2s ease, border-color .2s ease, box-shadow .2s ease;
}
.gallery-grid-client .gallery-tile-img {
    cursor: -webkit-zoom-in;    /* explicit cursor on the img too — with
                                  * pointer-events:none the parent's cursor
                                  * doesn't always propagate */
    cursor: zoom-in;
}
/* The hover zoom scales the IMAGE + its WATERMARK together as one unit.
 * Scaling only the image (with a static watermark on top) forced the browser
 * to repaint the watermark every frame — the overlap de-opt that caused the
 * hover lag. Scaling the shared wrapper lets it composite on the GPU as a
 * single layer. .gallery-tile-media is a client-grid-only wrapper around the
 * img + overlay; icons/filename are siblings and stay put. */
.gallery-tile-media { position: absolute; inset: 0; }
.gallery-grid-client .gallery-tile-media {
    transition: transform .25s ease;
}
.gallery-grid-client .gallery-tile:hover .gallery-tile-media {
    transform: scale(1.02) translateZ(0);
}
.gallery-grid-client .gallery-tile:hover {
    border-color: rgba(0,0,0,.18);
}

/* Filename — hover-only overlay at bottom of image (TP pattern). No
 * always-visible caption strip; cleaner client view. Tooltip carries
 * the full filename for accessibility. */
.gallery-tile-filename {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    padding: 8px 10px;
    background: linear-gradient(to top, rgba(0,0,0,.7), rgba(0,0,0,0));
    color: #fff;
    font-size: .72rem;
    line-height: 1.2;
    text-align: center;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    opacity: 0;
    transition: opacity .2s ease;
    pointer-events: none;
    z-index: 1;
}
.gallery-grid-client .gallery-tile:hover .gallery-tile-filename { opacity: 1; }

/* Client gallery cursor — custom SVG zoom-in cursor on image area,
 * pointer on interactive controls. SVG data URL guarantees consistent
 * rendering across browsers (Safari's native cursor:zoom-in is
 * unreliable). Hotspot at (11, 11) = center of the magnifying-glass
 * circle. Fallback: pointer.
 *
 * Each cursor is a 28×28 SVG: white circle with black stroke + handle.
 * Zoom-in has a "+" inside; zoom-out has a "−". */
/* Apply the zoom-in cursor to the .gallery-tile element TOO — not just
 * the imgwrap — so the thin border area (~1-3px between the tile edge
 * and the imgwrap content) doesn't show the underlying admin "grab"
 * cursor. Covers the whole tile surface uniformly. */
.gallery-grid-client .gallery-tile,
.gallery-grid-client .gallery-tile-imgwrap,
.gallery-grid-client .gallery-tile-img,
.gallery-grid-client .gallery-tile-filename {
    cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='28' height='28'><circle cx='11' cy='11' r='7.5' fill='white' stroke='black' stroke-width='1.5'/><line x1='11' y1='7' x2='11' y2='15' stroke='black' stroke-width='1.5' stroke-linecap='round'/><line x1='7' y1='11' x2='15' y2='11' stroke='black' stroke-width='1.5' stroke-linecap='round'/><line x1='16.5' y1='16.5' x2='25' y2='25' stroke='black' stroke-width='2.5' stroke-linecap='round'/></svg>") 11 11, pointer;
}
.gallery-grid-client .gallery-icon,
.gallery-grid-client .gallery-icon i {
    cursor: pointer;
}
/* imgwrap structure — relative + overflow:hidden so the hover filename
 * overlay positions correctly and the 1.02× hover scale doesn't spill */
.gallery-grid-client .gallery-tile { position: relative; }
.gallery-grid-client .gallery-tile-imgwrap {
    position: relative;
    overflow: hidden;
}
.gallery-grid-client .gallery-tile-img {
    pointer-events: auto;       /* restore — admin's drag pattern doesn't apply here */
}

/* Image-harvest friction — CSS-level. Combined with the JS-level
 * contextmenu + dragstart preventers in gallery-client.js, this stops
 * the casual right-click-save / drag-to-desktop on the client gallery.
 * Determined attackers can still grab via devtools — the goal is to
 * raise the bar, not seal it. */
.gallery-grid-client .gallery-tile-img,
.gallery-lightbox-stage img {
    -webkit-user-drag: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

/* Lightbox cursor:
 *   - Image area → custom zoom-OUT SVG cursor (signals click closes)
 *   - Nav arrows + close X + heart/check → pointer (hand)
 *   - Dark backdrop + empty stage area + caption → default arrow
 * SVG data URL approach matches the gallery zoom-in cursor; identical
 * design but with "−" instead of "+". */
.gallery-lightbox,
.gallery-lightbox-stage,
.gallery-lightbox-caption {
    cursor: default;
}
.gallery-lightbox-imgwrap,
.gallery-lightbox-stage img {
    cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='28' height='28'><circle cx='11' cy='11' r='7.5' fill='white' stroke='black' stroke-width='1.5'/><line x1='7' y1='11' x2='15' y2='11' stroke='black' stroke-width='1.5' stroke-linecap='round'/><line x1='16.5' y1='16.5' x2='25' y2='25' stroke='black' stroke-width='2.5' stroke-linecap='round'/></svg>") 11 11, pointer;
}
.gallery-lightbox-btn,
.gallery-lightbox-btn i,
.gallery-lightbox .gallery-icon,
.gallery-lightbox .gallery-icon i {
    cursor: pointer;
}
.gallery-lightbox-imgwrap {
    position: relative;
    display: inline-block;
    line-height: 0;            /* kill descender gap under img */
    max-width: 90vw;
    max-height: 82vh;
}

/* Lightbox heart + check — minimalist (matches grid icons), scaled up,
 * sit on the image's corners (NOT viewport corners, so they never collide
 * with the close X). */
.gallery-icon-lb {
    width: 44px; height: 44px;
    font-size: 26px;
    z-index: 1001;
}
.gallery-icon-lb-heart { top: 10px; left: 10px; }
.gallery-icon-lb-check { top: 10px; right: 10px; }

/* ── Submit modal ──────────────────────────────────────────────────── */
.gallery-submit-modal {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,.55);
    z-index: 1100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--sp-4);
}
.gallery-submit-modal[hidden] { display: none; }
.gallery-success-icon {
    font-size: 2.5rem;
    color: var(--gallery-check);
    margin-bottom: var(--sp-3);
}
.gallery-submit-modal-content {
    background: #fff;
    border-radius: 14px;
    padding: var(--sp-5) var(--sp-5) var(--sp-4);
    width: 100%;
    max-width: 520px;
    position: relative;
    box-shadow: 0 12px 32px -8px rgba(0,0,0,.3);
    max-height: calc(100vh - 2 * var(--sp-4));
    overflow-y: auto;       /* scroll if content overflows on tiny phones */
}
@media (max-width: 480px) {
    .gallery-submit-modal-content {
        padding: var(--sp-4);
        max-height: calc(100vh - 2 * var(--sp-3));
    }
    .gallery-submit-modal-content h2 { font-size: 1.1rem; }
    /* Keep "Keep browsing" + "Submit selections" SIDE BY SIDE (centered)
     * instead of stacking. Tyler audit: stacked buttons feel unfinished. */
    .gallery-submit-modal-content .action-row,
    .gallery-submit-modal-content .action-row-end {
        flex-direction: row;
        justify-content: center;
        flex-wrap: nowrap;
        gap: var(--sp-2);
    }
    .gallery-submit-modal-content .action-row .btn {
        flex: 0 1 auto;
        padding-left: var(--sp-3);
        padding-right: var(--sp-3);
        white-space: nowrap;
    }
}
.gallery-submit-modal-content h2 {
    font-size: 1.25rem;
    margin: 0 0 var(--sp-3);
}
.gallery-submit-modal-close {
    position: absolute;
    top: 12px; right: 12px;
    background: transparent; border: 0; padding: 6px 10px;
    color: var(--muted); font-size: 1.1rem; cursor: pointer;
    border-radius: 6px;
}
.gallery-submit-modal-close:hover { background: var(--bg-soft); color: var(--ink); }

.form-check-row {
    display: flex; align-items: center; gap: 10px;
    font-size: .9rem; color: var(--ink);
}
.form-check-row input[type="checkbox"] {
    width: 18px; height: 18px; cursor: pointer;
}

/* Lightbox */
.gallery-lightbox {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,.92);
    z-index: 200;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}
.gallery-lightbox[hidden] { display: none; }
.gallery-lightbox-stage {
    flex: 1 1 auto;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--sp-5);
    overflow: hidden;
    min-height: 0;          /* allow flex child to shrink */
}
/* Viewport-relative constraints — the inline-block imgwrap doesn't
 * propagate the parent's percentage max-* to the img, so we constrain
 * the img directly in viewport units. Matches TP's 80vw/80vh. */
.gallery-lightbox-stage img {
    max-width: 90vw;
    max-height: 82vh;
    width: auto;
    height: auto;
    display: block;
    transition: opacity .25s ease;
}
/* Lightbox caption — counter ABOVE filename (TP layout). Centered stack. */
.gallery-lightbox-caption {
    color: #fff;
    width: 100%;
    padding: var(--sp-3) var(--sp-5);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    font-size: .9rem;
    text-align: center;
}
.gallery-lightbox-counter {
    color: rgba(255,255,255,.55);
    font-size: .78rem;
    font-variant-numeric: tabular-nums;
    letter-spacing: .04em;
}

/* Lightbox nav arrows + close — TP style: icon-only, smaller + brighter
 * than the previous pass. Just a clean white chevron with subtle drop
 * shadow for contrast on any image. */
.gallery-lightbox-btn {
    position: absolute;
    background: transparent;
    border: 0;
    color: rgba(255,255,255,.95);
    width: 44px; height: 44px;
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    text-shadow: 0 1px 3px rgba(0,0,0,.5);
    transition: opacity .15s ease, transform .15s ease;
    z-index: 1;
    padding: 0;
    opacity: .85;
}
.gallery-lightbox-btn:hover { opacity: 1; transform: scale(1.18); color: #fff; }
.gallery-lightbox-close { top: 20px; right: 24px; }
.gallery-lightbox-prev  { left: 18px; top: 50%; transform: translateY(-50%); }
.gallery-lightbox-prev:hover  { left: 18px; top: 50%; transform: translateY(-50%) scale(1.18); }
.gallery-lightbox-next  { right: 18px; top: 50%; transform: translateY(-50%); }
.gallery-lightbox-next:hover  { right: 18px; top: 50%; transform: translateY(-50%) scale(1.18); }

/* Loader spinner shown while a medium variant is fetching. Sits in the
 * middle of the imgwrap. NEVER show a pixelated thumb in place of the
 * medium (Tyler audit) — a brief loader is way cleaner. */
.gallery-lightbox-loader {
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    color: rgba(255,255,255,.45);
    font-size: 2rem;
    z-index: 2;
    pointer-events: none;
}

/* Lightbox mobile — tighter nav button positioning + bigger viewport
 * usage. The 18px edge offset feels far on narrow screens. */
@media (max-width: 700px) {
    .gallery-lightbox-prev { left: 8px; }
    .gallery-lightbox-prev:hover { left: 8px; }
    .gallery-lightbox-next { right: 8px; }
    .gallery-lightbox-next:hover { right: 8px; }
    .gallery-lightbox-close { top: 14px; right: 14px; }
    .gallery-lightbox-stage { padding: var(--sp-3); }
    /* Heart/check inside lightbox imgwrap — slightly smaller on mobile
     * so they don't crowd the photo */
    .gallery-icon-lb { width: 40px; height: 40px; font-size: 22px; }
}
.gallery-lightbox-stage img {
    transition: opacity .25s ease;
}

/* Client gallery uses a slightly looser default — tiles are showcased,
 * not managed. Bigger gap, more "exhibition" feel. */
.gallery-grid-client { gap: 10px; }

/* Project view module — small "filmstrip" preview. Uniform height row so
 * the visual baseline aligns, widths flex with each image's native aspect.
 * Never crops; portrait + landscape mix gracefully. */
.project-module-gallery-preview {
    display: flex;
    flex-wrap: nowrap;
    align-items: stretch;
    gap: 6px;
    margin-bottom: var(--sp-3);
    overflow: hidden;        /* hide any overflow if the row would be too wide */
}
.project-module-gallery-preview-tile {
    position: relative;
    flex: 0 0 auto;
    height: 80px;
    border-radius: 6px;
    overflow: hidden;
    background: var(--bg-soft);
}
.project-module-gallery-preview-tile img {
    height: 100%;
    width: auto;
    display: block;          /* natural aspect, no crop */
}

/* Favorites / selections counters on the project-page gallery card. Heart =
   favorites (rose), check = selections (green) — matches the gallery icons. */
.gallery-pick-count {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-variant-numeric: tabular-nums;
}
.gallery-pick-count .fa-heart        { color: var(--gallery-heart); }
.gallery-pick-count .fa-square-check { color: var(--gallery-check); }

/* ── Client Submission card (studio project page) ─────────────────────── */
.client-sub-status {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    flex-wrap: wrap;
    margin-bottom: var(--sp-4);
}
.client-sub-unlock { margin-left: auto; }
.client-sub-group { margin-bottom: var(--sp-4); }
.client-sub-label {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: .72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-light);
    margin-bottom: 6px;
}
.client-sub-label .fa-square-check { color: var(--gallery-check); }
.client-sub-label .fa-heart        { color: var(--gallery-heart); }
.client-sub-list {
    list-style: none;
    margin: 0;
    padding: var(--sp-3) var(--sp-4);
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg-soft);
    max-height: 240px;
    overflow-y: auto;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: .85rem;
    line-height: 1.75;
    color: var(--text);
}
/* Favorites is a collapsible <details>; the summary IS the label row. */
.client-sub-fav > summary {
    cursor: pointer;
    list-style: none;
    user-select: none;
}
.client-sub-fav > summary::-webkit-details-marker { display: none; }
.client-sub-fav-chevron { font-size: .65rem; transition: transform .2s ease; color: var(--text-light); }
.client-sub-fav:not([open]) .client-sub-fav-chevron { transform: rotate(-90deg); }
.client-sub-email { font-size: .9rem; margin: var(--sp-4) 0 0; }
.client-sub-notes {
    padding: var(--sp-3) var(--sp-4);
    border-radius: 10px;
    background: #fefce8;
    border: 1px solid #fde68a;
    font-size: .9rem;
    line-height: 1.5;
}
.client-sub-notes.is-empty { color: var(--text-light); }

/* ──────────────────────────────────────────────────────────────
   Image protection — casual right-click / drag / long-press save
   deterrence across every portal surface. Photographer-SaaS table
   stakes; competitors (Pixieset, ShootProof, etc.) all do this.
   Not real DRM — anyone with dev tools or a screenshot can still
   extract the pixels. Goal is just to stop the 90% who
   right-click → Save Image As.

   Right-click is blocked via a JS contextmenu handler in
   portal.js (wireImageProtection). Drag + iOS long-press save
   are blocked declaratively here.
   ────────────────────────────────────────────────────────────── */
img {
    -webkit-user-drag: none;
    user-drag: none;
    -webkit-touch-callout: none;   /* iOS Safari long-press "Save Image" */
    -webkit-user-select: none;
    user-select: none;
}

/* ──────────────────────────────────────────────────────────────
   Deliverables — admin management list + publish bar.
   (Upload dropzone + queue reuse the .gallery-dropzone / .gallery-queue
   styles; only the file list + publish bar are deliverables-specific.)
   Mobile-first; enhanced ≥600px.
   ────────────────────────────────────────────────────────────── */
/* "Download everything" hero + collapsed per-file list */
.deliverables-download-all {
    display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--sp-4);
    padding: var(--sp-5) var(--sp-6); margin-bottom: var(--sp-4);
    background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-lg);
}
.deliverables-download-all-info { display: flex; align-items: center; gap: var(--sp-4); }
.deliverables-download-all-info > i { font-size: 1.5rem; color: var(--brand-accent); flex: 0 0 auto; }
.deliverables-download-all-title { font-size: 1.05rem; font-weight: 700; }
.deliverables-individual > summary {
    cursor: pointer; font-size: .85rem; font-weight: 600; color: var(--text-med);
    padding: var(--sp-2) 0; list-style: revert;
}
.deliverables-individual > summary:hover { color: var(--text); }

.deliverables-list {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    margin-top: var(--sp-4);
}
.deliverable-row {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-3);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 10px;
    text-decoration: none;   /* row is sometimes an <a> (cross-project list) */
    color: inherit;
}
a.deliverable-row:hover { border-color: var(--border-strong); }

/* Expand all / Collapse all toolbar above a stack of collapsible cards. */
.cards-toolbar {
    display: flex;
    justify-content: flex-start;
    margin-bottom: var(--sp-3);
}
.cards-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: none;
    border: none;
    cursor: pointer;
    color: var(--text-light);
    font: inherit;
    font-size: .8rem;
    font-weight: 600;
    padding: 6px 4px;
    border-radius: 6px;
}
.cards-toggle:hover { color: var(--text); }

/* Banner with a text block + an action button (e.g. gallery unlock).
   Rides on the existing .status-banner-success flex row; wraps on mobile. */
.status-banner:has(.status-banner-action) { flex-wrap: wrap; }
.status-banner-main   { flex: 1 1 auto; min-width: 0; }
.status-banner-action { flex: 0 0 auto; align-self: center; margin: 0; }
@media (max-width: 599px) {
    .status-banner-action { align-self: stretch; width: 100%; }
    .status-banner-action .btn { width: 100%; justify-content: center; }
}
.deliverable-row-icon {
    color: var(--text-light);
    font-size: 1.1rem;
    flex: 0 0 auto;
}
.deliverable-row-body {
    flex: 1 1 auto;
    min-width: 0;   /* enable truncation */
}
.deliverable-row-name {
    font-weight: 600;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.deliverable-row-meta { margin-top: 2px; }
/* Publish button never shrinks or wraps — long meta lines must not squeeze it. */
.deliverable-publish-btn { flex: 0 0 auto; white-space: nowrap; }
.deliverable-row-action {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    border: none;
    background: transparent;
    color: var(--text-light);
    border-radius: 8px;
    cursor: pointer;
}
.deliverable-row-action:hover { color: var(--text); background: var(--bg-soft); }
.deliverable-row-action-danger:hover { color: var(--danger); }

/* Leading select checkbox (draft rows) + aligned spacer (published rows). */
.deliverable-row-check {
    flex: 0 0 auto;
    width: 24px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}
.deliverable-row-check input { width: 16px; height: 16px; cursor: pointer; accent-color: var(--brand-accent); }
.deliverable-row-check-spacer { cursor: default; }
/* Select column only appears when there are 2+ drafts to batch-publish. */
.deliverables-list:not(.is-multi) .deliverable-row-check { display: none; }

/* Bulk-select bar above the file list. */
.deliverables-bulkbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-2) var(--sp-3);
    margin-bottom: var(--sp-3);
    background: var(--bg-soft);
    border: 1px solid var(--border);
    border-radius: 10px;
}
.deliverables-bulkbar[hidden] { display: none; }   /* author display:flex would otherwise beat [hidden] */
.deliverables-bulkbar-actions { display: flex; align-items: center; gap: var(--sp-3); }

/* "Publish all" bar — the prominent one-click path (drafts → one client .zip) */
.deliverables-publishall {
    display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-3);
    background: var(--brand-accent-tint, #fff2ec); border: 1px solid #ffd9c7; border-radius: 10px;
}
.deliverables-publishall[hidden] { display: none; }
.deliverables-publishall-info { display: inline-flex; align-items: center; gap: var(--sp-2); font-size: .88rem; color: var(--text); }
.deliverables-publishall-info > i { color: var(--brand-accent-strong, #c2410c); }
.deliverables-publishall .btn { flex: 0 0 auto; }
@media (max-width: 599px) {
    .deliverables-publishall { flex-direction: column; align-items: stretch; }
    .deliverables-publishall .btn { width: 100%; }
}
.deliverable-row-name-input {
    width: 100%;
    font: inherit;
    font-weight: 600;
    color: var(--text);
    border: 1px solid var(--border-strong);
    border-radius: 6px;
    padding: 2px 6px;
    background: var(--bg);
}


/* ──────────────────────────────────────────────────────────────
   Global search: dropdown "view all" footer + full results page
   ────────────────────────────────────────────────────────────── */
.topbar-search-all {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 12px var(--sp-5);
    border-top: 1px solid var(--border-soft);
    color: var(--brand);
    font-size: .85rem;
    font-weight: 600;
    text-decoration: none;
}
.topbar-search-all:hover { background: var(--bg-soft); }

/* Results page */
.search-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-4);
    border-bottom: 1px solid var(--border);
    margin-bottom: var(--sp-5);
}
.search-tab {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 2px;
    margin-bottom: -1px;
    color: var(--text-light);
    text-decoration: none;
    font-size: .9rem;
    font-weight: 600;
    border-bottom: 2px solid transparent;
}
.search-tab:hover { color: var(--text); }
.search-tab.is-active { color: var(--brand-accent-strong); border-bottom-color: var(--brand-accent); }
.search-tab-count {
    font-size: .72rem;
    font-weight: 600;
    color: var(--text-light);
    background: var(--bg-muted);
    border-radius: 999px;
    padding: 1px 7px;
}

.search-group { margin-bottom: var(--sp-7); }
.search-group-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: var(--sp-3);
}
.search-group-title {
    display: flex; align-items: center; gap: 8px;
    font-size: 1rem; margin: 0;
}
.search-group-title i { color: var(--text-light); }

.search-result-list { display: flex; flex-direction: column; gap: var(--sp-2); }
.search-result {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-3);
    border: 1px solid var(--border);
    border-radius: 10px;
    text-decoration: none;
    color: inherit;
}
.search-result:hover { border-color: var(--border-strong); background: var(--bg-soft); }
.search-result-thumb {
    width: 40px; height: 40px; flex: 0 0 auto;
    object-fit: cover; border-radius: 6px; background: var(--bg-muted);
}
.search-result-icon { width: 20px; text-align: center; color: var(--text-light); flex: 0 0 auto; }
.search-result-body { min-width: 0; display: flex; flex-direction: column; }
.search-result-label { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.search-result-sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Flex rows — reads left-to-right (1,2,3…) and keeps each photo's aspect.
   Fixed row height + natural width, wraps; ragged right edge.
   (CSS masonry columns ordered top-to-bottom per column, which broke
   reading order — 1, 18, 36 across the top.) */
.search-photo-grid { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.search-photo {
    display: block;
    height: 150px;
    flex: 0 0 auto;
    border-radius: 8px;
    overflow: hidden;
    background: var(--bg-muted);
    border: 1px solid var(--border);
}
.search-photo img { height: 100%; width: auto; display: block; }

.search-pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-4);
    margin-top: var(--sp-5);
}

/* ──────────────────────────────────────────────────────────────
   Gallery watermark (Tier 1 overlay) + settings panel
   ────────────────────────────────────────────────────────────── */
/* Tiled overlay above each image. --wm-tile is set inline on an ancestor. */
.wm-overlay {
    position: absolute;
    inset: 0;
    background-image: var(--wm-tile);
    background-repeat: repeat;
    /* --wm-tile-size is set (to the logical tile px) only when JS has swapped
       in a hi-DPI PNG tile; left unset it falls back to the SVG's natural size. */
    background-size: var(--wm-tile-size, auto);
    pointer-events: none;
    /* Print the watermark too (browsers skip background images otherwise). */
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
}
/* Hero image carries an explicit z-index:1, so the overlay must sit above it.
   Match the image's rounded corners so the tile doesn't square them off. */
.gallery-hero .wm-overlay { z-index: 2; border-radius: 6px; }
/* Search-result photo thumbs get the same overlay; keep the tile readable small. */
.search-photo { position: relative; }

.wm-enable-row { display: flex; align-items: center; gap: var(--sp-2); cursor: pointer; font-weight: 600; }
.wm-enable-row input { width: 16px; height: 16px; accent-color: var(--brand-accent); }
.wm-config { margin-top: var(--sp-4); display: grid; grid-template-columns: 1fr; gap: var(--sp-5); }
.wm-config.is-disabled { opacity: .45; pointer-events: none; }
.wm-controls { display: flex; flex-direction: column; gap: var(--sp-4); }
.wm-range { width: 100%; accent-color: var(--brand-accent); }
.wm-preview {
    position: relative;
    min-height: 220px;
    border-radius: 10px;
    overflow: hidden;
    border: 1px solid var(--border);
    background: linear-gradient(135deg, #5f6b78, #97a2ad 55%, #c8cfd6);
}
.wm-preview-label {
    position: absolute; left: 10px; bottom: 8px;
    font-size: .68rem; color: rgba(255,255,255,.85);
    letter-spacing: .05em; text-transform: uppercase;
}
@media (min-width: 700px) {
    .wm-config { grid-template-columns: 1fr 1fr; align-items: start; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Landing / sales page (.lp-*) — public marketing page. Edit's light system:
   white base, near-black ink, orange accent, Inter. Mobile-first.
   ═══════════════════════════════════════════════════════════════════════════ */
.btn-pill { border-radius: var(--radius-pill); }

.lp { background: var(--bg); color: var(--text); }
.lp .lp-accent { color: var(--brand-accent); font-style: italic; }
.lp-eyebrow {
    font-family: var(--font-mono);
    text-transform: uppercase;
    letter-spacing: .14em;
    font-size: .72rem;
    color: var(--brand-accent);
    margin: 0 0 var(--sp-4);
}
.lp-eyebrow::before { content: "— "; opacity: .8; }

/* ── Header ── */
.lp-header {
    position: sticky;
    top: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    padding: var(--sp-4) var(--sp-5);
    background: rgba(255,255,255,.82);
    -webkit-backdrop-filter: saturate(180%) blur(12px);
    backdrop-filter: saturate(180%) blur(12px);
    border-bottom: 1px solid var(--border-soft);
}
.lp-brand { display: inline-flex; align-items: center; gap: var(--sp-3); text-decoration: none; }
.lp-nav { display: none; gap: var(--sp-6); }
.lp-nav-link { color: var(--text-med); text-decoration: none; font-size: .92rem; font-weight: 500; }
.lp-nav-link:hover { color: var(--text); }
.lp-header-cta { font-size: .85rem; padding: 8px 18px; }

/* ── Hero ── */
.lp-hero {
    max-width: 1200px;
    margin: 0 auto;
    padding: var(--sp-9) var(--sp-5) var(--sp-10);
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--sp-9);
    align-items: center;
}
.lp-hero-headline {
    font-size: 2.6rem;
    font-weight: 800;
    line-height: 1.04;
    letter-spacing: -.03em;
    margin: 0 0 var(--sp-5);
    color: var(--brand);
}
.lp-hero-sub {
    font-size: 1.1rem;
    line-height: 1.55;
    color: var(--text-med);
    margin: 0 0 var(--sp-7);
    max-width: 34rem;
}
.lp-hero-sub strong { color: var(--text); font-weight: 600; }

/* ── Waitlist form ── */
.lp-waitlist { max-width: 34rem; }
.lp-waitlist .form-control { margin-bottom: var(--sp-3); }
.lp-waitlist-row { display: grid; grid-template-columns: 1fr; gap: var(--sp-3); }
.lp-waitlist-submit { margin-top: var(--sp-2); }
.lp-waitlist-note {
    font-family: var(--font-mono);
    font-size: .76rem;
    color: var(--text-light);
    margin: var(--sp-3) 0 0;
}
.lp-waitlist-note i { color: var(--success); margin-right: 4px; }

/* ── Product peek ── */
.lp-peek { display: flex; justify-content: center; }
.lp-peek-card {
    width: 100%;
    max-width: 30rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}
.lp-peek-bar {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: var(--sp-3) var(--sp-4);
    background: var(--bg-soft);
    border-bottom: 1px solid var(--border-soft);
}
.lp-peek-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--border-strong); }
.lp-peek-title { margin-left: var(--sp-3); font-family: var(--font-mono); font-size: .72rem; color: var(--text-light); }
.lp-peek-body { padding: var(--sp-3); display: flex; flex-direction: column; gap: var(--sp-3); }
.lp-peek-row {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-3);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius);
}
.lp-peek-avatar {
    flex: 0 0 auto;
    width: 34px; height: 34px;
    border-radius: 8px;
    display: inline-flex; align-items: center; justify-content: center;
    background: rgba(255,107,53,.12);
    color: var(--brand-accent);
    font-weight: 700;
    font-size: .9rem;
}
.lp-peek-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.lp-peek-name { font-weight: 600; font-size: .9rem; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lp-peek-meta { font-family: var(--font-mono); font-size: .7rem; color: var(--text-light); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lp-peek-chip {
    flex: 0 0 auto;
    font-family: var(--font-mono);
    font-size: .62rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    font-weight: 600;
    padding: 4px 9px;
    border-radius: var(--radius-pill);
}
.lp-peek-chip-amber { background: rgba(255,107,53,.12); color: #b45309; }
.lp-peek-chip-green { background: var(--success-bg); color: var(--success); }
.lp-peek-chip-ink   { background: var(--brand); color: #fff; }
.lp-peek-foot {
    display: flex;
    justify-content: space-between;
    padding: var(--sp-3) var(--sp-4);
    border-top: 1px solid var(--border-soft);
    font-family: var(--font-mono);
    font-size: .68rem;
    color: var(--text-light);
}

/* ── Replaces strip ── */
.lp-replaces {
    border-top: 1px solid var(--border-soft);
    border-bottom: 1px solid var(--border-soft);
    padding: var(--sp-6) var(--sp-5);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: var(--sp-3) var(--sp-5);
    text-align: center;
}
.lp-replaces-label {
    font-family: var(--font-mono);
    text-transform: uppercase;
    letter-spacing: .12em;
    font-size: .7rem;
    color: var(--text);
    font-weight: 600;
}
.lp-replaces-list {
    list-style: none;
    margin: 0; padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-3) var(--sp-5);
    justify-content: center;
}
.lp-replaces-list li {
    font-family: var(--font-mono);
    font-size: .82rem;
    color: var(--text-light);
    text-decoration: line-through;
    text-decoration-color: var(--brand-accent);
}

/* ── Generic section ── */
.lp-section { max-width: 1100px; margin: 0 auto; padding: var(--sp-10) var(--sp-5); }
.lp-section-alt {
    max-width: none;
    background: var(--bg-soft);
    border-top: 1px solid var(--border-soft);
    border-bottom: 1px solid var(--border-soft);
}
.lp-section-alt > * { max-width: 1100px; margin-left: auto; margin-right: auto; }
.lp-section-head { max-width: 40rem; margin: 0 auto var(--sp-9); text-align: center; }
.lp-section-title {
    font-size: 2rem;
    font-weight: 800;
    letter-spacing: -.02em;
    line-height: 1.1;
    margin: 0 0 var(--sp-4);
    color: var(--brand);
}
.lp-section-sub { font-size: 1.05rem; color: var(--text-med); line-height: 1.5; margin: 0; }

/* ── Features grid ── */
.lp-features { display: grid; grid-template-columns: 1fr; gap: var(--sp-5); }
.lp-feature {
    padding: var(--sp-6);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--bg);
}
.lp-feature-icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 42px; height: 42px;
    border-radius: 10px;
    background: rgba(255,107,53,.12);
    color: var(--brand-accent);
    font-size: 1.05rem;
    margin-bottom: var(--sp-4);
}
.lp-feature-title { font-size: 1.05rem; font-weight: 700; margin: 0 0 var(--sp-2); color: var(--brand); }
.lp-feature-blurb { font-size: .92rem; line-height: 1.5; color: var(--text-med); margin: 0; }

/* ── Workflow steps ── */
.lp-steps { list-style: none; margin: 0 auto; padding: 0; max-width: 1000px; display: grid; grid-template-columns: 1fr; gap: var(--sp-6); }
.lp-step { position: relative; }
.lp-step-num {
    display: inline-flex; align-items: center; justify-content: center;
    width: 32px; height: 32px;
    border-radius: 50%;
    background: var(--brand);
    color: #fff;
    font-weight: 700;
    font-size: .9rem;
    margin-bottom: var(--sp-3);
}
.lp-step-title { font-size: 1.05rem; font-weight: 700; margin: 0 0 var(--sp-1); color: var(--brand); }
.lp-step-blurb { font-size: .9rem; line-height: 1.5; color: var(--text-med); margin: 0; }

/* ── Why grid ── */
.lp-why { display: grid; grid-template-columns: 1fr; gap: var(--sp-6); }
.lp-why-title { font-size: 1.05rem; font-weight: 700; margin: 0 0 var(--sp-2); color: var(--brand); display: flex; align-items: center; gap: var(--sp-3); }
.lp-why-title i { color: var(--brand-accent); font-size: .95rem; }
.lp-why-blurb { font-size: .95rem; line-height: 1.55; color: var(--text-med); margin: 0; }

/* ── Closing CTA (dark band) ── */
.lp-cta {
    background: var(--bg-dark);
    color: var(--text-inverse);
    text-align: center;
    padding: var(--sp-11) var(--sp-5);
}
.lp-cta-title { font-size: 2rem; font-weight: 800; letter-spacing: -.02em; margin: 0 0 var(--sp-4); color: #fff; }
.lp-cta-sub { font-size: 1.05rem; color: rgba(255,255,255,.7); margin: 0 0 var(--sp-7); }
.lp-cta-form {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
    max-width: 30rem;
    margin: 0 auto var(--sp-4);
}
.lp-cta-form .form-control { background: #fff; border-color: transparent; }
.lp-cta-submit { background: var(--brand-accent); border-color: var(--brand-accent); color: #0a0a0a; }
.lp-cta-submit:hover { background: #ff7d4d; border-color: #ff7d4d; }
.lp-cta .lp-waitlist-note { color: rgba(255,255,255,.55); }

/* ── Footer ── */
.lp-footer {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-8) var(--sp-5);
    text-align: center;
}
.lp-footer-tag { font-family: var(--font-mono); font-size: .76rem; color: var(--text-light); }
.lp-footer-built { margin-top: var(--sp-3); font-size: .8rem; color: var(--text-med); }
.lp-footer-heart { color: var(--brand-accent); font-style: normal; }
.lp-footer-copy { font-size: .72rem; color: var(--text-light); }
.lp-footer-author { color: var(--text-med); text-decoration: underline; text-underline-offset: 2px; }
.lp-footer-author:hover { color: var(--brand-accent); }

/* ── Responsive ── */
@media (min-width: 480px) {
    .lp-waitlist-row { grid-template-columns: 1fr 1fr; }
    .lp-cta-form { flex-direction: row; }
    .lp-cta-form .form-control { flex: 1; }
}
@media (min-width: 700px) {
    .lp-features { grid-template-columns: 1fr 1fr; }
    .lp-why { grid-template-columns: 1fr 1fr; }
    .lp-steps { grid-template-columns: repeat(5, 1fr); gap: var(--sp-5); }
    .lp-hero-headline { font-size: 3.2rem; }
    .lp-section-title, .lp-cta-title { font-size: 2.4rem; }
}
@media (min-width: 900px) {
    .lp-nav { display: flex; }
    .lp-hero { grid-template-columns: 1.05fr .95fr; padding-top: var(--sp-11); padding-bottom: var(--sp-12); }
    .lp-hero-headline { font-size: 3.6rem; }
}
@media (min-width: 1000px) {
    .lp-features { grid-template-columns: repeat(4, 1fr); }
}

/* Landing page: smooth anchor scrolling + offset for the sticky header. */
html:has(.lp) { scroll-behavior: smooth; }
.lp [id] { scroll-margin-top: 76px; }

/* ── Storage usage card (Studio settings) ───────────────────────────────── */
.usage-card {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--sp-5) var(--sp-6);
    margin-bottom: var(--sp-6);
    max-width: 940px;
}
.usage-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--sp-4);
    margin-bottom: var(--sp-4);
}
.usage-title { font-size: 1rem; font-weight: 700; margin: 0; color: var(--text); }
.usage-sub { font-size: .85rem; color: var(--text-light); margin: 2px 0 0; }
.usage-figure { text-align: right; white-space: nowrap; }
.usage-used { font-size: 1.15rem; font-weight: 800; color: var(--text); font-variant-numeric: tabular-nums; }
.usage-quota { font-size: .82rem; color: var(--text-light); margin-left: 4px; }
.usage-meter {
    height: 10px;
    border-radius: var(--radius-pill);
    background: var(--bg-muted);
    overflow: hidden;
}
.usage-bar {
    height: 100%;
    border-radius: var(--radius-pill);
    background: var(--brand);                 /* ink fill (calm/neutral) */
    transition: width .3s ease;
}
/* Guarantee a visible sliver whenever there's any usage, even sub-1%. */
.usage-bar-nonzero { min-width: 4px; }
.usage-warn .usage-bar { background: var(--warn); }
.usage-over .usage-bar { background: var(--danger); }
.usage-detail { font-size: .85rem; color: var(--text); margin: var(--sp-3) 0 0; }
.usage-over-note { display: block; color: var(--danger); margin-top: 4px; font-weight: 600; }

/* ── Calendar (studio) ────────────────────────────────────────────────────── */
.cal-nav { gap: var(--sp-2); align-items: center; flex-wrap: wrap; }
.cal-monthnav { display: inline-flex; align-items: center; gap: var(--sp-2); }
.cal-month-label { font-weight: 600; min-width: 9.5rem; text-align: center; }

.cal-legend { display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap; margin-bottom: var(--sp-4); }
.cal-legend-item { display: inline-flex; align-items: center; gap: 6px; font-size: .8rem; color: var(--text-med); }
.cal-legend-hint { margin-left: auto; }

/* Subscribe-to-calendar modal */
.cal-sub-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.cal-sub-actions .btn { flex: 1 1 auto; justify-content: center; }
.cal-sub-foot { justify-content: space-between; }
[data-cal-feed-url] { flex: 1 1 auto; min-width: 0; }

.cal-dot { width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; }
.cal-dot-shoot    { background: var(--info); }
.cal-dot-deadline { background: var(--brand-accent); }
.cal-dot-invoice  { background: var(--danger); }

/* Month grid — 1px gap on a border-colored backdrop draws the gridlines. */
.cal-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 1px;
    background: var(--border);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
}
.cal-weekday {
    background: var(--bg-soft);
    padding: var(--sp-2);
    text-align: center;
    font-size: .72rem;
    text-transform: uppercase;
    letter-spacing: .05em;
    font-weight: 600;
    color: var(--text-light);
}
.cal-cell {
    background: #fff;
    min-height: 7.25rem;
    padding: var(--sp-2);
    display: flex;
    flex-direction: column;
    gap: 4px;
    cursor: pointer;
    transition: background .12s ease;
}
.cal-cell:hover { background: var(--bg-soft); }
.cal-cell:focus-visible { outline: 2px solid var(--brand-accent); outline-offset: -2px; }
.cal-cell-empty { background: var(--bg-soft); cursor: default; }
.cal-cell-empty:hover { background: var(--bg-soft); }
.cal-daynum {
    font-size: .8rem;
    font-weight: 500;
    color: var(--text-med);
    align-self: flex-start;
}
.cal-cell.is-today .cal-daynum {
    background: var(--brand);
    color: #fff;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.cal-events { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.cal-ev {
    display: block;
    font-size: .72rem;
    line-height: 1.3;
    padding: 2px 6px;
    border-radius: 5px;
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.cal-ev-shoot           { background: var(--info-bg);   color: var(--info); }
.cal-ev-shoot-tentative { background: var(--bg-muted);  color: var(--text-med); }
.cal-ev-deadline        { background: var(--brand-accent-tint); color: var(--brand-accent-strong); }
.cal-ev-invoice         { background: var(--danger-bg); color: var(--danger); }
.cal-more { font-size: .68rem; color: var(--text-light); padding: 0 6px; }

@media (max-width: 700px) {
    .cal-cell { min-height: 4.75rem; padding: 4px; }
    .cal-weekday { font-size: .6rem; padding: 4px; }
    .cal-ev { font-size: .62rem; padding: 1px 4px; }
    .cal-legend-hint { display: none; }

    /* Buttons (Subscribe/Today) on one row; month nav wraps to its own full
       row beneath so nothing runs off-screen. */
    .cal-nav { row-gap: var(--sp-3); }
    .cal-monthnav { flex: 1 1 100%; justify-content: space-between; }
    .cal-month-label { flex: 1 1 auto; }
}

/* Day → create modal actions */
.cal-day-actions { display: flex; flex-direction: column; gap: var(--sp-3); }
.cal-day-action {
    display: flex;
    align-items: center;
    gap: var(--sp-4);
    padding: var(--sp-4);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    text-decoration: none;
    color: var(--text);
    transition: background .12s ease, border-color .12s ease;
}
.cal-day-action:hover { background: var(--bg-soft); border-color: var(--border-strong); }
.cal-day-action > i { font-size: 1.15rem; color: var(--brand-accent-strong); width: 1.4rem; text-align: center; flex-shrink: 0; }
.cal-day-action span { display: flex; flex-direction: column; }
.cal-day-action small { color: var(--text-light); font-size: .8rem; }

/* Upcoming list under the calendar — count selector + JS row limiting */
.cal-upcoming-count { font-size: .8rem; color: var(--text-light); display: inline-flex; align-items: center; gap: var(--sp-2); }
.cal-upcoming-count select {
    font: inherit;
    font-size: .82rem;
    padding: 2px 1.6rem 2px var(--sp-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background-color: #fff;
    color: var(--text);
    cursor: pointer;
}
.dash-week-item-hidden { display: none; }
