:root {
    --primary: #4f46e5;
    --primary-hover: #4338ca;
    --primary-light: #eef2ff;
    --sidebar-bg: #0f172a;
    --sidebar-width: 260px;
    --sidebar-active: #1e293b;
    --sidebar-text: #94a3b8;
    --sidebar-text-active: #ffffff;
    --topbar-height: 64px;
    --body-bg: #f1f5f9;
    --card-bg: #ffffff;
    --text-primary: #0f172a;
    --text-secondary: #64748b;
    --border-color: #e2e8f0;
    --success: #10b981;
    --success-light: #d1fae5;
    --warning: #f59e0b;
    --warning-light: #fef3c7;
    --danger: #ef4444;
    --danger-light: #fee2e2;
    --info: #3b82f6;
    --info-light: #dbeafe;

    /* #33 §2/§18. A type scale, so the next component does not invent another
       font-size. The ~300 existing literals are deliberately left alone:
       rewriting them blind is how a layout regression ships. New work reaches
       for these, and the old values are already close to them. */
    --fs-xs: 0.75rem;
    --fs-sm: 0.8125rem;
    --fs-base: 0.9375rem;
    --fs-lg: 1.0625rem;
    --fs-xl: 1.25rem;
    --fs-2xl: 1.5rem;
    --fs-3xl: 1.875rem;

    /* Weights are a scale too, and the reason the pages looked inconsistent:
       labels, card headers and page titles were each picking their own. */
    --fw-label: 500;
    --fw-heading: 600;
    --fw-title: 700;

    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 24px;
    --space-6: 32px;

    --radius-sm: 6px;
    --radius: 8px;
    --radius-lg: 12px;
    --radius-xl: 16px;

    --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06);
    --shadow: 0 1px 3px rgba(15, 23, 42, 0.08), 0 1px 2px rgba(15, 23, 42, 0.04);
    --shadow-lg: 0 10px 30px rgba(15, 23, 42, 0.12);

    /* The one focus ring for the whole app. Bootstrap's is a 0.25rem alpha
       shadow in the component's own colour, which on a white card next to a
       light border is close to invisible — and it is not applied at all to
       links, table actions or the sidebar. */
    --focus-ring: 0 0 0 3px rgba(79, 70, 229, 0.35);
}

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

/* 14px was a full step below what this much dense tabular text needs, and the
   inherited line-height was Bootstrap's 1.5 against a smaller box, so rows read
   as tighter than they measured. 15px/1.5 is the change with the widest reach in
   #33 and the one nothing else depends on. */
body {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
    background: var(--body-bg);
    color: var(--text-primary);
    font-size: 15px;
    line-height: 1.5;
}

.fw-500 { font-weight: 500; }
.fw-600 { font-weight: 600; }
.x-small { font-size: 0.7rem; }
.text-muted-light { color: #64748b; }

/* ===== Accessibility ===== */

/* First tab stop on every page. Off-screen until focused, at which point it has
   to be visible *and* above the sticky topbar, or it is a link that announces
   itself and cannot be seen. */
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 2000;
    background: var(--primary);
    color: #fff;
    padding: 10px 16px;
    border-bottom-right-radius: var(--radius);
    font-size: var(--fs-sm);
    font-weight: var(--fw-heading);
    text-decoration: none;
}

.skip-link:focus {
    left: 0;
    color: #fff;
}

/* :focus-visible, not :focus — a mouse click on a button should not leave a
   ring behind, which is why the blanket :focus rules of a decade ago were
   turned off by everyone and accessibility lost the ring entirely. */
a:focus-visible,
button:focus-visible,
.btn:focus-visible,
.nav-link-item:focus-visible,
.chat-list-item:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
    box-shadow: var(--focus-ring);
}

.form-control:focus-visible,
.form-select:focus-visible,
.form-check-input:focus-visible {
    outline: 2px solid transparent;
    box-shadow: var(--focus-ring);
}

/* Inside the dark sidebar the indigo ring on near-black is not a contrast the
   WCAG non-text minimum is met at. White is. */
.app-sidebar a:focus-visible,
.app-sidebar button:focus-visible {
    outline-color: #fff;
    box-shadow: none;
}

/* Someone who has asked their OS to stop moving things means it. The sidebar
   slide, the recording pulse and every hover transition are decoration. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }
}

/* ===== App Layout ===== */
.app-wrapper {
    display: flex;
    min-height: 100vh;
}

/* ===== Sidebar ===== */
.app-sidebar {
    width: var(--sidebar-width);
    background: var(--sidebar-bg);
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    z-index: 1040;
    display: flex;
    flex-direction: column;
    transition: transform 0.3s ease;
}

.sidebar-brand {
    height: var(--topbar-height);
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 20px;
    font-size: 1.2rem;
    font-weight: 700;
    color: #fff;
    border-bottom: 1px solid rgba(255,255,255,0.06);
}

.sidebar-brand i {
    font-size: 1.5rem;
    color: #25d366;
}

/* An admin-uploaded logo (#23). Bounded in both directions so a tall or very
   wide upload cannot push the brand row out of the topbar's height, which is a
   fixed layout variable everything else lines up against. */
.sidebar-brand-logo {
    max-height: 34px;
    max-width: 100%;
    width: auto;
    object-fit: contain;
}

.sidebar-nav {
    flex: 1;
    overflow-y: auto;
    padding: 16px 0;
}

/* #33 §6. The four sections (Main / WhatsApp / Account / Platform) were
   separated by 8px and a small caption, which at a glance is one long list of
   eleven links. A rule plus real space makes the grouping do its job. */
