/* ============================================================
   Enrichment Cards (parent browse)
   ============================================================ */

.enrichment-card {
    background: var(--color-bg);
    border-radius: 10px;
    border: 1px solid var(--color-border);
    margin-bottom: 12px;
    overflow: hidden;
    cursor: pointer;
    transition: box-shadow 0.15s, border-color 0.15s;
}
.enrichment-card:hover {
    box-shadow: 0 2px 8px rgba(107, 76, 138, 0.1);
    border-color: var(--color-primary-light);
}
.enrichment-card-registered {
}

.enrichment-card-full {
    opacity: 0.65;
}
.enrichment-card-img {
    width: 100%;
    height: 120px;
    object-fit: cover;
}
.enrichment-card-body {
    padding: 12px 14px;
}
.enrichment-card-price {
    font-size: var(--font-size-label);
    color: var(--color-text-light);
    white-space: nowrap;
    flex-shrink: 0;
}
.enrichment-card-layout {
    display: flex;
    align-items: stretch;
    gap: 12px;
}
.enrichment-card-left {
    flex: 1;
    min-width: 0;
}
.enrichment-card-price-stack {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    flex-shrink: 0;
    gap: 2px;
    white-space: nowrap;
}
.enrichment-card-public-price {
    text-decoration: line-through;
    color: var(--color-text-light);
    font-size: var(--font-size-small);
    white-space: nowrap;
}
.price-calc-hint {
    font-size: var(--font-size-label);
    color: var(--color-text-light);
    margin-top: 4px;
}
.enrichment-card-provider {
    font-size: var(--font-size-label);
    color: var(--color-text-light);
    margin: 0 0 8px;
}
.enrichment-card-meta {
    display: flex;
    gap: 12px;
    font-size: var(--font-size-label);
    color: var(--color-text-light);
    flex-wrap: wrap;
}
.badge-registered {
    display: inline-flex;
    align-items: center;
    padding: 3px 8px;
    border-radius: 12px;
    font-size: var(--font-size-small);
    font-weight: 600;
    background: var(--color-primary);
    color: #fff;
    white-space: nowrap;
}
.badge-full {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 12px;
    font-size: var(--font-size-small);
    font-weight: 600;
    background: var(--color-text-light);
    color: #fff;
    white-space: nowrap;
}
.badge-subsidy {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 3px 8px;
    border-radius: 12px;
    font-size: var(--font-size-small);
    font-weight: 600;
    background: var(--color-primary);
    color: #fff;
    white-space: nowrap;
}
.subsidy-callout {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    background: color-mix(in srgb, var(--color-accent) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--color-accent) 30%, transparent);
    border-radius: 8px;
    padding: 10px 14px;
    margin-top: 12px;
    margin-bottom: 14px;
    font-size: var(--font-size-body);
    color: var(--color-text);
    line-height: 1.4;
}

/* ============================================================
   Enrichment Image Carousel
   ============================================================ */

.enrich-carousel {
    position: relative;
    border-radius: 10px;
    overflow: hidden;
    margin-bottom: 16px;
    background: var(--color-border);
}

.enrich-carousel-inner {
    position: relative;
    overflow: hidden;
    touch-action: pan-y;
}

.enrich-carousel-track {
    display: flex;
    transition: transform 0.3s ease;
    will-change: transform;
}

.enrich-carousel-slide {
    flex: 0 0 100%;
}

.enrich-carousel-slide img {
    width: 100%;
    height: 220px;
    object-fit: cover;
    display: block;
    cursor: pointer;
}

.enrich-carousel-arrow {
    position: absolute;
    top: 92px; /* centred in 220px image: (220-36)/2 */
    z-index: 10;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: none;
    background: rgba(0, 0, 0, 0.35);
    color: #fff;
    font-size: var(--font-size-body);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.15s;
    line-height: 1;
}
.enrich-carousel-arrow:hover { background: rgba(0, 0, 0, 0.6); }
.enrich-carousel-prev { left: 10px; }
.enrich-carousel-next { right: 10px; }

@media (max-width: 768px) {
    .enrich-carousel-arrow { display: none; }
}

.enrich-carousel-dots {
    display: flex;
    justify-content: center;
    gap: 6px;
    padding: 8px 0 6px;
    background: var(--color-card);
}

