/* =============================================================================
   Lobster Crypto Accounting — Design tokens
   =============================================================================
   Source de vérité : docs/DESIGN_SYSTEM.md
   Ce fichier définit les CSS variables utilisées partout.
   Toute modification doit respecter les contrastes WCAG AA (§2 du DESIGN_SYSTEM).
   ============================================================================= */

:root {

    /* ---------- Brand colors ----------
       3 couleurs canoniques de la charte (logo officiel Lobster Accounting):
       primary blue + cyan + secondary peach. Toute couleur de marque ailleurs
       dans le code doit dériver de ces 3 teintes. */
    --lobster-primary:      #3693FB;
    --lobster-primary-2:    #2D7BD1;
    --lobster-primary-3:    #1F5A9E;
    --lobster-primary-50:   #EAF3FE;

    --lobster-cyan:         #6FE0FF;  /* couleur "goutte centrale" du logo */
    --lobster-cyan-100:     #E0F8FF;
    --lobster-cyan-200:     #BAEEFF;

    --lobster-secondary:    #FF8770;  /* couleur "goutte droite" du logo */
    --lobster-secondary-2:  #FFAB94;
    --lobster-secondary-50: #FFEFE9;
    --lobster-accent:       #FFD166;

    /* Brand gradient — utilisable comme bg pour bandeaux, pills, états vedettes */
    --lobster-brand-gradient: linear-gradient(135deg, #1F5A9E 0%, #3693FB 30%, #6FE0FF 60%, #FF8770 100%);

    /* ---------- Neutrals (contraste AA validé sur neutral-50) ---------- */
    --neutral-0:   #FFFFFF;
    --neutral-50:  #F8F9FB;
    --neutral-100: #F3F4F7;
    --neutral-150: #EDEFF3;
    --neutral-200: #E2E5EB;
    --neutral-300: #C9CED6;
    --neutral-400: #8C94A2;   /* Ratio 4.56:1 sur neutral-50 */
    --neutral-500: #5B6472;   /* Ratio 7.2:1  sur neutral-50 (AAA) */
    --neutral-600: #3F4652;
    --neutral-700: #374151;
    --neutral-800: #1F2937;
    --neutral-900: #111827;
    --neutral-950: #080A0C;

    /* ---------- Semantic colors (text-safe, solid, bg) ---------- */
    --semantic-success-text:  #15803D;  /* 4.62:1 */
    --semantic-success-solid: #22C55E;
    --semantic-success-bg:    #DCFCE7;

    --semantic-warning-text:  #B45309;  /* 4.59:1 */
    --semantic-warning-solid: #F59E0B;
    --semantic-warning-bg:    #FEF3C7;

    --semantic-error-text:    #B91C1C;  /* 5.94:1 */
    --semantic-error-solid:   #EF4444;
    --semantic-error-bg:      #FEE2E2;

    --semantic-info-text:     #1F5A9E;  /* 6.32:1 */
    --semantic-info-solid:    #3693FB;
    --semantic-info-bg:       #EAF3FE;

    /* ---------- Tokens sémantiques (préparation dark mode) ---------- */
    --color-bg-canvas:       var(--neutral-50);
    --color-bg-surface:      var(--neutral-0);
    --color-bg-subtle:       var(--neutral-100);
    --color-bg-emphasis:     var(--neutral-900);

    --color-text-primary:    var(--neutral-900);
    --color-text-secondary:  var(--neutral-600);
    --color-text-tertiary:   var(--neutral-500);
    --color-text-disabled:   var(--neutral-400);
    --color-text-on-dark:    var(--neutral-0);

    --color-border-subtle:   var(--neutral-150);
    --color-border-default:  var(--neutral-200);
    --color-border-strong:   var(--neutral-300);

    /* ---------- Sidebar (dark) ---------- */
    --sidebar-bg:          var(--neutral-950);
    --sidebar-bg-hover:    #1A1D21;
    --sidebar-bg-active:   #25282E;
    --sidebar-text:        var(--neutral-300);
    --sidebar-text-active: var(--neutral-0);
    --sidebar-border:      rgba(255, 255, 255, 0.06);

    /* ---------- Chart palette (8 séries max) ---------- */
    --chart-1: #3693FB;
    --chart-2: #22C55E;
    --chart-3: #FF8770;
    --chart-4: #A78BFA;
    --chart-5: #F59E0B;
    --chart-6: #34D4D1;
    --chart-7: #F472B6;
    --chart-8: #94A3B8;

    /* ---------- Fonts ---------- */
    --font-display:  "Outfit", system-ui, sans-serif;
    --font-body:     "Poppins", system-ui, sans-serif;
    --font-mono:     "JetBrains Mono", "SF Mono", "Menlo", monospace;
    --font-numeric:  "Outfit", system-ui, sans-serif;

    /* ---------- Shadows ---------- */
    --shadow-xs:  0 1px 2px 0 rgba(8, 10, 12, 0.04);
    --shadow-sm:  0 1px 3px 0 rgba(8, 10, 12, 0.06), 0 1px 2px -1px rgba(8, 10, 12, 0.04);
    --shadow-md:  0 4px 6px -1px rgba(8, 10, 12, 0.06), 0 2px 4px -2px rgba(8, 10, 12, 0.05);
    --shadow-lg:  0 10px 15px -3px rgba(8, 10, 12, 0.08), 0 4px 6px -4px rgba(8, 10, 12, 0.06);
    --shadow-xl:  0 20px 25px -5px rgba(8, 10, 12, 0.10), 0 8px 10px -6px rgba(8, 10, 12, 0.08);
    --shadow-focus-ring: 0 0 0 3px rgba(54, 147, 251, 0.24);
    --shadow-error-ring: 0 0 0 3px rgba(239, 68, 68, 0.24);

    /* ---------- Timings ---------- */
    --ease-standard:   cubic-bezier(0.4, 0, 0.2, 1);
    --ease-accelerate: cubic-bezier(0.4, 0, 1, 1);
    --ease-decelerate: cubic-bezier(0, 0, 0.2, 1);
    /* Spring : léger overshoot pour les effets "surprenants" (hover icône,
       pop de chip, rebond du logo). À utiliser avec parcimonie. */
    --ease-spring:     cubic-bezier(0.34, 1.56, 0.64, 1);

    --dur-fast:   100ms;
    --dur-medium: 200ms;
    --dur-slow:   300ms;
}

/* =============================================================================
   Dark mode (préparé v2 — tokens sémantiques inversés)
   ============================================================================= */
[data-theme="dark"] {
    --neutral-0:   #16191F;
    --neutral-50:  #0F1217;
    --neutral-100: #1C2027;
    --neutral-150: #262A32;
    --neutral-200: #2F343D;
    --neutral-300: #3B414C;
    --neutral-400: #6C7380;
    --neutral-500: #A4ABB8;
    --neutral-600: #C1C7D3;
    --neutral-700: #D6DBE3;
    --neutral-800: #E5E8ED;
    --neutral-900: #F3F4F7;
    --neutral-950: #FFFFFF;

    --semantic-success-text:  #4ADE80;
    --semantic-success-bg:    rgba(34, 197, 94, 0.12);

    --semantic-error-text:    #F87171;
    --semantic-error-bg:      rgba(239, 68, 68, 0.12);

    --semantic-warning-text:  #FBBF24;
    --semantic-warning-bg:    rgba(245, 158, 11, 0.12);

    --shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.24),
                 inset 0 0 0 1px rgba(255, 255, 255, 0.06);
    --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.32),
                 inset 0 0 0 1px rgba(255, 255, 255, 0.06);
    --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.40),
                 inset 0 0 0 1px rgba(255, 255, 255, 0.08);

    --sidebar-bg: #050609;
}