.nav-section {
    margin-bottom: var(--space-3);
    padding-bottom: var(--space-3);
    border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.nav-section:last-child {
    margin-bottom: 0;
    padding-bottom: 0;
    border-bottom: none;
}

.nav-section-title {
    display: block;
    padding: 8px 20px 4px;
    font-size: 0.65rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: #475569;
}

.nav-link-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 20px;
    color: var(--sidebar-text);
    text-decoration: none;
    font-size: 0.875rem;
    font-weight: 500;
    transition: all 0.15s ease;
    border-left: 3px solid transparent;
}

.nav-link-item:hover {
    color: var(--sidebar-text-active);
    background: rgba(255,255,255,0.04);
}

/* The active row differed from its neighbours by a 3px bar and a background one
   step off the sidebar's own — findable only by looking for it. The icon carries
   the state too now, which is what the eye lands on first. */
.nav-link-item.active {
    color: var(--sidebar-text-active);
    background: var(--sidebar-active);
    border-left-color: var(--primary);
    font-weight: var(--fw-heading);
}

.nav-link-item i {
    font-size: 1.1rem;
    width: 20px;
    text-align: center;
}

.nav-link-item.active i {
    color: var(--sidebar-text-active);
}

.sidebar-footer {
    padding: 16px 20px;
    border-top: 1px solid rgba(255,255,255,0.06);
}

.sidebar-user-info {
    min-width: 0;
    overflow: hidden;
}

.sidebar-user-info div {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sidebar-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.5);
    z-index: 1039;
}

/* ===== Main Content ===== */
.app-main {
    flex: 1;
    margin-left: var(--sidebar-width);
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

.app-topbar {
    height: var(--topbar-height);
    background: var(--card-bg);
    border-bottom: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 24px;
    position: sticky;
    top: 0;
    z-index: 1020;
}

.app-content {
    flex: 1;
    padding: var(--space-5);
}

/* #33 §2. The page title is the largest thing in the chrome and was 600 — the
   same weight as every card header on the page under it, so it did not read as
   the title of anything. The size is unchanged (--fs-xl is Bootstrap's own h5);
   only the weight moves, because the size was never the problem. */
.app-topbar h5 {
    font-size: var(--fs-xl);
    font-weight: var(--fw-title);
}

/* #33 §9/§17. A 44×44 target is the floor in both the WCAG 2.5.5 guidance and
   Apple's HIG, and this is the *only* way to reach the navigation on a phone —
   the one control on the app that must not be fiddly. Bootstrap's .btn-link
   padding gave it about 30×24. */
.sidebar-toggle {
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
}

/* #33 §3. A row for the actions that belong to the whole page. It exists
   because the tenant dashboard was putting its "Link account" button inside the
   stat-card grid, where a button reads as a metric. */
.page-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    flex-wrap: wrap;
    margin-bottom: var(--space-5);
}

.page-toolbar .page-toolbar-title {
    font-size: var(--fs-lg);
    font-weight: var(--fw-heading);
    margin: 0;
}

.page-toolbar .page-toolbar-subtitle {
    font-size: var(--fs-sm);
    color: var(--text-secondary);
    margin: 2px 0 0;
}

.page-toolbar-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

/* ===== User Avatar ===== */
.user-avatar-sm {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--primary);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: 0.8rem;
    flex-shrink: 0;
}

.user-avatar-lg {
    width: 80px;
    height: 80px;
    border-radius: 50%;
    background: var(--primary);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 2rem;
    flex-shrink: 0;
}

.user-menu-btn {
    display: flex;
    align-items: center;
    gap: 8px;
    text-decoration: none;
    color: var(--text-primary);
    font-weight: 500;
    font-size: 0.875rem;
}

.user-menu-btn::after {
    font-size: 0.7rem;
}

/* ===== Cards ===== */
.card {
    background: var(--card-bg);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
}

.card-header {
    background: transparent;
    border-bottom: 1px solid var(--border-color);
    padding: var(--space-4) 20px;
    font-weight: var(--fw-heading);
}

/* #33 §7. An icon in the header, so a page of six stacked cards can be scanned
   rather than read. Tinted rather than inherited: at the header's own weight an
   icon in the text colour is just another glyph in the sentence. */
.card-header > i:first-child,
.card-header .card-header-icon {
    color: var(--primary);
    margin-right: var(--space-2);
    font-size: var(--fs-lg);
    vertical-align: -1px;
}

.card-body {
    padding: 20px;
}

/* ===== Stat Cards ===== */
.stat-card {
    border-radius: 12px;
    padding: 20px;
    border: 1px solid var(--border-color);
    background: var(--card-bg);
    box-shadow: 0 1px 3px rgba(0,0,0,0.04);
}

.stat-card .stat-icon {
    width: 48px;
    height: 48px;
    border-radius: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.3rem;
}

.stat-card .stat-value {
    font-size: 1.75rem;
    font-weight: 700;
    line-height: 1.2;
}

.stat-card .stat-label {
    font-size: 0.8rem;
    color: var(--text-secondary);
    font-weight: 500;
}

/* ===== Status Badges =====
 *
 * #33 §1/§7 asked for one pill-shaped component for every status. The
 * vocabulary itself is not redefined here — it lives in waStatusLabel() /
 * waStatusClass() (and now apptStatusLabel() / apptStatusClass()) so a status
 * cannot render two ways. What is added is a *semantic* tier underneath the
 * connection-specific names, for the statuses that are not connections:
 * appointments, subscriptions, tenants. Pill for a state, rectangular for a
 * tag — which is why `.badge-tag` is separate and square-ish. */
.badge-status {
    display: inline-block;
    font-size: 0.7rem;
    font-weight: var(--fw-heading);
    padding: 4px 10px;
    border-radius: 20px;
    line-height: 1.4;
    white-space: nowrap;
}

.badge-connected,
.badge-ok {
    background: var(--success-light);
    color: #065f46;
}