.enrich-carousel-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--color-border);
    border: none;
    cursor: pointer;
    padding: 0;
    transition: background 0.15s, transform 0.15s;
}

.enrich-carousel-dot.active {
    background: var(--color-primary);
    transform: scale(1.3);
}


.enrich-carousel-single {
    width: 100%;
    max-height: 220px;
    object-fit: cover;
    border-radius: 10px;
    margin-bottom: 16px;
    cursor: pointer;
    display: block;
}

/* ============================================================
   Planner Grid (Mon-Fri weekly view)
   ============================================================ */

.planner-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 16px;
    margin-top: 12px;
}
.planner-day-column {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.planner-time-grid {
    position: relative;
    overflow: visible;
}
.planner-day-header {
    font-weight: 700;
    font-size: var(--font-size-body);
    text-align: center;
    padding: 6px 0;
    background: var(--color-primary);
    color: #fff;
    border-radius: 6px;
}
.planner-empty {
    font-size: var(--font-size-small);
    color: var(--color-text-light);
    text-align: center;
    padding: 16px 4px;
}
.planner-slot {
    border-radius: 6px;
    padding: 8px;
    font-size: var(--font-size-label);
    border: 1px dashed var(--color-border);
    background: var(--color-bg);
    cursor: pointer;
    overflow: hidden;
    transition: box-shadow 0.2s cubic-bezier(0.22, 1, 0.36, 1), transform 0.15s cubic-bezier(0.22, 1, 0.36, 1);
}
.planner-slot:hover {
    box-shadow: 0 2px 8px rgba(107, 76, 138, 0.15);
    transform: translateY(-1px);
}
.planner-slot:active {
    transform: translateY(0);
}
.planner-slot-registered {
    border: 1px solid var(--color-primary);
    background: rgba(107, 76, 138, 0.06);
}
.planner-slot-available {
    border: 1px solid rgba(107, 76, 138, 0.25);
    background: rgba(107, 76, 138, 0.02);
}
.planner-slot-available:hover {
    border-color: rgba(107, 76, 138, 0.5);
    background: rgba(107, 76, 138, 0.06);
}
.planner-slot-clash {
    border: 2px solid var(--color-danger);
    background: rgba(220, 53, 69, 0.06);
}
.planner-slot-cta {
    font-size: var(--font-size-small);
    color: var(--color-primary);
    font-weight: 600;
    margin-top: 4px;
    text-align: right;
}
.planner-slot-name {
    font-weight: 600;
    font-size: var(--font-size-label);
    margin-bottom: 2px;
    word-break: break-word;
}
.planner-slot-time {
    font-size: var(--font-size-small);
    color: var(--color-text-light);
}
.planner-slot-badge {
    font-size: var(--font-size-small);
    color: var(--color-primary);
    font-weight: 700;
    margin-top: 4px;
}
.planner-slot-clash-warn {
    font-size: var(--font-size-small);
    color: var(--color-danger);
    font-weight: 600;
    margin-top: 2px;
}

/* Planner mobile: column dividers */
@media (max-width: 768px) {
    .planner-day-column {
        border-bottom: 1px solid var(--color-border);
        padding-bottom: 12px;
    }
    .planner-day-column:last-child {
        border-bottom: none;
    }
}

/* Safe area for notched phones */
@supports (padding-top: env(safe-area-inset-top)) {
    body {
        padding-top: env(safe-area-inset-top);
        padding-bottom: env(safe-area-inset-bottom);
    }
    /* Shipped native builds use ios.contentInset:"always" - the OS already
       insets the web content below the notch, so the env() padding above would
       double it, leaving an empty band over the header. Suppress it for those
       builds (.cap-inset-always, set in supabase-client.js). Web keeps the
       padding; edge-to-edge builds (contentInset:"never") swap to
       .cap-edge-to-edge in native-init.js and keep the single, correct inset. */
    html.cap-inset-always body {
        padding-top: 0;
        padding-bottom: 0;
    }
}

/* ============================================================
   Contextual Alert Cards (Announcements page)
   ============================================================ */

.ctx-alert {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px 14px;
    border-radius: 8px;
    margin-bottom: 8px;
    font-size: var(--font-size-body);
    line-height: 1.4;
    border-left-width: 3px;
    border-left-style: solid;
}
.ctx-alert-link {
    cursor: pointer;
}
.ctx-alert-link:hover {
    filter: brightness(0.96);
}
.ctx-alert-link:active {
    filter: brightness(0.92);
}
.ctx-alert-chevron {
    font-size: var(--font-size-heading);
    flex-shrink: 0;
    margin-left: auto;
    align-self: center;
    opacity: 0.5;
}
.ctx-alert-icon {
    font-size: var(--font-size-body);
    flex-shrink: 0;
    margin-top: 1px;
}
.ctx-alert-title {
    display: block;
    font-weight: 600;
    margin-bottom: 1px;
}
.ctx-alert-urgent {
    background: #FFF4F4;
    border-left-color: var(--color-error);
    color: #8B1A1A;
}
.ctx-alert-warn {
    background: #FFFBF0;
    border-left-color: #D4A843;
    color: #7A5500;
}
.ctx-alert-info {
    background: #F4F0FA;
    border-left-color: var(--color-primary);
    color: var(--color-primary-dark);
}
.ctx-alert-success {
    background: #F0FBF4;
    border-left-color: var(--color-success);
    color: #1A5C34;
}
[data-theme="dark"] .ctx-alert-urgent { background: rgba(192,57,43,0.12); color: #F9BABA; }
[data-theme="dark"] .ctx-alert-warn   { background: rgba(212,168,67,0.12); color: #F5D78A; }
[data-theme="dark"] .ctx-alert-info   { background: rgba(107,76,138,0.15); color: var(--color-primary-light); }
[data-theme="dark"] .ctx-alert-success{ background: rgba(46,158,94,0.12); color: #90DEB0; }
[data-theme="dark"] .event-banner-upcoming .event-banner-badge { background: var(--color-primary); color: #fff; }
[data-theme="dark"] .event-banner-live .event-banner-badge     { background: rgba(192,57,43,0.2);   color: #F9BABA; }

/* ============================================================
   Micro-animations
   ============================================================ */

/* Auth page card lift-in - login, first-login, forgot-password */
@keyframes cardLiftIn {
    from { opacity: 0; transform: translateY(12px) scale(0.99); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}
.app-container > .card {
    animation: cardLiftIn 0.35s cubic-bezier(0.22, 1, 0.36, 1) both;
    animation-delay: 0.1s;
}

/* Modal overlay fade-in */
@keyframes overlayFade {
    from { opacity: 0; }
    to   { opacity: 1; }
}
.modal-overlay.show {
    animation: overlayFade 0.2s ease both;
}

/* Modal box scale-in */
@keyframes modalEnter {
    from { opacity: 0; transform: scale(0.95) translateY(8px); }
    to   { opacity: 1; transform: none; }
}
.modal-overlay.show .modal {
    animation: modalEnter 0.22s cubic-bezier(0.22, 1, 0.36, 1) both;
}

/* Alert slide-in from top */
@keyframes alertSlideIn {
    from { opacity: 0; transform: translateY(-6px); }
    to   { opacity: 1; transform: translateY(0); }
}
.alert.show {
    animation: alertSlideIn 0.2s cubic-bezier(0.22, 1, 0.36, 1) both;
}

/* Fee notes - saved indicator fade in → hold → fade out */
@keyframes feeNotesSaved {
    0%   { opacity: 0; transform: translateY(-3px); }
    15%  { opacity: 1; transform: translateY(0); }
    75%  { opacity: 1; }
    100% { opacity: 0; }
}
.fee-notes-status.show {
    animation: feeNotesSaved 1.8s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}

/* Card enter animation - list items fade in and slide up */
@keyframes cardEnter {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: translateY(0); }
}
.enrichment-card,
.announcement-card {
    animation: cardEnter 0.2s cubic-bezier(0.22, 1, 0.36, 1) both;
}
/* Stagger siblings up to 6 */
.enrichment-card:nth-child(1), .announcement-card:nth-child(1) { animation-delay: 0ms; }
.enrichment-card:nth-child(2), .announcement-card:nth-child(2) { animation-delay: 30ms; }
.enrichment-card:nth-child(3), .announcement-card:nth-child(3) { animation-delay: 60ms; }
.enrichment-card:nth-child(4), .announcement-card:nth-child(4) { animation-delay: 90ms; }
.enrichment-card:nth-child(5), .announcement-card:nth-child(5) { animation-delay: 120ms; }
.enrichment-card:nth-child(n+6), .announcement-card:nth-child(n+6) { animation-delay: 150ms; }
/* Suppress cardEnter on announcement cards restored from the cold-start snapshot
   cache (restoreAnnouncementsEarly in shared.js stamps .ann-restored on every
   card immediately after the innerHTML paint, BEFORE the next frame, so the
   animation never starts). The 10px translateY → 0 is the visible "first
   flicker (only nudges the cards)" the user reported on every iOS cold-resume
   to the dashboard. Restored cards are being shown again, not for the first
   time, so they should appear instantly. Fresh renders from the network
   revalidate path don't get the class → they still animate in normally. */
.announcement-card.ann-restored { animation: none !important; }

/* Success modal scale-in (upgrade existing successPop) */
@keyframes successPop {
    0%   { transform: scale(0.6); opacity: 0; }
    60%  { transform: scale(1.12); }
    100% { transform: scale(1); opacity: 1; }
}

/* Tab content crossfade. Only the FALLBACK for browsers without the View
   Transitions API: where VT is supported, switchTab() wraps the swap in
   document.startViewTransition() and the @view-transition root crossfade (below)
   owns the animation, so also running tabFade would double up the fade. The
   @supports test detects VT via the view-transition-name property, which ships
   together with document.startViewTransition(). */
@supports not (view-transition-name: none) {
    .tab-content.active {
        animation: tabFade 0.18s ease-out;
    }
}
@keyframes tabFade {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* ═══ Page transitions (View Transitions API) ═══

   One unified crossfade for BOTH navigation styles so the whole app feels
   continuous instead of hard-cutting:

   1. Cross-document (e.g. Messenger to Calendar, which loads messages.html to
      dashboard.html): @view-transition opts every page in (style.css is shared).
      The browser holds the outgoing page, then crossfades to the incoming page's
      first painted frame. Theme + color-scheme are set synchronously in each
      page's <head> before first paint, so both frames are already correctly
      themed and there is no light/dark flash across the fade.
   2. Same-document tab switch (e.g. Announcements to Enrichment): switchTab()
      calls document.startViewTransition(), which reuses this same root crossfade.

   Pure progressive enhancement: unsupported browsers (older iOS/Safari, Firefox)
   navigate exactly as before. The first cold load is not a navigation, so it
   does not animate.

   Deliberately NO custom ::view-transition-old/new(root) animation overrides:
   the UA default crossfade fades both root snapshots over the SAME duration with
   complementary curves, so their opacities sum to exactly 1 at every frame and
   plus-lighter blending keeps pixels that are identical in both frames (header
   logo, fixed bottom bar) perfectly steady. Two earlier custom overrides were
   removed for causing artefacts the user could see: a translateY(8px) drift on
   the new snapshot bounced the whole viewport (the snapshot includes the fixed
   bottom bar), and a 200ms-out / 260ms-in duration mismatch made the opacity sum
   dip to ~0.78 mid-fade, visibly flickering static content like the org logo.
   If a custom curve is ever reintroduced, both layers must keep EQUAL durations,
   complementary opacity curves, and zero transform. */
@view-transition {
    navigation: auto;
}

/* The global prefers-reduced-motion block targets the * selector, which does
   NOT match ::view-transition-* pseudo-elements. Disable the crossfade here too
   so reduced-motion users get an instant cut on both nav styles. */
@media (prefers-reduced-motion: reduce) {
    ::view-transition-group(*),
    ::view-transition-old(*),
    ::view-transition-new(*) {
        animation: none !important;
    }
}

/* Messenger-return splash — an instant branded loading screen (org logo on the
   current theme's bg) painted as the cross-document view-transition's incoming
   frame when returning from the messenger (see dashboard.html <head> boot script
   + admin-messenger.js _msgrAnimateAndLeave). It covers the still-bootstrapping
   dashboard so the slide animates a LIGHTWEIGHT, instant-painting frame (never the
   heavy dashboard, which would flash), then fades out when the dashboard is ready
   (admin-dashboard.js _fadeMsgrReturnSplash). Robust to old WebKit: even without
   the view-transition the splash paints on load and fades, so the return is never
   a raw flash. --splash-bg / --splash-logo are set pre-paint by the boot script. */
#msgrReturnSplash { display: none; }
html.msgr-return-splash #msgrReturnSplash {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 100000;
    background-color: var(--splash-bg, var(--color-bg));
    opacity: 1;
    transition: opacity 0.45s ease;
}
html.msgr-return-splash #msgrReturnSplash.msgr-return-splash-out { opacity: 0; }
/* Logo lives on ::before (not the div's background) so it can breathe without
   touching the bg layer. Paints the exact same first frame: the vars are set
   pre-paint in <head> and pseudo-elements render with the div. The breath is a
   deliberate sub-perceptual cue (am I hung, or loading?) that starts at scale 1
   so the messenger-exit frame and the arrival frame still match seamlessly. */
html.msgr-return-splash #msgrReturnSplash::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image: var(--splash-logo, none);
    background-repeat: no-repeat;
    background-position: center;
    background-size: 84px auto;
    animation: splashBreath 2.4s ease-in-out infinite;
}
@keyframes splashBreath {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.03); }
}
/* No progress bar under the logo: the breathing org logo alone is the loading
   cue. An indeterminate sweep line read as odd because it never completes before
   the bootstrap finishes and the splash fades, so the line just vanished mid-fill. */
/* Leaving page (messages.html slide-out): keep the splash a STATIC frame. The
   iOS nav shield freezes this page's last frame across the navigation (L221),
   and that frozen frame must byte-match the arrival splash's first frame: a
   mid-breath logo (scale 1.03) would visibly jump at the page swap. No
   reduced-motion block needed here or above: the global *, *::before, *::after
   rule already freezes these. */
html.msgr-leaving #msgrReturnSplash::before { animation: none; }

/* Messages module - recipient row reveal */
@keyframes rowFadeIn {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: translateY(0); }
}
.row-reveal {
    animation: rowFadeIn 0.2s cubic-bezier(0.22, 1, 0.36, 1) both;
}

/* Messages module - card entrances */
#tab-messages.active .card,
#tab-messages-settings.active .card {
    animation: cardEnter 0.2s cubic-bezier(0.22, 1, 0.36, 1) both;
}
#tab-messages-settings.active .card:nth-child(2) { animation-delay: 60ms; }

/* Messages module - recipient preview crossfade */
@keyframes previewFade {
    from { opacity: 0.3; }
    to   { opacity: 1; }
}
.preview-updated {
    animation: previewFade 0.18s ease-out both;
}

/* Messages module - recipient summary bar */
.msg-recipient-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 8px;
}
.msg-recipient-count {
    font-size: var(--font-size-body);
    font-weight: 500;
    color: var(--color-text);
}
.msg-excluded-note {
    font-weight: 400;
    color: var(--color-muted);
}
.msg-exclude-toggle {
    background: none;
    border: none;
    cursor: pointer;
    font-size: var(--font-size-label);
    color: var(--color-primary);
    padding: 0;
    white-space: nowrap;
    flex-shrink: 0;
}
.msg-exclude-toggle:hover { text-decoration: underline; }