/* =============================================================================
   Global reset — no horizontal scroll, min-width safety
   ============================================================================= */
html, body {
    overflow-x: hidden;
    max-width: 100%;
}

/* =============================================================================
   Sidebar mobile — slide-in pattern
   Applied via Alpine store :class bindings that Tailwind can't statically extract.
   ============================================================================= */
aside.sidebar-mobile {
    position: fixed;
    inset-block: 0;
    left: 0;
    z-index: 50;
    transition: transform 200ms var(--ease-standard);
    transform: translateX(-100%);
}
aside.sidebar-mobile.is-open {
    transform: translateX(0);
}
@media (min-width: 1024px) {
    aside.sidebar-mobile {
        position: sticky;
        top: 0;
        height: 100vh;
        max-height: 100vh;
        align-self: flex-start;
        transform: none !important;
        overflow: hidden; /* nav inside is the scroll container */
    }
}
main, aside, header, section, article {
    min-width: 0; /* flex children can overflow without this */
}

/* =============================================================================
   RESPONSIVE DEFENSIVE LAYER — ajout 2026-05-15
   user-feedback : "à 960px (sous lg:1024), beaucoup d'endroits sont cassés".
   Ces règles posent un filet anti-overflow sans toucher aux templates.
   ============================================================================= */

/* Tables : tout <table> intercepté par un wrapper scroll auto.
   Au lieu de wrapper chaque template, on rend la table elle-même
   scrollable via display:block + overflow-x:auto. Le tbody/thead/tr
   gardent leur comportement table grâce à display:table sur >tr. */
.card > table,
.card > div > table,
main > .card table,
.table-data,
.table-clean {
    max-width: 100%;
}
.card,
main > section,
[data-settings-panel] {
    min-width: 0;
}
/* Tables larges : scroll horizontal au lieu de pousser le viewport.
   On wrap PAR JS si pas déjà fait, mais d'abord on garantit via CSS
   que la table ne sort jamais de son conteneur en termes de layout. */
.card {
    overflow-x: clip;  /* le card ne pousse jamais le viewport */
}

/* Tables qui ont besoin de scroll horizontal : pose explicitement .table-scroll
   sur le parent direct.
   user-feedback 2026-05-15 : la règle `:has(> table)` rendait TOUS les
   cards-avec-table scrollables horizontalement, ce qui contredisait
   l'overflow:hidden explicite du template (settings/currencies).
   On garde le comportement automatique uniquement quand le card N'A PAS
   .overflow-hidden — sinon le template décide. */