.badge-warn {
    background: var(--warning-light);
    color: #92400e;
}

.badge-bad {
    background: var(--danger-light);
    color: #991b1b;
}

.badge-pending {
    background: var(--info-light);
    color: #1e40af;
}

.badge-neutral {
    background: #f1f5f9;
    color: #475569;
}

/* A tag is not a state: it labels a row rather than describing where it is in a
   lifecycle, so it does not get the pill. */
.badge-tag {
    display: inline-block;
    font-size: 0.7rem;
    font-weight: var(--fw-label);
    padding: 3px 7px;
    border-radius: var(--radius-sm);
    background: #f1f5f9;
    color: #475569;
    white-space: nowrap;
}

/* badge-failed had no rule at all, so the one status that always needs a human
   rendered as unstyled text — the least visible badge on the page. */
.badge-disconnected,
.badge-logged_out,
.badge-failed {
    background: var(--danger-light);
    color: #991b1b;
}

.badge-qr_required {
    background: var(--warning-light);
    color: #92400e;
}

.badge-reconnecting {
    background: var(--info-light);
    color: #1e40af;
}

/* ===== Tables ===== */
.table-card {
    border-radius: 12px;
    overflow: hidden;
}

.table-card .table {
    margin-bottom: 0;
}

.table-card .table th {
    background: #f8fafc;
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-secondary);
    border-bottom: 1px solid var(--border-color);
    padding: 12px 16px;
}

.table-card .table td {
    padding: 14px 16px;
    vertical-align: middle;
    border-bottom: 1px solid var(--border-color);
    font-size: 0.875rem;
}

.table-card .table tr:last-child td {
    border-bottom: none;
}

/* #33 §4. Hover, not stripes. A striped table encodes nothing — the bands are
   there whatever the data says — while a hover row tells you which one your
   pointer is on, which is the question you actually have when a row has three
   action buttons at the end of it. Bootstrap's own hover variable is what
   .table-hover reads, so setting it here works with or without the class. */
.table-card .table tbody tr {
    transition: background 0.12s ease;
}

.table-card .table tbody tr:hover {
    background: #f8fafc;
}

.table-card .table > :not(caption) > * > * {
    box-shadow: none;                /* Bootstrap draws its stripes with one */
}

/* Numbers are compared down a column, not read across a row, so they align on
   the right — and in a font where the digits are all the same width, or the
   columns do not line up even when the text does. */
.table-card .table th.num,
.table-card .table td.num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Opt-in, because it only helps a table long enough to scroll past its own
   header, and it needs an ancestor that scrolls rather than clips: it is the
   .table-responsive that scrolls, so the sticky offset is relative to it. */
.table-sticky {
    max-height: 70vh;
    overflow-y: auto;
}

.table-sticky .table th {
    position: sticky;
    top: 0;
    z-index: 2;
}

/* ===== Auth Pages ===== */
.auth-body {
    background: linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%);
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
}

.auth-card {
    width: 100%;
    max-width: 440px;
    background: #fff;
    border-radius: 16px;
    padding: 40px;
    box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}

.auth-brand {
    text-align: center;
    margin-bottom: 32px;
}

.auth-brand i {
    font-size: 2.5rem;
    color: #25d366;
}

.auth-brand h2 {
    font-size: 1.5rem;
    font-weight: 700;
    margin-top: 8px;
    color: var(--text-primary);
}

.auth-brand p {
    color: var(--text-secondary);
    font-size: 0.875rem;
    margin-top: 4px;
}

/* An admin-uploaded logo on the sign-in card (#23). Taller than the sidebar's
   because this card is the widest thing on the page, but still bounded — the
   card has a fixed max-width and an unconstrained image would overflow it. */
.auth-brand-logo {
    max-height: 56px;
    max-width: 100%;
    width: auto;
    object-fit: contain;
}