/* Messages module - exclude panel */
.msg-exclude-panel {
    border: 1px solid var(--color-border);
    border-radius: 8px;
    max-height: 200px;
    overflow-y: auto;
    margin-bottom: 12px;
}
.msg-exclude-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 14px;
    cursor: pointer;
    border-bottom: 1px solid var(--color-border);
    transition: background 0.12s;
    gap: 8px;
}
.msg-exclude-row:last-child { border-bottom: none; }
.msg-exclude-row:hover { background: var(--color-surface); }
.msg-exclude-row.excluded { opacity: 0.45; }
.msg-exclude-row-name {
    font-size: var(--font-size-body);
    color: var(--color-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.msg-exclude-row.excluded .msg-exclude-row-name {
    text-decoration: line-through;
}
.msg-exclude-action {
    font-size: var(--font-size-label);
    color: var(--color-muted);
    flex-shrink: 0;
}
.msg-exclude-row:hover .msg-exclude-action { color: var(--color-primary); }
.msg-exclude-row.excluded:hover .msg-exclude-action { color: var(--color-success); }

/* Ticket issuance - mode toggle */
.issue-mode-toggle {
    display: inline-flex;
    background: var(--color-bg);
    border-radius: 20px;
    padding: 3px;
    gap: 2px;
    margin-bottom: 12px;
}
.issue-mode-btn {
    padding: 5px 14px;
    border: none;
    background: transparent;
    border-radius: 18px;
    font-size: var(--font-size-label);
    font-weight: 500;
    cursor: pointer;
    color: var(--color-text-light);
    transition: background 0.15s, color 0.15s;
}
.issue-mode-btn.active {
    background: var(--color-primary);
    color: white;
}
.issue-mode-btn:hover:not(.active) {
    background: rgba(107, 76, 138, 0.12);
    color: var(--color-primary);
}

/* Ticket issuance - search results */
.issue-search-results {
    max-height: 200px;
    overflow-y: auto;
    border: 1px solid var(--color-border);
    border-radius: 8px;
    margin-top: 4px;
}
.issue-search-results:empty { display: none; }
.issue-search-result {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 12px;
    cursor: pointer;
    font-size: var(--font-size-body);
    transition: background 0.1s;
}
.issue-search-result:hover { background: var(--color-bg); }
.issue-search-result + .issue-search-result {
    border-top: 1px solid var(--color-border);
}
.issue-search-result-add {
    color: var(--color-primary);
    font-size: var(--font-size-label);
    font-weight: 500;
    white-space: nowrap;
}

/* Ticket issuance - selected user chips */
.issue-user-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 8px;
}
.issue-user-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 8px 4px 10px;
    border-radius: 99px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    font-size: var(--font-size-label);
    color: var(--color-text);
}
.issue-chip-remove {
    background: none;
    border: none;
    cursor: pointer;
    color: var(--color-text-light);
    font-size: var(--font-size-body);
    line-height: 1;
    padding: 0 2px;
    display: flex;
    align-items: center;
    transition: color 0.15s;
}
.issue-chip-remove:hover { color: var(--color-error); }
.issue-selected-label {
    font-size: var(--font-size-label);
    font-weight: 600;
    color: var(--color-text-light);
    margin-top: 12px;
    margin-bottom: 2px;
}