.table-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}
.card:has(> table):not(.overflow-hidden):not([class*="overflow-x-"]),
.card:has(> div > table.table-data):not(.overflow-hidden):not([class*="overflow-x-"]),
.card:has(> div > table.table-clean):not(.overflow-hidden):not([class*="overflow-x-"]) {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* À <1024px, les grilles "lg:grid-cols-N" passent à 1 col par Tailwind.
   On force min-w-0 sur tous les enfants directs des grids/flex
   pour empêcher leurs contenus d'élargir le parent. */
@media (max-width: 1023.98px) {
    .grid > *,
    .flex > * {
        min-width: 0;
    }
    /* KPI / cards trop larges deviennent défilables verticalement plutôt
       que d'élargir horizontalement. */
    .card {
        max-width: 100%;
    }
    /* Boutons d'actions dans les headers de page : wrap au lieu de
       déborder. Pattern type : flex justify-between dans h1 row. */
    main > section > .flex,
    main > div > .flex,
    main > .flex {
        flex-wrap: wrap;
    }
    /* Header app : à <md (768px) on cache "Importer" + email user déjà,
       mais entre 768 et 1024 il y a parfois du débordement à droite.
       On laisse le flex se compacter. */
    header > div {
        min-width: 0;
    }
    /* Tables : sur petit écran, taille auto plutôt que fixed pour mieux
       absorber la largeur disponible. (Override le table-layout:fixed
       inline qu'on trouve sur transactions.html, dashboard.html, etc.) */
    table[style*="table-layout: fixed"],
    table.table-data {
        table-layout: auto !important;
    }
    /* Cellules tableau : pas d'élément qui force min-width supérieur
       à viewport. Garde-fou contre les chips/inputs intra-table. */
    td > .input,
    td input,
    td select {
        min-width: 0 !important;
        max-width: 100%;
    }
    /* Long texte dans <td> : permettre le wrap (par défaut white-space
       est normal, mais beaucoup de cellules ont whitespace-nowrap). */
    td.allow-wrap,
    td.wrap {
        white-space: normal !important;
    }
    /* Boutons primaires en headers de page : leur container flex doit
       wrapper sur lignes multiples au lieu de déborder. */
    h1 + p + .flex,
    h1 ~ .flex {
        flex-wrap: wrap;
        gap: 0.5rem;
    }
}

/* À <768px : règles plus agressives — single column partout. */
@media (max-width: 767.98px) {
    /* Forms grids en 1 col forcé même si template dit md:grid-cols-2 */
    form .grid.md\:grid-cols-2,
    form .grid.md\:grid-cols-3,
    form .grid.md\:grid-cols-4 {
        grid-template-columns: 1fr;
    }
    /* Padding main réduit pour gagner de l'espace */
    main {
        padding-left: 0.75rem !important;
        padding-right: 0.75rem !important;
    }
    /* Cards padding réduit */
    .card {
        padding: 1rem !important;
    }
    /* H1 plus petit pour pas déborder */
    h1, .text-h1 {
        font-size: 1.5rem !important;
        line-height: 2rem !important;
    }
}

/* Drawers / drop-downs : limiter la largeur à viewport-padding. */
[role="menu"],
.absolute.right-0,
.absolute.left-0 {
    max-width: calc(100vw - 1rem);
}

/* Inputs inside form-field never overflow */
.form-field input,
.form-field select,
.form-field textarea,
.input {
    max-width: 100%;
}

/* =============================================================================
   RESPONSIVE DEFENSIVE LAYER — pass 2 (Playwright audit 2026-05-15)
   Patterns A et B trouvés sur invoices, counterparties, dashboard, imports, assets.
   ============================================================================= */

/* Pattern A : `.truncate` ne tronque rien sur un élément INLINE (span par
   défaut). Tailwind v3 `.truncate` = overflow:hidden + text-overflow:ellipsis
   + white-space:nowrap mais SANS display:block → si appliqué à un <span>
   inline, le span s'étend à la largeur du texte et déborde le parent.
   On force display:block (compatible avec flex children, table cells)
   et max-width:100% pour que le parent contraigne.
   NOTE : on EXCLUT les éléments aussi marqués .hidden (Tailwind display:none)
   pour pas réafficher accidentellement le user.email à 960px (regression
   trouvée 2026-05-15 par audit visuel). */
.truncate:not(.hidden) {
    display: block;
    max-width: 100%;
    min-width: 0;
}
/* En plus, le parent flex/td doit avoir min-width:0 pour que les enfants
   block puissent rétrécir. La plupart des templates ont déjà min-w-0
   mais certains oublient — défilet global. */
.flex > .truncate,
td > .truncate,
th > .truncate,
[class*="flex-1"] > .truncate,
.flex > div > .truncate,
td > div > .truncate,
th > div > .truncate {
    min-width: 0;
}
.flex:has(> .truncate),
td:has(> .truncate),
th:has(> .truncate),
[class*="flex-1"]:has(> .truncate),
.flex.flex-col:has(.truncate),
.flex:has(.truncate) {
    min-width: 0;
}
td .truncate,
th .truncate {
    min-width: 0;
}

/* Pattern B : `td.whitespace-nowrap` sans largeur contrainte
   → la cellule s'étend pour contenir le texte → débordement.
   À <1024px on autorise le wrap natural sauf si la cellule a la classe
   explicite `.force-nowrap` (opt-in). */
@media (max-width: 1023.98px) {
    td.whitespace-nowrap:not(.force-nowrap),
    th.whitespace-nowrap:not(.force-nowrap) {
        white-space: normal !important;
        word-break: break-word;
    }
}

/* Pattern C : KPI grids 4-cols qui se serrent à md (768).
   On force 2 cols à md, 4 cols seulement à lg. */
@media (min-width: 768px) and (max-width: 1023.98px) {
    .kpi-row.md\:grid-cols-4,
    .grid.grid-cols-2.md\:grid-cols-4,
    .grid.md\:grid-cols-4 {
        grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    }
    /* Pattern D : forms `md:grid-cols-3` étriqués à 768-1023.
       Décaler à 2 cols dans cette plage. Le templates qui veulent
       VRAIMENT 3 cols à md utilisent `lg:grid-cols-3` ou ajoutent
       la classe opt-in `.grid-keep-3` (non implémentée — pas besoin
       pour l'instant). */
    form .grid.md\:grid-cols-3,
    .card .grid.md\:grid-cols-3,
    .grid.md\:grid-cols-3:not(.grid-keep-3) {
        grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    }
}

/* Pattern E : filenames / hashes / addresses dans les cellules.
   Toute cellule font-mono (= hashes/addresses) doit clipper. */
td.font-mono,
td .font-mono,
.font-mono.truncate {
    max-width: 18rem;
}
@media (max-width: 767.98px) {
    td.font-mono,
    td .font-mono {
        max-width: 8rem;
    }
}

/* Pattern F : settings menu plein-écran sous lg.
   La grille `lg:grid-cols-4` du /app/settings rend la sidebar fullwidth à
   <1024. On lui pose un padding-bottom pour pas coller au contenu, ET
   on lui pose un max-height + scroll quand il est seul en haut. */
@media (max-width: 1023.98px) {
    aside.col-span-1 nav[aria-label="Navigation Paramètres"] {
        display: flex;
        flex-wrap: wrap;
        gap: 0.25rem;
    }
    aside.col-span-1 nav[aria-label="Navigation Paramètres"] > div {
        flex: 1 1 auto;
        border-top: none !important;
        padding-top: 0 !important;
        margin-top: 0 !important;
    }
    /* Pour les boutons/links de tabs, ils deviennent des chips compacts */
    aside.col-span-1 nav[aria-label="Navigation Paramètres"] button,
    aside.col-span-1 nav[aria-label="Navigation Paramètres"] a {
        flex: 0 1 auto;
        padding: 0.5rem 0.875rem !important;
        font-size: 0.8125rem !important;
    }
}

/* =============================================================================
   Hidden scrollbar — for sidebar nav, dropdowns, etc. where scroll must be
   possible but the scrollbar UI is undesired.
   ============================================================================= */
.scrollbar-hide {
    scrollbar-width: none;       /* Firefox */
    -ms-overflow-style: none;    /* Edge legacy / IE */
}
.scrollbar-hide::-webkit-scrollbar {
    display: none;               /* Chrome / Safari / new Edge */
}
/* Safeguard tables, code, hash: ne peuvent pas pousser le viewport */
pre, code, .hash {
    max-width: 100%;
    overflow-wrap: break-word;
}

/* =============================================================================
   Form loading state — via HTMX htmx-request class on submitting form
   OR Alpine :class="{'is-submitting': loading}"
   ============================================================================= */
form.htmx-request button[type="submit"],
form.is-submitting button[type="submit"] {
    opacity: 0.7;
    cursor: progress;
    pointer-events: none;
}
/* Note : l'ancien spinner ::after est désactivé. Les boutons qui contiennent
   un <span class="lobster-loader"> auront l'animation du logo à la place.
   Pour les boutons sans loader, on garde une opacité réduite + cursor progress. */
form.htmx-request button[type="submit"]:not(:has(.lobster-loader))::after,
form.is-submitting button[type="submit"]:not(:has(.lobster-loader))::after {
    content: "";
    display: inline-block;
    width: 12px;
    height: 12px;
    margin-left: 8px;
    border: 2px solid currentColor;
    border-right-color: #6FE0FF;
    border-radius: 50%;
    animation: lobster-spin 0.7s linear infinite;
    vertical-align: middle;
}
@keyframes lobster-spin {
    to { transform: rotate(360deg); }
}

/* Note 2026-05-20 : #lobster-progress (top bar) supprime. Cf. templates/base.html.
   Le keyframe lobster-progress-shimmer reste ici car reutilise par
   templates/components/_progress_panel.html (per-file progress dans imports). */
@keyframes lobster-progress-shimmer {
    from { background-position: 100% 0; }
    to   { background-position: -100% 0; }
}

/* =============================================================================
   Lobster loader — animation officielle basée sur le logo (3 capsules qui
   bouncent verticalement comme un equalizer). À utiliser pour TOUTE action
   longue : auth submit, imports, FEC export, audit, recategorization, etc.

   Markup canonique :
     <span class="lobster-loader" role="status" aria-label="Chargement">
       <span></span><span></span><span></span>
     </span>
   ============================================================================= */
.lobster-loader {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    height: 28px;
    width: 26px;
    vertical-align: middle;
}
.lobster-loader > span {
    display: block;
    width: 6px;
    border-radius: 3px;
    transform-origin: center;
    animation: lobster-bar-bounce 1.1s ease-in-out infinite;
}
.lobster-loader > span:nth-child(1) {
    height: 60%;
    background: linear-gradient(180deg, #6FE0FF 0%, #3693FB 100%);
    animation-delay: 0s;
}
.lobster-loader > span:nth-child(2) {
    height: 100%;
    background: linear-gradient(180deg, #3693FB 0%, #1F5A9E 100%);
    animation-delay: 0.18s;
}
.lobster-loader > span:nth-child(3) {
    height: 60%;
    background: linear-gradient(180deg, #FFAB94 0%, #FF8770 100%);
    animation-delay: 0.36s;
}
@keyframes lobster-bar-bounce {
    0%, 100% { transform: scaleY(0.45); opacity: 0.65; }
    50%      { transform: scaleY(1);    opacity: 1; }
}
/* Tailles préréglées */
.lobster-loader--sm { height: 18px; width: 18px; gap: 2px; }
.lobster-loader--sm > span { width: 4px; border-radius: 2px; }
.lobster-loader--md { height: 32px; width: 32px; gap: 4px; }
.lobster-loader--md > span { width: 7px; border-radius: 3px; }
.lobster-loader--lg { height: 56px; width: 52px; gap: 6px; }
.lobster-loader--lg > span { width: 12px; border-radius: 6px; }

/* Animation barre de progression indéterminée (loader graph flows). */
@keyframes lobster-loader-bar {
    0%   { transform: translateX(-100%); }
    50%  { transform: translateX(20%); }
    100% { transform: translateX(100%); }
}

/* Helper : intégré dans un bouton, n'apparait qu'en état submitting */
button .lobster-loader,
.btn .lobster-loader { display: none; }
form.is-submitting button .lobster-loader,
form.htmx-request button .lobster-loader,
.btn.is-loading .lobster-loader { display: inline-flex; }
form.is-submitting button > :not(.lobster-loader),
form.htmx-request button > :not(.lobster-loader) {
    visibility: hidden;
    margin-right: -8px;  /* avoid double space */
}
form.is-submitting button,
form.htmx-request button { position: relative; cursor: progress; }
form.is-submitting button .lobster-loader,
form.htmx-request button .lobster-loader {
    position: absolute; inset: 0;
    margin: auto;
    visibility: visible !important;
}

@media (prefers-reduced-motion: reduce) {
    .lobster-loader > span {
        animation: none !important;
        opacity: 0.85;
    }
}

/* =============================================================================
   Lobster busy overlay — feedback global pour les actions backend longues
   (bulk delete wallets, sync API, generate FEC, etc.). Une SEULE indicateur a
   la fois. Declenche via data-busy-overlay="Texte..." sur form ou button.
   Cf. templates/base.html.
   ============================================================================= */
#lobster-busy {
    position: fixed;
    inset: 0;
    z-index: 9998;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: auto;
}
#lobster-busy.hidden { display: none; }
.lobster-busy-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, 0.45);
    backdrop-filter: blur(2px);
}
.lobster-busy-card {
    position: relative;
    background: #fff;
    border-radius: 16px;
    padding: 28px 36px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    box-shadow: 0 24px 60px rgba(15, 23, 42, 0.18);
    min-width: 260px;
    max-width: 90vw;
}
.lobster-busy-card p { margin: 0; text-align: center; }


/* =============================================================================
   Numeric helpers — tabular-nums + financial rendering
   ============================================================================= */
.money,
.hash,
.tabular {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1, "cv11" 1;
}
.hash {
    font-family: var(--font-mono);
    letter-spacing: -0.01em;
}

/* =============================================================================
   Chip group — reusable across filters, presets, tabs
   ============================================================================= */
.chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border-radius: 8px;
    font-size: 12px;
    font-weight: 500;
    color: var(--neutral-700);
    background: var(--neutral-100);
    border: 1px solid transparent;
    cursor: pointer;
    user-select: none;
    transition: background-color var(--dur-medium) var(--ease-standard),
                color var(--dur-medium) var(--ease-standard),
                border-color var(--dur-medium) var(--ease-standard),
                transform var(--dur-fast) var(--ease-standard);
    white-space: nowrap;
}
.chip:hover {
    background: var(--neutral-150);
    color: var(--neutral-900);
}
.chip:active {
    transform: scale(0.97);
}
/* État actif des chips : couvre aria-pressed (toggle button) ET aria-checked
   (radio chip, ex. presets fiscal). Style plus marqué pour bonne lisibilité
   user-feedback 2026-05-11 : "il faudrait que le bouton reste cliqué". */