/* ===== Form Styles ===== */
.form-control,
.form-select {
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 10px 14px;
    font-size: 0.875rem;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.form-control:focus,
.form-select:focus {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
}

.form-label {
    font-weight: var(--fw-label);
    font-size: 0.875rem;
    margin-bottom: 6px;
    color: var(--text-primary);
}

/* #33 §5. A disabled field looked like an ordinary one that happened to be
   empty — same border, same fill — so "you may not change this" was only
   discoverable by clicking it. The cursor is half the fix: it is the part that
   answers the click. */
.form-control:disabled,
.form-select:disabled,
.form-check-input:disabled {
    background: #f1f5f9;
    color: #64748b;
    cursor: not-allowed;
    border-color: var(--border-color);
}

.btn-primary {
    background: var(--primary);
    border-color: var(--primary);
    border-radius: 8px;
    padding: 10px 20px;
    font-weight: 600;
    font-size: 0.875rem;
    transition: all 0.15s ease;
}

.btn-primary:hover {
    background: var(--primary-hover);
    border-color: var(--primary-hover);
}

.btn-outline-primary {
    border-color: var(--primary);
    color: var(--primary);
    border-radius: 8px;
    padding: 10px 20px;
    font-weight: 600;
    font-size: 0.875rem;
}

.btn-outline-primary:hover {
    background: var(--primary);
    border-color: var(--primary);
}

.btn-sm {
    padding: 6px 20px;
    font-size: 0.8rem;
    justify-content: space-between;
    align-items: center;
    display: inline-flex;
}

/* ===== QR Code ===== */
.qr-container {
    background: #fff;
    border: 2px dashed var(--border-color);
    border-radius: 16px;
    padding: 32px;
    text-align: center;
    min-height: 320px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

.qr-container img {
    max-width: 256px;
    border-radius: 8px;
}

.qr-container .qr-status {
    margin-top: 16px;
    font-size: 0.875rem;
    font-weight: 500;
}

/* ===== Sync Status Bar ===== */
.sync-status-bar {
    background: #e8f5e9;
    border: 1px solid #c8e6c9;
    border-radius: 8px;
    padding: 8px 16px;
    margin-bottom: 12px;
    font-size: 0.82rem;
    color: #2e7d32;
}

.sync-status-bar.sync-done {
    background: #e3f2fd;
    border-color: #bbdefb;
    color: #1565c0;
}

.sync-status-inner {
    display: flex;
    align-items: center;
}

.sync-status-inner .spinner-border {
    width: 14px;
    height: 14px;
    border-width: 2px;
    color: #2e7d32;
}

.sync-done .sync-status-inner .spinner-border {
    display: none;
}

/* ===== Chat UI ===== */
.chat-wrapper {
    display: flex;
    height: calc(100vh - var(--topbar-height) - 48px);
    background: var(--card-bg);
    border-radius: 12px;
    border: 1px solid var(--border-color);
    overflow: hidden;
}

.chat-sidebar {
    width: 340px;
    border-right: 1px solid var(--border-color);
    display: flex;
    flex-direction: column;
    flex-shrink: 0;
}

.chat-sidebar-header {
    padding: 8px 12px;
    border-bottom: 1px solid var(--border-color);
    background: #fafbfc;
}

.chat-search-wrap {
    display: flex;
    align-items: center;
    gap: 8px;
    background: #f0f2f5;
    border-radius: 8px;
    padding: 6px 12px;
}

.chat-search-wrap i {
    color: var(--text-secondary);
    font-size: 0.85rem;
    flex-shrink: 0;
}

.chat-search-wrap input {
    border: none;
    background: transparent;
    outline: none;
    font-size: 0.85rem;
    color: var(--text-primary);
    width: 100%;
}

.chat-search-wrap input::placeholder {
    color: #8696a0;
}

.chat-list {
    flex: 1;
    overflow-y: auto;
}

/* "Archived (n)" row that opens the archived-only view, and doubles as the way
   back out of it. Sits at the top of the chat list, as in WhatsApp Web. */
.chat-list-header {
    display: flex;
    align-items: center;
    padding: 12px 16px;
    cursor: pointer;
    border-bottom: 1px solid #f1f3f5;
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--primary);
    background: #f8fafc;
    transition: background 0.1s ease;
}

.chat-list-header:hover {
    background: #eef2ff;
}

.chat-list-header i {
    font-size: 1.05rem;
}

.chat-list-header .chat-list-count {
    margin-left: auto;
    background: var(--primary);
    color: #fff;
    border-radius: 10px;
    padding: 1px 8px;
    font-size: 0.7rem;
    font-weight: 600;
}

.chat-header-archived {
    margin-left: 8px;
    font-size: 0.65rem;
    font-weight: 500;
    color: #64748b;
    background: #e2e8f0;
    border-radius: 4px;
    padding: 2px 6px;
    vertical-align: middle;
}

.chat-list-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 16px;
    cursor: pointer;
    border-bottom: 1px solid #f1f3f5;
    transition: background 0.1s ease;
}

.chat-list-item:hover {
    background: #f8f9fa;
}

.chat-list-item.active {
    background: var(--primary-light);
}

.chat-list-item .chat-avatar {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background: var(--primary);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: 1rem;
    flex-shrink: 0;
}

.chat-list-item .chat-avatar.chat-avatar-group {
    background: #25d366;
}

/* Group avatars render an icon rather than an initial, so the glyph needs to be
   sized independently of the initial's font-size. */
.chat-avatar.chat-avatar-group i,
.chat-header-avatar.chat-avatar-group i {
    font-size: 1.25rem;
}

.chat-header-avatar.chat-avatar-group {
    background: #25d366;
}

.chat-list-item .chat-info {
    flex: 1;
    min-width: 0;
}

.chat-list-item .chat-info-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 2px;
}

.chat-list-item .chat-name {
    font-weight: 600;
    font-size: 0.875rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text-primary);
    flex: 1;
    min-width: 0;
}

.chat-list-item .chat-time {
    font-size: 0.7rem;
    color: var(--text-secondary);
    flex-shrink: 0;
    white-space: nowrap;
}

.chat-list-item.active .chat-time {
    color: var(--primary);
}