/* Messages module - attach button (photo previews reuse the shared .msgr-media-* thumbnail grid) */
.msg-attach-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: var(--font-size-label);
    cursor: pointer;
    color: var(--color-muted);
    background: none;
    border: 1px dashed var(--color-border);
    border-radius: 8px;
    padding: 7px 12px;
    transition: color 0.15s, border-color 0.15s;
    margin-bottom: 12px;
    width: 100%;
    justify-content: center;
}
.msg-attach-btn:hover,
.msg-attach-btn.drag-over {
    color: var(--color-primary);
    border-color: var(--color-primary);
    background: color-mix(in srgb, var(--color-primary) 6%, transparent);
}

/* Voice note - action row, mic button, recording overlay, preview */
.msg-action-row {
    display: flex;
    gap: 8px;
    margin-bottom: 12px;
    align-items: stretch;
}
.msg-action-row .msg-attach-btn { flex: 1; margin-bottom: 0; }

.msg-voice-btn {
    width: 40px;
    min-height: 40px;
    border-radius: 50%;
    border: 1px dashed var(--color-border);
    background: none;
    color: var(--color-text-light);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    transition: all 0.2s cubic-bezier(0.22, 1, 0.36, 1);
}
.msg-voice-btn:hover {
    color: var(--color-primary);
    border-color: var(--color-primary);
    background: color-mix(in srgb, var(--color-primary) 6%, transparent);
}