.chip[aria-pressed="true"],
.chip[aria-checked="true"],
.chip.chip-active {
    background: var(--lobster-primary-1, #3693FB);
    color: #ffffff;
    border-color: var(--lobster-primary-1, #3693FB);
    font-weight: 600;
    box-shadow: 0 1px 3px rgba(54, 147, 251, 0.25);
}
.chip[aria-pressed="true"]:hover,
.chip[aria-checked="true"]:hover,
.chip.chip-active:hover {
    background: var(--lobster-primary-2, #1e7ce8);
    color: #ffffff;
}
.chip-group {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 6px;
}

/* =============================================================================
   Focus ring — unified via :focus-visible
   ============================================================================= */
:focus-visible {
    outline: none;
    box-shadow: var(--shadow-focus-ring);
    border-radius: inherit;
}
button:focus-visible,
a:focus-visible,
[role="button"]:focus-visible {
    box-shadow: var(--shadow-focus-ring);
}

/* =============================================================================
   Reduced motion — respect user OS preference
   ============================================================================= */
@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;
    }
}

/* =============================================================================
   Button press feedback
   ============================================================================= */
.btn:active,
button:not(:disabled):active {
    transform: translateY(0.5px);
}

/* =============================================================================
   Micro-interactions — entrées de page, hover, stagger (2026-05-20)
   Donne au logiciel une sensation "soignée". Le bloc prefers-reduced-motion
   plus haut neutralise automatiquement TOUT ce qui suit pour les users
   sensibles au mouvement (animation-duration → 0.001ms).
   ============================================================================= */

/* --- Entrée de page : à chaque navigation hx-boost le contenu de <main>
   est rejoué avec un slide-up + fade. La classe .lobster-page-swap est posée
   sur #main-content par le hook htmx:afterSwap (base.html). --- */
@keyframes lobster-page-enter {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: translateY(0); }
}
/* fill-mode `backwards` (et NON `both`) : applique l'état initial pendant le
   delay, mais NE LAISSE PAS de transform résiduel après la fin. Critique :
   `both` laissait `transform: translateY(0)` à demeure → un ancêtre
   transformé devient le bloc englobant des descendants `position: fixed` →
   tous les drawers/modals enfants de <main> se retrouvaient mal ancrés
   (panneau hors écran, hauteur 0). Incident 2026-05-20 drawer "Lier".
   `.lobster-no-anim` : opt-out explicite pour un conteneur qui héberge un
   élément fixed et ne doit jamais porter de transform. */