.chat-list-item .chat-preview {
    font-size: 0.8rem;
    color: var(--text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    line-height: 1.4;
}

.chat-main {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.chat-main-header {
    padding: 12px 20px;
    border-bottom: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    gap: 12px;
    background: #fafbfc;
}

.chat-header-avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: var(--primary);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: 0.9rem;
    flex-shrink: 0;
}

.chat-header-info {
    flex: 1;
    min-width: 0;
}

.chat-header-name {
    font-weight: 600;
    font-size: 0.9rem;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.chat-header-status {
    font-size: 0.75rem;
    color: var(--text-secondary);
}

.chat-messages {
    flex: 1;
    overflow-y: auto;
    padding: 16px 60px;
    background: #efeae2;
    background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23d5d0c8' fill-opacity='0.15'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
}

.chat-loading {
    text-align: center;
    padding: 40px;
    color: var(--text-secondary);
    font-size: 0.85rem;
}

.chat-no-messages {
    text-align: center;
    padding: 40px;
    color: var(--text-secondary);
    font-size: 0.85rem;
}

.date-separator {
    text-align: center;
    margin: 16px 0;
}

.date-separator span {
    background: #fff;
    color: var(--text-secondary);
    font-size: 0.75rem;
    font-weight: 500;
    padding: 5px 14px;
    border-radius: 8px;
    box-shadow: 0 1px 2px rgba(0,0,0,0.08);
    display: inline-block;
}

.msg-row {
    display: flex;
    margin-bottom: 2px;
}

.msg-row.msg-out {
    justify-content: flex-end;
}

.msg-row.msg-in {
    justify-content: flex-start;
}

.chat-bubble {
    max-width: 65%;
    padding: 6px 8px 4px;
    border-radius: 8px;
    font-size: 0.875rem;
    line-height: 1.45;
    position: relative;
    word-wrap: break-word;
}

.chat-bubble.incoming {
    background: #fff;
    color: var(--text-primary);
    border-top-left-radius: 0;
    box-shadow: 0 1px 1px rgba(0,0,0,0.06);
}

.chat-bubble.outgoing {
    background: #d9fdd3;
    color: var(--text-primary);
    border-top-right-radius: 0;
    box-shadow: 0 1px 1px rgba(0,0,0,0.06);
}

.bubble-sender {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--primary);
    margin-bottom: 2px;
}

.bubble-content {
    padding: 0 4px;
}

.bubble-meta {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 3px;
    margin-top: 1px;
    padding: 0 2px;
}

.bubble-time {
    font-size: 0.65rem;
    color: #667781;
}

.tick-mark {
    font-size: 0.75rem;
    color: #53bdeb;
}

.media-indicator {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 0;
    color: #667781;
}

.media-indicator i {
    font-size: 1rem;
}

.chat-bubble.outgoing .media-indicator {
    color: #667781;
}

.media-label {
    font-size: 0.82rem;
    font-style: italic;
}

.media-caption {
    padding: 2px 0 0;
    font-size: 0.875rem;
}

/* Wraps the input row plus the error / preview / recording strips that stack
   above it. .chat-main is a flex column whose .chat-messages takes the slack, so
   this must not grow. */
.composer {
    flex-shrink: 0;
    border-top: 1px solid var(--border-color);
    background: #f0f2f5;
}

.chat-input-area {
    padding: 10px 16px;
    display: flex;
    gap: 10px;
    align-items: center;
    background: #f0f2f5;
}

.chat-input-area .form-control {
    flex: 1;
    border-radius: 20px;
    padding: 8px 16px;
    font-size: 0.875rem;
    border: 1px solid var(--border-color);
    background: #fff;
}

.btn-send {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: var(--primary);
    color: #fff;
    border: none;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1rem;
    cursor: pointer;
    transition: background 0.15s ease;
    flex-shrink: 0;
}

.btn-send:hover {
    background: var(--primary-hover);
}

.btn-send:disabled,
.btn-mic:disabled {
    opacity: 0.6;
    cursor: default;
}

.btn-attach,
.btn-mic {
    width: 38px;
    height: 38px;
    border-radius: 50%;
    border: none;
    background: transparent;
    color: #54656f;
    font-size: 1.1rem;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.btn-attach:hover,
.btn-mic:hover {
    background: rgba(0, 0, 0, 0.06);
    color: var(--primary);
}

/* Recording is a mode, not a moment — the button has to look latched. */
.btn-mic.recording {
    background: #e74c3c;
    color: #fff;
}

.composer-attach .dropdown-menu {
    font-size: 0.875rem;
    min-width: 190px;
}

.composer-error {
    margin: 0;
    padding: 8px 16px;
    background: #fdecea;
    color: #a32b21;
    font-size: 0.82rem;
    border-bottom: 1px solid #f5c6c2;
}

.composer-preview,
.composer-recording {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 16px;
    background: #e9edf0;
    border-bottom: 1px solid var(--border-color);
}

.composer-preview-thumb {
    width: 46px;
    height: 46px;
    border-radius: 6px;
    background: #fff;
    border: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    flex-shrink: 0;
    color: var(--primary);
    font-size: 1.3rem;
}

.composer-preview-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.composer-preview-info {
    flex: 1;
    min-width: 0;
}

.composer-preview-name {
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.composer-preview-meta {
    font-size: 0.75rem;
    color: var(--text-secondary);
}

.composer-preview-remove {
    color: var(--text-secondary);
    text-decoration: none;
    flex-shrink: 0;
}

.composer-progress {
    height: 4px;
    border-radius: 2px;
    background: rgba(0, 0, 0, 0.1);
    margin-top: 6px;
    overflow: hidden;
}

.composer-progress-bar {
    height: 100%;
    width: 0;
    background: var(--primary);
    transition: width 0.15s linear;
}

.composer-recording .rec-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: #e74c3c;
    flex-shrink: 0;
    animation: rec-pulse 1.2s ease-in-out infinite;
}

.composer-recording .rec-label {
    font-size: 0.85rem;
    color: var(--text-primary);
}

.composer-recording .rec-timer {
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
    color: var(--text-secondary);
    flex: 1;
}

.composer-recording .rec-cancel {
    color: #e74c3c;
    text-decoration: none;
}

@keyframes rec-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.25; }
}

.chat-empty {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-secondary);
    background: #f0f2f5;
}

.chat-empty i {
    font-size: 3rem;
    margin-bottom: 16px;
    opacity: 0.3;
}

/* ===== The chatbot test conversation (#34) =====
 *
 * The tenant talking to their own bot, in a modal. Reuses .msg-row /
 * .chat-bubble / .bubble-content from the real conversation view on purpose: the
 * point of the test is "what will my customer see", so it must not look like a
 * different product.
 *
 * The whole dialog is a flex column bounded by the viewport, and the transcript
 * is the only part allowed to take the slack. The first version gave the thread a
 * fixed 52vh, which is fine until you add a header, a pinned warning strip, a
 * composer and a footnote to it — on a short window (a laptop with devtools
 * open) the dialog then grew past the viewport and the header and the composer
 * were both scrolled off, leaving a chat you could not type into. Bounding the
 * dialog and letting the thread shrink is the arrangement that cannot do that. */