.msg-voice-overlay {
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: 8px;
    padding: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 16px;
    animation: msgFadeIn 0.2s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.msg-voice-recording {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    font-size: var(--font-size-body);
    color: var(--color-text);
}
.msg-recording-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--color-danger);
    flex-shrink: 0;
    animation: msgPulse 1s ease-in-out infinite;
}
.msg-recording-label { color: var(--color-text-light); }
.msg-recording-time {
    font-variant-numeric: tabular-nums;
    font-weight: 500;
    color: var(--color-text);
    margin-left: auto;
}
.msg-voice-stop {
    width: 40px;
    height: 40px;
    border-radius: 8px;
    border: none;
    background: var(--color-danger);
    color: white;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    transition: transform 0.15s;
}
.msg-voice-stop:active { transform: scale(0.92); }

@keyframes msgPulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.3; }
}
@keyframes msgFadeIn {
    from { opacity: 0; transform: scale(0.97); }
    to { opacity: 1; transform: scale(1); }
}
@keyframes msgFadeOut {
    from { opacity: 1; transform: scale(1); }
    to   { opacity: 0; transform: scale(0.97); }
}
@keyframes vpEnter {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}
@keyframes vpExit {
    from { opacity: 1; transform: translateY(0); }
    to   { opacity: 0; transform: translateY(-4px); }
}
@keyframes vpPlayPop {
    0%   { transform: scale(0.8); }
    100% { transform: scale(1); }
}
.msg-voice-overlay.exiting {
    animation: msgFadeOut 150ms var(--ease-out) forwards;
}