.lobster-page-swap > *:not(.lobster-main-halo):not(.lobster-no-anim) {
    animation: lobster-page-enter 300ms var(--ease-decelerate) backwards;
}

/* --- Sidebar link : hover = glissement latéral léger + l'icône avance.
   active = même glissement mais légèrement enfoncé. --- */
aside nav .sidebar-link {
    transition: background-color 120ms var(--ease-standard),
                color 120ms var(--ease-standard),
                transform 140ms var(--ease-standard);
}
aside nav .sidebar-link:hover  { transform: translateX(3px); }
aside nav .sidebar-link:active { transform: translateX(3px) scale(0.98); }
aside nav .sidebar-link svg {
    transition: opacity 120ms var(--ease-standard),
                transform 200ms var(--ease-spring);
}
aside nav .sidebar-link:hover svg { transform: scale(1.18) rotate(-4deg); }

/* --- Indicateur de lien actif : la barre verticale pousse depuis le centre. --- */
aside nav .sidebar-link-active::before {
    transform-origin: center;
    animation: lobster-indicator-in 280ms var(--ease-decelerate);
}
@keyframes lobster-indicator-in {
    from { transform: scaleY(0); opacity: 0; }
    to   { transform: scaleY(1); opacity: 1; }
}

/* --- Stagger : conteneur opt-in (classe .lobster-stagger sur un grid/flex).
   Les enfants directs montent en cascade. Idéal pour les grilles de cartes
   (wallets, KPI dashboard, etc.). --- */
@keyframes lobster-rise-in {
    from { opacity: 0; transform: translateY(16px); }
    to   { opacity: 1; transform: translateY(0); }
}
.lobster-stagger > * {
    /* `backwards` (pas `both`) : laisse le transform se nettoyer après la fin,
       sinon casse les position:fixed descendants. Cf. fix drawer 2026-05-20. */
    animation: lobster-rise-in 420ms var(--ease-decelerate) backwards;
}
.lobster-stagger > *:nth-child(1)    { animation-delay: 30ms; }
.lobster-stagger > *:nth-child(2)    { animation-delay: 70ms; }
.lobster-stagger > *:nth-child(3)    { animation-delay: 110ms; }
.lobster-stagger > *:nth-child(4)    { animation-delay: 150ms; }
.lobster-stagger > *:nth-child(5)    { animation-delay: 190ms; }
.lobster-stagger > *:nth-child(6)    { animation-delay: 230ms; }
.lobster-stagger > *:nth-child(7)    { animation-delay: 270ms; }
.lobster-stagger > *:nth-child(8)    { animation-delay: 310ms; }
.lobster-stagger > *:nth-child(n+9)  { animation-delay: 340ms; }

/* --- CTA pleins : hover = soulèvement + ombre portée, press = enfoncé net. --- */
.btn-primary, .btn-danger, .btn-error, .btn-success {
    transition: background-color var(--dur-medium) var(--ease-standard),
                box-shadow var(--dur-medium) var(--ease-standard),
                transform var(--dur-fast) var(--ease-standard);
}
.btn-primary:not(:disabled):hover,
.btn-danger:not(:disabled):hover,
.btn-error:not(:disabled):hover,
.btn-success:not(:disabled):hover {
    transform: translateY(-1.5px);
    box-shadow: 0 4px 14px rgba(15, 23, 42, 0.16);
}
.btn:not(:disabled):active { transform: translateY(1px) scale(0.985); }