.test-chat-modal .modal-dialog {
    max-height: calc(100vh - 2rem);
    height: calc(100vh - 2rem);
}

.test-chat-modal .modal-content {
    max-height: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* min-height: 0 is what actually lets it shrink. A flex item's default
   min-height is auto, i.e. its content, so without this the thread refuses to
   get smaller than the transcript and pushes the composer out of the dialog —
   the exact failure this section exists to prevent. */
.test-chat-modal .modal-body {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.test-chat-header {
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
}

.test-chat-identity {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-width: 0;
}

.test-chat-avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: #25d366;
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.15rem;
    flex-shrink: 0;
}

.test-chat-header .modal-title {
    font-size: var(--fs-base);
    font-weight: var(--fw-heading);
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.test-chat-subtitle {
    font-size: var(--fs-xs);
    color: var(--text-secondary);
    display: flex;
    align-items: center;
    gap: 5px;
}

/* Green means a real customer message would be answered right now; grey means
   this is a rehearsal. It is the one fact that changes how you read every reply
   in the transcript, so it sits next to the name rather than in a banner. */
.test-chat-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    flex-shrink: 0;
}

.test-chat-dot.is-live { background: var(--success); }
.test-chat-dot.is-off  { background: #94a3b8; }

.test-chat-header-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-left: auto;
}

/* --- The pinned strip: standing facts, stated once ------------------------- */
.test-chat-pinned {
    flex-shrink: 0;
    background: var(--body-bg);
    border-bottom: 1px solid var(--border-color);
}

.test-chat-pinned:empty {
    display: none;
}

.test-chat-note {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    padding: 8px var(--space-4);
    font-size: var(--fs-xs);
    line-height: 1.45;
    border-left: 3px solid transparent;
}

.test-chat-note i { margin-top: 1px; flex-shrink: 0; }

.test-chat-note.is-warn { background: var(--warning-light); color: #92400e; border-left-color: var(--warning); }
.test-chat-note.is-info { background: var(--info-light);    color: #1e40af; border-left-color: var(--info); }
.test-chat-note.is-ok   { background: var(--success-light); color: #065f46; border-left-color: var(--success); }

/* The same note inside the transcript, where it is about one message rather than
   about the configuration. Centred and inset so it reads as an annotation on the
   conversation and not as another participant in it. */
.test-chat-note.is-inline {
    margin: var(--space-3) auto;
    max-width: 88%;
    border-radius: var(--radius);
    border-left-width: 3px;
    box-shadow: var(--shadow-sm);
}

/* Collapsed by default. The count is the part that has to be visible; the list
   of five reasons is not something to read on every open. */
.test-chat-gaps {
    font-size: var(--fs-xs);
    background: var(--warning-light);
    color: #92400e;
    border-left: 3px solid var(--warning);
}

.test-chat-gaps > summary {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 8px var(--space-4);
    cursor: pointer;
    font-weight: var(--fw-label);
    list-style: none;
}

.test-chat-gaps > summary::-webkit-details-marker { display: none; }

.test-chat-gaps > summary::after {
    content: "\F282";                       /* bi-chevron-down */
    font-family: "bootstrap-icons";
    margin-left: auto;
    font-size: 0.7rem;
    transition: transform 0.15s ease;
}

.test-chat-gaps[open] > summary::after { transform: rotate(180deg); }

.test-chat-gaps ul {
    margin: 0;
    padding: 0 var(--space-4) 10px 34px;
}

.test-chat-gaps li { margin-bottom: 5px; }
.test-chat-gaps a  { color: inherit; font-weight: var(--fw-heading); }

/* --- The transcript ------------------------------------------------------- */
.test-chat-thread {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: var(--space-4);
    background: #efeae2;
    background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23d5d0c8' fill-opacity='0.2'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
}

.test-chat-thread .msg-row { margin-bottom: var(--space-2); }

.test-chat-thread .chat-bubble {
    max-width: 74%;
    padding: 7px 10px 5px;
    box-shadow: 0 1px 1px rgba(15, 23, 42, 0.09);
}

/* WhatsApp's bubble tail. The bubble already squares off the corner nearest its
   sender; the tail is what makes it read as speech rather than as a card. */
.test-chat-thread .chat-bubble::before {
    content: "";
    position: absolute;
    top: 0;
    width: 9px;
    height: 11px;
}

.test-chat-thread .chat-bubble.incoming::before {
    left: -8px;
    background: #fff;
    clip-path: polygon(100% 0, 100% 100%, 0 0);
}

.test-chat-thread .chat-bubble.outgoing::before {
    right: -8px;
    background: #d9fdd3;
    clip-path: polygon(0 0, 0 100%, 100% 0);
}

/* Timing inside the bubble, where WhatsApp puts its timestamp. It used to be a
   separate block underneath, which read as another message and doubled the
   vertical cost of every reply. */
.test-chat-meta {
    font-size: 0.65rem;
    color: #667781;
    cursor: help;
}

/* Three dots that actually animate, rather than the word "typing…". */
.test-chat-typing {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 11px 12px;
}

.test-chat-typing span {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: #99a3ad;
    animation: test-chat-blink 1.3s infinite ease-in-out both;
}

.test-chat-typing span:nth-child(2) { animation-delay: 0.16s; }
.test-chat-typing span:nth-child(3) { animation-delay: 0.32s; }

@keyframes test-chat-blink {
    0%, 80%, 100% { opacity: 0.3; transform: scale(0.85); }
    40%           { opacity: 1;   transform: scale(1); }
}

/* --- Empty state ---------------------------------------------------------- */
.test-chat-empty {
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: var(--space-4);
}

.test-chat-empty-icon {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.75);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.5rem;
    color: #667781;
    margin-bottom: var(--space-3);
}

.test-chat-empty-title {
    font-size: var(--fs-base);
    font-weight: var(--fw-heading);
    color: var(--text-primary);
    margin: 0 0 4px;
}

.test-chat-empty-body {
    font-size: var(--fs-sm);
    color: #5b6b76;
    max-width: 380px;
    margin: 0 0 var(--space-4);
}

/* Real first messages, not placeholder prose. Each chip exercises a different
   half of the prompt — knowledge base, opening hours, booking instructions —
   which is also how a tenant discovers what is missing. */
.test-chat-suggestions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    justify-content: center;
}

.test-chat-chip {
    border: 1px solid rgba(15, 23, 42, 0.12);
    background: rgba(255, 255, 255, 0.9);
    color: var(--text-primary);
    border-radius: 20px;
    padding: 6px 14px;
    font-size: var(--fs-sm);
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
}

.test-chat-chip:hover {
    background: #fff;
    border-color: var(--primary);
    color: var(--primary);
}

/* --- Composer ------------------------------------------------------------- */
.test-chat-footer {
    flex-shrink: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
    padding: 10px var(--space-4) 8px;
    background: #f0f2f5;
}

.test-chat-composer {
    display: flex;
    align-items: center;
    gap: 10px;
}

.test-chat-composer .form-control {
    flex: 1;
    border-radius: 20px;
    padding: 9px 16px;
    border: 1px solid var(--border-color);
    background: #fff;
}

.test-chat-footnote {
    font-size: 0.65rem;
    color: var(--text-secondary);
    text-align: center;
}

@media (max-width: 575.98px) {
    .test-chat-modal .modal-dialog { max-height: 100vh; height: 100vh; margin: 0; }
    .test-chat-thread .chat-bubble { max-width: 86%; }
}

/* ===== Contacts Toolbar ===== */
.contacts-toolbar {
    background: var(--card-bg);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 12px 16px;
}

.contacts-toolbar-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}

.contacts-toolbar-left {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    flex: 1;
    min-width: 0;
}

.contacts-toolbar-right {
    display: flex;
    align-items: center;
    flex-shrink: 0;
}

.contacts-search-wrap {
    display: flex;
    align-items: center;
    gap: 8px;
    background: var(--body-bg);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 0 10px;
    min-width: 200px;
    max-width: 280px;
}

.contacts-search-wrap i {
    color: var(--text-secondary);
    font-size: 0.85rem;
    flex-shrink: 0;
}

.contacts-search-wrap .form-control {
    border: none;
    background: transparent;
    padding: 6px 0;
    box-shadow: none;
}

.contacts-search-wrap .form-control:focus {
    box-shadow: none;
}

.contacts-toolbar-left .form-select {
    max-width: 180px;
}

.sort-header {
    text-decoration: none;
    color: inherit;
    white-space: nowrap;
    display: inline-flex;
    align-items: center;
}

.sort-header:hover {
    color: var(--primary);
}

.sort-header i {
    font-size: 0.7rem;
}

@media (max-width: 767.98px) {
    .contacts-toolbar-inner {
        flex-direction: column;
        align-items: stretch;
    }
    .contacts-toolbar-right {
        justify-content: space-between;
    }
    .contacts-search-wrap {
        max-width: 100%;
        min-width: 0;
    }
    .contacts-toolbar-left .form-select {
        max-width: 100%;
    }
}

/* ===== Media in Chat Bubbles ===== */
.media-image { max-width: 300px; border-radius: 8px; overflow: hidden; margin: -4px -6px 4px; }
.media-image img { width: 100%; display: block; cursor: pointer; border-radius: 8px; }
.media-sticker { max-width: 180px; margin: -4px -6px 4px; }
.media-sticker img { width: 100%; display: block; }
.media-video { max-width: 320px; border-radius: 8px; overflow: hidden; margin: -4px -6px 4px; }
.media-video video { width: 100%; display: block; border-radius: 8px; max-height: 280px; }
.media-audio { display: flex; align-items: center; min-width: 240px; }
.media-audio audio { flex: 1; height: 36px; }
.media-audio i { font-size: 1.3rem; color: #128c7e; }
.media-voice { background: rgba(0,0,0,0.03); border-radius: 20px; padding: 6px 12px; }

.media-document {
    display: flex; align-items: center; gap: 10px;
    background: rgba(0,0,0,0.04); border-radius: 8px; padding: 10px 14px;
    text-decoration: none; color: inherit; margin: -2px -4px 4px;
    transition: background 0.15s;
}
.media-document:hover { background: rgba(0,0,0,0.08); color: inherit; }
.media-document > i { font-size: 1.6rem; color: #128c7e; flex-shrink: 0; }
.media-doc-info { display: flex; flex-direction: column; min-width: 0; }
.media-doc-name { font-size: 0.85rem; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 200px; }
.media-doc-type { font-size: 0.7rem; color: #888; text-transform: uppercase; }

.media-contacts { display: flex; flex-direction: column; gap: 6px; }
.media-contact-card {
    display: flex; align-items: center; gap: 10px;
    background: rgba(0,0,0,0.04); border-radius: 8px; padding: 10px 12px;
}
.media-contact-card > i { font-size: 1.8rem; color: #128c7e; }
.media-contact-card strong { font-size: 0.85rem; }
.media-contact-card small { color: #666; font-size: 0.78rem; }

.media-location {
    display: block; text-decoration: none; color: inherit;
    background: rgba(0,0,0,0.04); border-radius: 8px; overflow: hidden;
    margin: -2px -4px 4px;
}
.media-location:hover { color: inherit; }
.media-location > img { width: 100%; max-width: 300px; display: block; }
.media-loc-map { background: #e8f5e9; padding: 24px; text-align: center; font-size: 2.5rem; color: #e74c3c; }
.media-loc-info { padding: 8px 12px; font-size: 0.85rem; }
.media-loc-info i { color: #e74c3c; }
.media-loc-info small { color: #666; }

.media-caption { margin-top: 4px; font-size: 0.875rem; }

/* ===== WhatsApp text formatting inside bubbles ===== */
.bubble-content a {
    color: #027eb5;
    text-decoration: underline;
    word-break: break-word;
}

.chat-bubble.outgoing .bubble-content a { color: #056162; }

.bubble-content strong { font-weight: 600; }
.bubble-content del { text-decoration: line-through; opacity: 0.85; }

.wa-code,
.wa-pre {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.82rem;
    background: rgba(0, 0, 0, 0.06);
    border-radius: 4px;
}

.wa-code { padding: 1px 4px; }

.wa-pre {
    display: block;
    padding: 8px 10px;
    margin: 4px 0;
    white-space: pre-wrap;
    word-break: break-word;
}

/* WhatsApp marks a quote with a bar, not an indent-and-italic. */
.wa-quote {
    margin: 2px 0;
    padding: 2px 0 2px 10px;
    border-left: 4px solid rgba(0, 0, 0, 0.2);
    color: inherit;
    font-size: inherit;
    opacity: 0.85;
}

.wa-list {
    margin: 2px 0;
    padding-left: 22px;
}

.wa-list li { margin: 1px 0; }

.media-lightbox {
    position: fixed; top: 0; left: 0; right: 0; bottom: 0;
    background: rgba(0,0,0,0.85); z-index: 9999;
    display: flex; align-items: center; justify-content: center;
    cursor: pointer;
}
.media-lightbox img {
    max-width: 90vw; max-height: 90vh; object-fit: contain;
    border-radius: 4px; cursor: default;
}

/* ===== Alerts ===== */
/* #33 §7. `border: none` left these floating on the page with no edge, which is
   why they read as a coloured gap rather than as a notice. A left accent gives
   them one, in their own severity colour, without adding a full border that
   would compete with the cards around them. */
.alert {
    border-radius: 10px;
    font-size: 0.875rem;
    border: none;
    border-left: 4px solid currentColor;
}

/* Bootstrap 5 sets --bs-alert-color per variant, so currentColor above is
   already the right hue — except that the same colour at full strength is the
   text colour too, and a 4px bar of it is heavy. These pull it back to the
   palette's own accent. */
.alert-success { border-left-color: var(--success); }
.alert-danger  { border-left-color: var(--danger); }
.alert-warning { border-left-color: var(--warning); }
.alert-info    { border-left-color: var(--info); }
.alert-primary { border-left-color: var(--primary); }

/* #33 §9. A table wider than its container scrolls, and on a trackpad or a
   phone nothing says so — the last column is simply missing.
 *
 * Four background layers, and the trick is the attachment pair: the two shadows
 * are `scroll`, so they stay pinned to the visible edges of the box, and the two
 * white covers are `local`, so they travel with the content and slide over the
 * shadow exactly when that edge is reached. The cue therefore appears only on
 * the side there is more to see, with no scroll listener. */
.table-responsive {
    background:
        linear-gradient(to right, var(--card-bg) 30%, rgba(255, 255, 255, 0)) left / 32px 100% no-repeat,
        linear-gradient(to left, var(--card-bg) 30%, rgba(255, 255, 255, 0)) right / 32px 100% no-repeat,
        radial-gradient(farthest-side at 0 50%, rgba(15, 23, 42, 0.14), rgba(15, 23, 42, 0)) left / 12px 100% no-repeat,
        radial-gradient(farthest-side at 100% 50%, rgba(15, 23, 42, 0.14), rgba(15, 23, 42, 0)) right / 12px 100% no-repeat;
    background-attachment: local, local, scroll, scroll;
}

/* ===== Toasts =====
 *
 * One container, created by forms.js on demand and shared by the AJAX saves and
 * by the server-rendered flash messages (includes/flash.php). Positioned rather
 * than in flow, which is the whole point of #33 §7: an alert prepended into the
 * page pushed everything below it down by its own height the moment a save
 * finished, so the thing you were about to click moved. */
.wa-toast {
    box-shadow: var(--shadow-lg);
    border-radius: var(--radius);
}

.wa-toast .toast-body {
    font-size: var(--fs-sm);
}

/* ===== Empty State ===== */
.empty-state {
    text-align: center;
    padding: 60px 20px;
    color: var(--text-secondary);
}

.empty-state i {
    font-size: 3rem;
    opacity: 0.3;
}

.empty-state h5 {
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 8px;
}

.empty-state p {
    font-size: 0.875rem;
    max-width: 400px;
    margin: 0 auto 20px;
}

/* ===== Responsive ===== */
@media (max-width: 991.98px) {
    .app-sidebar {
        transform: translateX(-100%);
    }
    .app-sidebar.show {
        transform: translateX(0);
    }
    .sidebar-overlay.show {
        display: block;
    }
    .app-main {
        margin-left: 0;
    }
    .chat-sidebar {
        width: 100%;
        position: absolute;
        left: 0;
        top: 0;
        bottom: 0;
        z-index: 10;
        background: #fff;
    }
}

@media (max-width: 767.98px) {
    .app-content {
        padding: 16px;
    }
    .auth-card {
        margin: 16px;
        padding: 24px;
    }
    .chat-wrapper {
        height: calc(100vh - var(--topbar-height) - 32px);
    }
}

/* ===== Live chats: the agent's view of a handed-over conversation ===== */
.handoff-thread {
    max-height: 340px;
    overflow-y: auto;
    padding: 12px;
    background: #efeae2;
    border-radius: 8px;
    border: 1px solid var(--border-color);
}

.handoff-thread .chat-bubble { max-width: 85%; }