.msg-voice-preview { margin-bottom: 12px; }

/* Custom voice preview player */
.msg-voice-preview-card {
    display: flex;
    align-items: center;
    gap: 10px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 10px;
    padding: 10px 12px;
}
.msg-vp-play {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: none;
    background: var(--color-primary);
    color: #fff;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    transition: opacity 0.15s;
}
.msg-vp-play:hover { opacity: 0.85; }
.msg-vp-track {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}
.msg-vp-bar {
    flex: 1;
    height: 3px;
    background: var(--color-border);
    border-radius: 2px;
    overflow: hidden;
}
.msg-vp-fill {
    height: 100%;
    width: 100%;
    background: var(--color-primary);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.1s var(--ease-out);
}
.msg-vp-time {
    font-size: var(--font-size-small);
    color: var(--color-text-light);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.msgr-voice-clear-btn {
    background: none;
    border: none;
    color: var(--color-text-light);
    cursor: pointer;
    padding: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    font-size: 16px;
    transition: color 0.15s;
}
.msgr-voice-clear-btn:hover { color: var(--color-danger); }

.msg-action-row.voice-active .msg-attach-btn {
    opacity: 0.4;
    pointer-events: none;
    cursor: not-allowed;
}

@media (max-width: 400px) {
    .msg-action-row { flex-direction: column; }
    .msg-voice-btn {
        width: 100%;
        border-radius: 8px;
        height: auto;
        padding: 7px 12px;
    }
}

/* ============================================================
   Purple Badges
   ============================================================ */

/* ============================================================
   Empty States
   ============================================================ */

.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 48px 24px;
    text-align: center;
    color: var(--color-text-light);
    gap: 12px;
}
.empty-state-icon {
    font-size: 2.4rem;
    line-height: 1;
    opacity: 0.5;
}
.empty-state-title {
    font-size: var(--font-size-body);
    font-weight: 600;
    color: var(--color-text);
    margin: 0;
}
.empty-state-body {
    font-size: var(--font-size-body);
    color: var(--color-text-light);
    max-width: 280px;
    margin: 0;
    line-height: 1.5;
}