/* --- Logo mark : petit rebond joueur au survol du brand (sidebar + header). --- */
.header-brand-link:hover img {
    animation: lobster-logo-pop 420ms var(--ease-spring);
}
@keyframes lobster-logo-pop {
    0%   { transform: scale(1) rotate(0); }
    40%  { transform: scale(1.20) rotate(-8deg); }
    100% { transform: scale(1.10) rotate(0); }
}

/* --- Chip toggle : petit pop élastique quand on l'active. --- */
.chip[aria-pressed="true"],
.chip[aria-checked="true"],
.chip.chip-active {
    animation: lobster-chip-pop 240ms var(--ease-spring);
}
@keyframes lobster-chip-pop {
    0%   { transform: scale(0.90); }
    100% { transform: scale(1); }
}

/* --- Cartes : le hover existant (.card-hover) gagne une transition plus
   douce + l'ombre passe en spring. Les <tr> de tableau réagissent au survol. --- */
main table tbody tr {
    transition: background-color 110ms var(--ease-standard);
}

/* --- Drawer / modal : entrée glissée depuis la droite (pattern "Paramètres").
   À utiliser sur tout panneau latéral via la classe .lobster-slide-in-right. --- */
@keyframes lobster-slide-in-right {
    from { opacity: 0; transform: translateX(24px); }
    to   { opacity: 1; transform: translateX(0); }
}
.lobster-slide-in-right {
    animation: lobster-slide-in-right 280ms var(--ease-decelerate) both;
}

/* --- Busy overlay : la carte entre avec un petit zoom. --- */
.lobster-busy-card {
    animation: lobster-busy-pop 240ms var(--ease-spring) both;
}
@keyframes lobster-busy-pop {
    from { opacity: 0; transform: scale(0.92); }
    to   { opacity: 1; transform: scale(1); }
}

/* =============================================================================
   Lobster Hero — bandeau killer feature en tête de page (2026-05-20).
   Pattern unifié sur Transactions / Audit / Justificatifs / Lettrage /
   Fiscalité / Plan comptable. Variantes de teinte via --lobster-hero-*.
   Usage : <section class="lobster-hero lobster-hero--primary"> + .__icon,
   .__cta, .__blob.
   ============================================================================= */