/* ============================================================
   Keyboard Focus Visible (accessible focus ring)
   ============================================================ */

:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* ============================================================
   Brand Mark - SVG wordmark footer
   ============================================================ */

.brand-mark {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 2px;
    padding: 18px 16px 22px;
    margin-top: auto;
    text-decoration: none;
    position: relative;
    border-top: 1px solid var(--color-border);
}
.brand-mark-logo {
    height: 28px;
    width: auto;
    display: block;
    flex-shrink: 0;
}
.brand-mark-version {
    position: absolute;
    right: 16px;
    font-size: 0.58rem;
    color: var(--color-text-light);
    opacity: 0.5;
    letter-spacing: 0.04em;
}
.brand-mark-legal {
    position: absolute;
    left: 16px;
    display: flex;
    gap: 12px;
    align-items: center;
    font-size: 0.58rem;
    color: var(--color-text-light);
    opacity: 0.5;
    letter-spacing: 0.04em;
}
.brand-mark-legal a {
    color: inherit;
    text-decoration: none;
}
.brand-mark-legal a:hover {
    opacity: 1;
}
.brand-mark-text {
    display: flex;
    flex-direction: column;
    line-height: 1.25;
}
.brand-mark-canopy {
    font-size: 0.84rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    color: var(--color-canopy-brand);
}
.brand-mark-byline {
    font-size: 0.56rem;
    font-weight: 300;
    letter-spacing: 0.08em;
    color: var(--color-text-light);
}