.lobster-hero {
    position: relative;
    padding: 22px 24px;
    border-radius: 16px;
    border: 1px solid color-mix(in srgb, var(--lobster-hero-accent, #3693FB) 18%, transparent);
    background: linear-gradient(135deg,
        color-mix(in srgb, var(--lobster-hero-accent, #3693FB) 8%, #fff) 0%,
        #fff 55%,
        color-mix(in srgb, var(--lobster-hero-accent2, #6FE0FF) 8%, #fff) 100%);
    overflow: hidden;
}
.lobster-hero__blob {
    position: absolute;
    top: -60px;
    right: -60px;
    width: 220px;
    height: 220px;
    background: radial-gradient(circle,
        color-mix(in srgb, var(--lobster-hero-accent, #3693FB) 22%, transparent) 0%,
        transparent 70%);
    border-radius: 50%;
    filter: blur(8px);
    pointer-events: none;
}
.lobster-hero__icon {
    flex-shrink: 0;
    width: 56px;
    height: 56px;
    border-radius: 14px;
    background: linear-gradient(135deg,
        var(--lobster-hero-accent, var(--lobster-primary, #3693FB)) 0%,
        var(--lobster-hero-accent2, #6FE0FF) 100%);
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 6px 20px color-mix(in srgb, var(--lobster-hero-accent, #3693FB) 35%, transparent);
}
.lobster-hero__cta {
    box-shadow: 0 6px 20px color-mix(in srgb, var(--lobster-hero-accent, #3693FB) 38%, transparent);
}
.lobster-hero__cta:not(:disabled):hover {
    box-shadow: 0 10px 28px color-mix(in srgb, var(--lobster-hero-accent, #3693FB) 50%, transparent);
}
.lobster-hero--primary  { --lobster-hero-accent: #3693FB; --lobster-hero-accent2: #6FE0FF; }
.lobster-hero--success  { --lobster-hero-accent: #10B981; --lobster-hero-accent2: #5EEAD4; }
.lobster-hero--warning  { --lobster-hero-accent: #F59E0B; --lobster-hero-accent2: #FCD34D; }
.lobster-hero--violet   { --lobster-hero-accent: #8B5CF6; --lobster-hero-accent2: #C4B5FD; }
.lobster-hero--rose     { --lobster-hero-accent: #EC4899; --lobster-hero-accent2: #F9A8D4; }
.lobster-hero--secondary{ --lobster-hero-accent: #FF8770; --lobster-hero-accent2: #FFAB94; }
.lobster-hero--teal     { --lobster-hero-accent: #14B8A6; --lobster-hero-accent2: #99F6E4; }

/* --- Filtres rapides en chips (page Transactions, etc.) : feedback de clic
   INSTANTANÉ. Au clic, hx-boost pose .htmx-request sur le <a> → on le passe
   en bleu plein IMMÉDIATEMENT, avant même la réponse serveur. Après le swap
   de <main>, l'état serveur (.chip-filter-on) prend le relais. L'utilisateur
   voit donc TOUJOURS quel filtre est actif, et la sélection paraît instantanée.
   Incident UX 2026-05-20 : chips sans état actif visible. --- */
.chip-filter {
    cursor: pointer;
    transition: background-color 90ms var(--ease-standard),
                color 90ms var(--ease-standard),
                transform 90ms var(--ease-standard),
                box-shadow 90ms var(--ease-standard);
}
.chip-filter:active { transform: scale(0.94); }
/* État actif (rendu serveur) : ombre bleue pour le détacher nettement. */
.chip-filter-on {
    box-shadow: 0 2px 8px rgba(54, 147, 251, 0.35);
}
/* Feedback instantané pendant la requête hx-boost : bleu plein + léger zoom. */
.chip-filter.htmx-request {
    background: var(--lobster-primary, #3693FB) !important;
    color: #ffffff !important;
    border-color: var(--lobster-primary, #3693FB) !important;
    box-shadow: 0 2px 10px rgba(54, 147, 251, 0.45);
    transform: scale(1.04);
}
/* La pastille de couleur de catégorie passe en blanc quand le chip est sélectionné. */
.chip-filter.htmx-request span[style*="background"] {
    background: #ffffff !important;
}

/* =============================================================================
   Skeleton loader
   ============================================================================= */
@keyframes lobster-shimmer {
    0% { background-position: -200px 0; }
    100% { background-position: calc(200px + 100%) 0; }
}
.skeleton {
    background: linear-gradient(
        90deg,
        var(--neutral-100) 0%,
        var(--neutral-150) 50%,
        var(--neutral-100) 100%
    );
    background-size: 200px 100%;
    background-repeat: no-repeat;
    animation: lobster-shimmer 1.4s infinite linear;
    border-radius: 6px;
}

/* =============================================================================
   Sidebar link — refined spacing + active indicator
   ============================================================================= */
aside nav .sidebar-link {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 10px;
    border-radius: 8px;
    font-size: 13px;
    font-weight: 500;
    color: #C9CED6;
    transition: background-color 120ms var(--ease-standard),
                color 120ms var(--ease-standard);
    position: relative;
}
aside nav .sidebar-link:hover {
    background: #1A1D21;
    color: #FFFFFF;
}
aside nav .sidebar-link svg {
    opacity: 0.6;
    transition: opacity 120ms var(--ease-standard);
}
aside nav .sidebar-link:hover svg,
aside nav .sidebar-link-active svg {
    opacity: 1;
}
aside nav .sidebar-link-active {
    background: #25282E;
    color: #FFFFFF;
    font-weight: 600;
}
aside nav .sidebar-link-active::before {
    content: "";
    position: absolute;
    left: -3px;
    top: 8px;
    bottom: 8px;
    width: 2px;
    background: var(--lobster-primary);
    border-radius: 2px;
}

/* =============================================================================
   Card hover
   ============================================================================= */
.card-hover {
    transition: transform var(--dur-medium) var(--ease-standard),
                box-shadow var(--dur-medium) var(--ease-standard),
                border-color var(--dur-medium) var(--ease-standard);
}
.card-hover:hover {
    transform: translateY(-1px);
    box-shadow: var(--shadow-md);
    border-color: var(--neutral-200);
}

/* =============================================================================
   KPI card — label tiny, big number, optional trend + sparkline
   ============================================================================= */
.kpi-card {
    position: relative;
    padding: 1.125rem 1.25rem;
    background: #fff;
    border: 1px solid var(--neutral-150);
    border-radius: 12px;
    transition: border-color 200ms var(--ease-standard);
}
.kpi-card:hover { border-color: var(--neutral-200); }
.kpi-label {
    display: block;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--neutral-500);
    margin-bottom: 8px;
}
.kpi-value {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 28px;
    line-height: 1.1;
    color: var(--neutral-900);
    font-variant-numeric: tabular-nums;
}
.kpi-sub {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 6px;
    font-size: 12px;
    color: var(--neutral-500);
}
.kpi-trend {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 2px 6px;
    border-radius: 6px;
    font-size: 11px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.kpi-trend-up   { color: var(--semantic-success-text); background: var(--semantic-success-bg); }
.kpi-trend-down { color: var(--semantic-error-text);   background: var(--semantic-error-bg); }
.kpi-trend-flat { color: var(--neutral-500);           background: var(--neutral-100); }

/* =============================================================================
   Empty state — canonical layout
   ============================================================================= */
.empty-state {
    text-align: center;
    padding: 48px 24px;
    color: var(--neutral-500);
}
.empty-state__icon {
    width: 48px;
    height: 48px;
    margin: 0 auto 16px;
    color: var(--neutral-300);
}
.empty-state__title {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 16px;
    color: var(--neutral-900);
    margin-bottom: 6px;
}
.empty-state__body {
    font-size: 14px;
    color: var(--neutral-500);
    max-width: 420px;
    margin: 0 auto 16px;
    line-height: 1.5;
}

/* =============================================================================
   Table data (dense, sticky header)
   ============================================================================= */
.table-data {
    width: 100%;
    font-size: 13px;
    border-collapse: separate;
    border-spacing: 0;
}
.table-data thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--neutral-50);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--neutral-500);
    padding: 9px 12px;
    text-align: left;
    border-bottom: 1px solid var(--neutral-200);
}
.table-data tbody td {
    padding: 11px 12px;
    border-bottom: 1px solid var(--neutral-150);
    vertical-align: middle;
    color: var(--neutral-800);
}
.table-data tbody tr {
    transition: background-color 80ms var(--ease-standard);
}
.table-data tbody tr:hover {
    background: var(--neutral-50);
}
.table-data .numeric,
.table-data .money {
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

/* =============================================================================
   Badge — unified pills for status/category
   ============================================================================= */
.badge-lg {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 3px 8px;
    border-radius: 6px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.01em;
    line-height: 1.3;
}
.badge-neutral  { background: var(--neutral-100);            color: var(--neutral-700); }
.badge-primary  { background: var(--lobster-primary-50);     color: var(--lobster-primary-3); }
.badge-success  { background: var(--semantic-success-bg);   color: var(--semantic-success-text); }
.badge-warning  { background: var(--semantic-warning-bg);   color: var(--semantic-warning-text); }
.badge-error    { background: var(--semantic-error-bg);     color: var(--semantic-error-text); }
.badge-info     { background: var(--semantic-info-bg);      color: var(--semantic-info-text); }

/* =============================================================================
   Severity border-left accent (audit anomaly cards, alerts)
   ============================================================================= */
.sev-critical { border-left: 3px solid var(--semantic-error-solid); }
.sev-error    { border-left: 3px solid var(--semantic-error-solid); }
.sev-warning  { border-left: 3px solid var(--semantic-warning-solid); }
.sev-info     { border-left: 3px solid var(--semantic-info-solid); }
.sev-success  { border-left: 3px solid var(--semantic-success-solid); }

/* =============================================================================
   Alert strip (top of page)
   ============================================================================= */
.alert-strip {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 16px;
    border-radius: 10px;
    font-size: 13px;
    line-height: 1.4;
    margin-bottom: 20px;
    border: 1px solid transparent;
}
.alert-strip--warning { background: var(--semantic-warning-bg);  color: var(--semantic-warning-text); border-color: rgba(245,158,11,.25); }
.alert-strip--error   { background: var(--semantic-error-bg);    color: var(--semantic-error-text);   border-color: rgba(239,68,68,.25); }
.alert-strip--info    { background: var(--semantic-info-bg);     color: var(--semantic-info-text);    border-color: rgba(54,147,251,.25); }
.alert-strip--success { background: var(--semantic-success-bg);  color: var(--semantic-success-text); border-color: rgba(34,197,94,.25); }

/* =============================================================================
   Dropzone (file upload)
   ============================================================================= */
.dropzone {
    position: relative;
    border: 2px dashed var(--neutral-300);
    border-radius: 12px;
    padding: 36px 24px;
    text-align: center;
    background: var(--neutral-50);
    color: var(--neutral-500);
    transition: border-color 150ms var(--ease-standard),
                background-color 150ms var(--ease-standard);
    cursor: pointer;
}
.dropzone:hover,
.dropzone.is-dragover {
    border-color: var(--lobster-primary);
    background: var(--lobster-primary-50);
    color: var(--lobster-primary-3);
}
.dropzone input[type="file"] {
    position: absolute;
    inset: 0;
    opacity: 0;
    cursor: pointer;
}

/* =============================================================================
   Toasts
   ============================================================================= */
#lobster-toasts {
    position: fixed;
    right: 16px;
    bottom: 16px;
    z-index: 9998;
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-width: 380px;
    pointer-events: none;
}
.toast {
    pointer-events: auto;
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 14px;
    border-radius: 10px;
    background: #fff;
    border: 1px solid var(--neutral-200);
    box-shadow: var(--shadow-lg);
    font-size: 13px;
    color: var(--neutral-800);
    animation: toast-in 180ms var(--ease-decelerate);
}
.toast--success { border-left: 3px solid var(--semantic-success-solid); }
.toast--error   { border-left: 3px solid var(--semantic-error-solid); }
.toast--info    { border-left: 3px solid var(--lobster-primary); }
@keyframes toast-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* =============================================================================
   Print — FEC/reports hidden chrome, clean layout
   ============================================================================= */
@media print {
    aside, header, #lobster-toasts,
    .no-print { display: none !important; }
    main { padding: 0 !important; }
    body { background: #fff !important; }
    .card { border: 1px solid #ccc !important; box-shadow: none !important; page-break-inside: avoid; }
}

/* =============================================================================
   Chain palette — partagé par dashboard, imports, reports.
   `--c` = couleur de la blockchain ; les classes .chain-pill/.chain-avatar/
   .chain-strip/.chain-badge la consomment via color-mix.
   ============================================================================= */
.chain-eth      { --c: #627EEA; }
.chain-arb      { --c: #28A0F0; }
.chain-matic    { --c: #8247E5; }
.chain-bsc      { --c: #F0B90B; }
.chain-op       { --c: #FF0420; }
.chain-base     { --c: #0052FF; }
.chain-avax     { --c: #E84142; }
.chain-linea    { --c: #121212; }
.chain-monad    { --c: #6F2DBD; }
.chain-fantom   { --c: #1969FF; }
.chain-ftm      { --c: #1969FF; }
.chain-cronos   { --c: #002D74; }
.chain-cro      { --c: #002D74; }
.chain-unichain { --c: #FF007A; }
.chain-uni      { --c: #FF007A; }
.chain-kraken   { --c: #5741D9; }
.chain-unknown  { --c: #6B7280; }

.chain-avatar { background: color-mix(in srgb, var(--c) 15%, white); color: color-mix(in srgb, var(--c) 80%, black); border: 1px solid color-mix(in srgb, var(--c) 25%, white); }
.chain-badge  { background: color-mix(in srgb, var(--c) 12%, white); color: color-mix(in srgb, var(--c) 80%, black); }
.chain-strip  { background: linear-gradient(90deg, var(--c) 0%, color-mix(in srgb, var(--c) 70%, white) 100%); height: 4px; }

.chain-pill {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 8px 3px 6px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 500;
    background: color-mix(in srgb, var(--c) 8%, white);
    color: color-mix(in srgb, var(--c) 80%, black);
    border: 1px solid color-mix(in srgb, var(--c) 18%, white);
    max-width: 100%;
}
.chain-pill__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--c); flex-shrink: 0; }
.chain-pill__count { background: color-mix(in srgb, var(--c) 15%, white); color: color-mix(in srgb, var(--c) 85%, black); padding: 0 6px; border-radius: 999px; font-weight: 600; margin-left: 2px; }

/* ============================================================================
 * Layout halos / atmospherics
 * (extracted 2026-05-19 from templates/_layouts/app.html + landing.html)
 * ========================================================================= */

/* App layout : subtle brand halo at the top of main, echoes landing's
   chromatic atmosphere on a light background. */
.lobster-main-halo {
    position: absolute;
    inset-inline: 0;
    top: 0;
    height: 8rem;
    pointer-events: none;
    z-index: -10;
    opacity: 0.4;
    background: linear-gradient(180deg, rgba(54, 147, 251, 0.08) 0%, transparent 100%);
}

/* Landing layout : dot-grid texture + 2 slow-drifting corner orbs that tint
   the canvas without washing it out. ABSOLUTE inside main so they only
   render on the dark canvas. */
.lobster-landing-dotgrid {
    position: absolute;
    inset: 0;
    background-image: radial-gradient(circle, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
    background-size: 28px 28px;
}
.amb-orb {
    position: absolute;
    border-radius: 50%;
    filter: blur(140px);
    will-change: transform;
}
.amb-orb-1 {
    width: 700px;
    height: 700px;
    top: -150px;
    left: -250px;
    background: radial-gradient(circle, rgba(54, 147, 251, 0.28) 0%, transparent 65%);
    animation: amb-1 28s ease-in-out infinite alternate;
}
.amb-orb-2 {
    width: 600px;
    height: 600px;
    bottom: -200px;
    right: -200px;
    background: radial-gradient(circle, rgba(255, 135, 112, 0.20) 0%, transparent 65%);
    animation: amb-2 36s ease-in-out infinite alternate;
}
@keyframes amb-1 {
    from { transform: translate(0, 0); }
    to   { transform: translate(120px, 80px); }
}
@keyframes amb-2 {
    from { transform: translate(0, 0); }
    to   { transform: translate(-100px, -120px); }
}
@media (prefers-reduced-motion: reduce) {
    .amb-orb { animation: none; }
}
