/* Byggbetalning — visual refresh, phase 1 (issue #185, epic #184).

   Only linked when Features:ModernUI is on (_Layout.cshtml). Everything here
   CONSUMES the token layer in theme.css; nothing here declares a token. With the
   flag off this file is absent and the app renders exactly as before — that
   separation is the whole point of the split, so keep new tokens in theme.css and
   new rules here.

   Loads immediately after theme.css, so it wins ties against it and against
   connect.min.css/app.css/bootstrap.min.css. It still loses to the per-page
   @section Styles links (DataTables) — same caveat as theme.css, same remedy
   (higher specificity, see the table rules below).

   Design intent, in one line each:
   - Hierarchy comes from weight and colour, not from size alone.
   - Grouping comes from distance, not from rules and borders.
   - Elevation replaces 1px borders, so panels read as layers instead of a grid. */

/* ==================== Typography ==================== */

/* connect.min.css leaves h4-h6 at weight 500 and sizes them close together, which
   flattens every page heading into the same visual weight. theme.css already
   nudges h4-h6 to 600; this takes the whole scale to explicit token values. */

h1, .h1 {
    font-size: var(--bb-text-2xl);
    font-weight: 700;
    line-height: 1.2;
}

h2, .h2 {
    font-size: var(--bb-text-xl);
    font-weight: 700;
    line-height: 1.25;
}

h3, .h3 {
    font-size: var(--bb-text-lg);
    font-weight: 600;
    line-height: 1.3;
}

h4, .h4,
h5, .h5,
h6, .h6 {
    font-size: var(--bb-text-base);
    font-weight: 600;
    line-height: 1.4;
}

/* Text som var en rubrik enbart för storlekens skull och nu är ett <p> (#224) behåller
   rubrikernas typsnitt. Deklarationen ligger HÄR och inte i theme.css av en mätbar anledning:
   med ModernUI av renderas rubrikelementen inte i Montserrat utan i system-ui, och en
   font-family i theme.css hade då gett de omgjorda styckena ett typsnitt som rubrikerna
   omkring dem inte har. Den här filen länkas bara när flaggan är på, så regeln gäller exakt
   när den ska. Vikten ligger kvar i theme.css, som gäller i båda lägena. */
.bb-h-restore {
    font-family: Montserrat, sans-serif;
}

/* ==================== Label / value pairs ==================== */

/* The single highest-leverage rule in this file. When a label and its value share
   a weight and a colour the reader has to parse the line; splitting them lets the
   eye jump straight to the number. .bb-label deliberately matches the quiet
   uppercase treatment .table thead th and .detail-field-label already use, so the
   three vocabularies converge instead of competing. */

.bb-label {
    display: block;
    margin-bottom: var(--bb-space-1);
    font-size: var(--bb-text-sm);
    font-weight: 500;
    letter-spacing: .01em;
    color: var(--bb-text-muted);
}

.bb-value {
    font-size: var(--bb-text-base);
    font-weight: 600;
    color: var(--bb-text);
}

/* tabular-nums is not decoration: proportional digits stop amount columns from
   lining up, which directly costs the reader the ability to compare magnitude at
   a glance. Every money figure in the app should carry this. */
.bb-value--money {
    font-variant-numeric: tabular-nums;
    text-align: right;
}

/* ==================== Status pills ==================== */

/* The shared status vocabulary. Phase 2 (#186) renders these from
   StatusPresentation via Views/Shared/_StatusBadge.cshtml; the classes exist here
   first so the palette and the markup can land in separate, revertible PRs.

   Not built on Bootstrap's .badge: connect.min.css hardcodes .badge padding and
   the BS4-era .badge-* colours, and inheriting that would mean fighting it with
   !important in both themes. A standalone class is cheaper and theme-clean. */

.bb-status {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .2rem .55rem;
    border-radius: 999px;
    font-size: var(--bb-text-xs);
    font-weight: 600;
    line-height: 1.5;
    white-space: nowrap;
    /* Fall back to neutral rather than to transparent-on-transparent if a caller
       ever passes a tone that has no token. */
    background-color: var(--bb-status-neutral-bg);
    color: var(--bb-status-neutral-fg);
}

    .bb-status .bi {
        font-size: .9em;
        /* The icon is a redundant cue for colour-blind readers, not a decoration —
           it must not pull weight away from the word next to it. */
        opacity: .85;
    }

.bb-status--neutral {
    background-color: var(--bb-status-neutral-bg);
    color: var(--bb-status-neutral-fg);
}

.bb-status--info {
    background-color: var(--bb-status-info-bg);
    color: var(--bb-status-info-fg);
}

.bb-status--progress {
    background-color: var(--bb-status-progress-bg);
    color: var(--bb-status-progress-fg);
}

.bb-status--success {
    background-color: var(--bb-status-success-bg);
    color: var(--bb-status-success-fg);
}

.bb-status--warning {
    background-color: var(--bb-status-warning-bg);
    color: var(--bb-status-warning-fg);
}

.bb-status--danger {
    background-color: var(--bb-status-danger-bg);
    color: var(--bb-status-danger-fg);
}

/* The ÄTA rows sit inside the contract modal and are deliberately quieter than the
   contract's own status in the header. theme.css:560 does this for .badge; with the
   flag on those cells render .bb-status instead, so the rule needs a counterpart
   here — theme.css must stay unaware that .bb-status exists. */
.ata-table .bb-status {
    font-size: .7rem;
}

/* ==================== Cards and elevation ==================== */

/* theme.css already drops the card border and rounds the corners. This funnels the
   .shadow/.shadow-sm utilities the views sprinkle on cards through the token scale
   too, so a card cannot end up with Bootstrap's much harder default shadow. */

.card {
    box-shadow: var(--bb-shadow-sm);
    --bs-box-shadow: var(--bb-shadow-sm);
    --bs-box-shadow-sm: var(--bb-shadow-xs);
    --bs-box-shadow-lg: var(--bb-shadow-md);
}

.card > .card-body {
    padding: var(--bb-space-5);
}

/* ==================== Tables ==================== */

/* Amounts right-aligned with tabular figures. .text-end is what the views already
   use for money cells, so this needs no markup change. The table.dataTable prefix
   is required to outrank the DataTables sheet, which loads after this file via the
   per-page @section Styles (see the file header). */

table.dataTable td.text-end,
table.dataTable th.text-end,
.table td.text-end,
.table th.text-end {
    font-variant-numeric: tabular-nums;
}

/* Row separators carry the grid; the eye does not also need a heavy header rule. */
table.dataTable thead th,
.table thead th {
    border-bottom-width: 1px;
}

/* ==================== Sections and whitespace ==================== */

/* Doubling the gap between sections relative to the gap inside one is the cheapest
   "designed" signal available, and it lets the divider lines go away. */

.detail-section {
    margin-top: var(--bb-space-6);
}

.detail-section-title {
    margin-bottom: var(--bb-space-3);
    font-size: var(--bb-text-base);
    font-weight: 600;
}

/* The label is metadata, the value is the information. Same split as .bb-label /
   .bb-value above, applied to the detail-modal vocabulary that already exists so
   the two modals pick the refresh up without touching their markup. */

.detail-field-label {
    margin-bottom: var(--bb-space-1);
    font-size: var(--bb-text-xs);
    color: var(--bb-text-muted);
}

.detail-field-value {
    font-size: var(--bb-text-base);
    font-weight: 600;
    color: var(--bb-text);
}

.detail-field-note {
    margin-top: var(--bb-space-1);
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
}

/* The economy panel is the modal's single anchor, so its primary figure keeps the
   step up in size that theme.css gives it — only the surrounding rhythm moves onto
   the token scale. */

.detail-economy {
    padding: var(--bb-space-4) var(--bb-space-5);
}

.detail-economy-figures {
    gap: var(--bb-space-3) var(--bb-space-5);
}

/* ==================== Forms ==================== */

.form-label,
form label {
    font-size: var(--bb-text-sm);
    font-weight: 500;
    margin-bottom: var(--bb-space-1);
    color: var(--bb-text-muted);
}

/* A field's own value must stay full-strength text — only its label is quiet. */
.form-control,
.form-select {
    font-size: var(--bb-text-base);
    color: var(--bb-text);
}

/* En <select class="form-control"> ser ut som ett textfält.
   Bootstraps .form-control sätter appearance: none och tar bort operativsystemets pil, men
   lägger — till skillnad från .form-select — ingen egen pil tillbaka. Kvar blir en ruta som
   inte avslöjar att den öppnar en lista. Syns tydligast på Skapa faktura, där produktradens
   Typ och Moms står bredvid fyra riktiga textfält och blir omöjliga att skilja från dem.

   Rätt lösning i markupen vore .form-select, men klassen sitter också i den rad som klonas
   för varje ny produktrad och i flera vyer; pilen läggs därför tillbaka i stilen i stället,
   med samma SVG och samma mått som Bootstrap använder för .form-select. */
select.form-control {
    /* 2rem, inte Bootstraps 2.25rem: pilen ska rymmas även i produktradens smala
       Moms-kolumn utan att äta av värdet. */
    padding-right: 2rem;
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
    background-repeat: no-repeat;
    background-position: right .625rem center;
    background-size: 16px 12px;
}

[data-bs-theme="dark"] select.form-control {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23adb5bd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
}

/* ==================== Motion ==================== */

/* Under ~150ms a transition reads as responsiveness; past it, as animation. */

table.dataTable tbody tr,
.table tbody tr {
    transition: background-color .12s ease;
}

@media (prefers-reduced-motion: reduce) {
    table.dataTable tbody tr,
    .table tbody tr {
        transition: none;
    }
}

/* ==================== KPI-rad över listan (#188) ==================== */

/* Ett kort som bara visar en siffra är dekoration; ett kort som filtrerar listan
   under sig är navigation. Markupen är därför byggd för att i nästa steg bli en
   länk utan att stilen ändras — samma box, ny tagg. */

/* CSS-grid och inte Bootstraps .row/.col: korten måste kunna skickas ensamma i ett
   out-of-band-svar, och per-kort-kolumner hade tvingat höljet att följa med in i
   tabellraden. Auto-fit gör samma responsiva jobb som col-6 col-lg-3 gjorde. */

/* 170px och inte 190px: med fem kort (utbetalningskortet tillkom när "Betalt denna månad" delades
   i "På väg till dig" och "Utbetalt denna månad") slog 190px över redan vid ~1000px innehållsbredd
   och lade det femte kortet ensamt på en egen rad med ett tomrum bredvid sig. 170px håller ihop
   raden ned till ~900px, vilket täcker delad skärm och mindre laptops; därunder bryter den till
   3+2 som förut. Måttet är satt efter de fem etiketterna vi faktiskt har — "Utbetalt denna månad"
   är den längsta och ryms på en rad. */
.bb-kpi-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: var(--bb-space-3);
    margin-bottom: var(--bb-space-5);
}

.bb-kpi {
    display: block;
    height: 100%;
    padding: var(--bb-space-4);
    border-radius: var(--bb-radius);
    background: var(--bb-surface);
    box-shadow: var(--bb-shadow-sm);
    text-decoration: none;
    color: inherit;
}

a.bb-kpi:hover,
a.bb-kpi:focus-visible {
    box-shadow: var(--bb-shadow-md);
    text-decoration: none;
}

.bb-kpi-label {
    display: block;
    font-size: var(--bb-text-sm);
    font-weight: 500;
    color: var(--bb-text-muted);
    margin-bottom: var(--bb-space-1);
}

.bb-kpi-value {
    display: block;
    font-size: var(--bb-text-xl);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: 1.2;
}

/* Money inside a KPI card reads as a headline, not as a table column: no right
   alignment here, only the tabular figures. */
.bb-kpi .bb-value--money {
    text-align: left;
}

.bb-kpi-sub {
    display: block;
    margin-top: var(--bb-space-1);
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
}

/* Red is reserved for the one number that actually demands action. */
.bb-kpi--danger .bb-kpi-value {
    color: var(--bb-status-danger-fg);
}

/* Punktindikatorerna under svepremsan (#391 etapp 5-efterjustering).

   INGENTING på desktop: där ligger korten på en rad och det finns inget att svepa, så prickarna
   hade varit tre punkter som aldrig ändrar sig. Basen döljer dem alltså helt och mobilblocket
   nedan slår på dem — samma riktning som resten av filen, där desktop är utgångsläget.

   Höljet .bb-kpi-strip finns bara vid förstarenderingen, av samma skäl som .bb-kpi-row: det som
   swappas out-of-band är korten själva, och ett hölje i det svaret hade landat inne i tabellraden.
   Se _KpiCards.cshtml / _ContractKpiCards.cshtml. */
.bb-kpi-dots {
    display: none;
}

/* KPI-korten som VÅGRÄT REMSA på mobilen (#391 etapp 2b).

   auto-fit + minmax(170px, 1fr) ovan faller till EN kolumn vid 375 px, och fyra fullbreddskort
   staplade blev ~570 px — hela första skärmen gick åt till nyckeltal innan en enda faktura syntes.
   Ägarens invändning var precis den: "skärmen bör rymmas på browser och mobil". Mockens mobil-E
   lägger korten i en vågrät remsa där nästa kort tittar fram, och åtgärdssektionen börjar direkt
   under.

   66 % av remsans bredd, inte 100: det halva kortet i kanten ÄR svepindikatorn. Ett kort som
   slutar exakt vid kanten ser ut som det sista kortet som finns.

   Gäller båda listorna, eftersom båda delar .bb-kpi-row. Två beteenden på två listor var
   uttryckligen det sämsta alternativet när samma fråga kom upp för tabellernas kortläge (#210).

   TANGENTBORD: remsan behöver ingen egen tabindex — korten är <a> med href och alltså
   fokuserbara, och webbläsaren skrollar automatiskt fram det fokuserade kortet. Det är också
   varför snappningen är `proximity` och inte `mandatory`: mandatory drar tillbaka remsan mot
   närmaste snappunkt och kan slåss med den fokusskrollningen. */
@media (max-width: 767.98px) {
    .bb-kpi-row {
        grid-template-columns: none;
        grid-auto-flow: column;
        grid-auto-columns: 66%;
        overflow-x: auto;
        scroll-snap-type: x proximity;

        /* overflow-x: auto klipper även lodrätt. Utan luften här kapas kortens box-shadow
           på över- och underkant och korten ser ut att sakna djup just på mobilen. */
        padding-block: var(--bb-space-1);
        scroll-padding-inline: var(--bb-space-1);

        /* Rullningslisten är skräpig ovanpå fyra kort och remsan har redan en visuell
           indikator i det halva kortet. Den DÖLJS bara — innehållet är fortfarande
           skrollbart med svep, hjul och tangentbord. */
        scrollbar-width: none;
    }

    .bb-kpi-row::-webkit-scrollbar {
        display: none;
    }

    .bb-kpi-row > .bb-kpi {
        scroll-snap-align: start;
    }

    /* Mjuk skrollning bara för den som inte bett om motsatsen. scroll-behavior gäller även
       webbläsarens egen fokusskrollning, så påtvingad smooth hade animerat varje Tab-tryck
       för någon som uttryckligen stängt av rörelse i sitt operativsystem. */
    @media (prefers-reduced-motion: no-preference) {
        .bb-kpi-row {
            scroll-behavior: smooth;
        }
    }

    /* PRICKARNA (#391 etapp 5-efterjustering).

       Det halva kortet i kanten säger att remsan FORTSÄTTER, men inte hur långt — och i mätningen
       lästes det lika ofta som ett avklippt kort som en inbjudan att svepa. Prickarna svarar på
       båda frågorna på en gång: hur många kort som finns, och var i raden man står.

       Luften flyttas hit från raden. .bb-kpi-row bär --bb-space-5 under sig i basen; behålls den
       hamnar prickarna en halv skärmhöjd ned, lösryckta från det de hör till. Marginalen bärs
       därför av höljet i stället, och raden får bara det lilla avståndet upp till prickarna. */
    .bb-kpi-strip {
        margin-bottom: var(--bb-space-5);
    }

    .bb-kpi-strip .bb-kpi-row {
        margin-bottom: var(--bb-space-2);
    }

    .bb-kpi-dots {
        display: flex;
        justify-content: center;
        gap: var(--bb-space-2);
    }

    /* 6 px prick och inte 8: den aktiva blir dubbelt så bred, och den bredare formen är det som
       skiljer lägena åt även för den som inte ser färgskillnaden. Storleken är alltså satt efter
       hur det AKTIVA läget ska se ut, inte det vilande. */
    .bb-kpi-dot {
        width: 6px;
        height: 6px;
        border-radius: 999px;
        background: var(--bb-border);

        /* Övergången animerar bara bredd och färg — inget som flyttar prickarna i sidled mer än
           den enda som växer, så raden inte hoppar under fingret medan man sveper. */
        transition: width 150ms ease, background-color 150ms ease;
    }

    /* Aktivt läge: både fyllt OCH bredare. Färgen ensam räcker inte — det här är den enda
       indikatorn på sidan som saknar text, och den måste gå att avläsa på form. */
    .bb-kpi-dot--active {
        width: 18px;
        background: var(--bb-text-muted);
    }

    @media (prefers-reduced-motion: reduce) {
        .bb-kpi-dot {
            transition: none;
        }
    }
}

/* ==================== Kreditnota inuti moderraden (#188) ==================== */

/* Kreditfakturan renderas som en indenterad andrarad i moderfakturans celler, inte
   som ett syskon-<tr>: DataTables kan lägga en sidbrytning mellan två rader, och då
   står kreditfakturan ensam överst på nästa sida utan sin moderfaktura.

   min-height håller de tomma cellernas andrarad lika hög som de fyllda, så linjen
   löper vågrätt genom hela raden i stället för att hoppa. */

.bb-cell-sub {
    min-height: 1.6em;
    margin-top: var(--bb-space-2);
    padding-top: var(--bb-space-2);
    padding-left: var(--bb-space-3);
    border-top: 1px solid var(--bb-border);
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
}

/* ==================== Radspråket (#391 etapp 1) ==================== */

/* Fakturaraden lär sig prata (docs/design/fakturaytor-mockup.html): nummer före namnet, en
   berättande statusrad under det, beställaren under beloppet och dämpade avslutade rader. */

/* Numret är en adress, inte en rubrik: mono för att siffror ska stå still i ögat mellan rader,
   dämpat för att inte konkurrera med projektnamnet. Ingen mono-token finns i theme.css — stacken
   är skissens. */
.bb-row-no {
    font-family: Consolas, "Cascadia Mono", monospace;
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
}

/* ÄTA-namnet efter projektnamnet: särskiljer rader som delar projektnamn, därför alltid med —
   se kommentaren i _InvoiceRow.cshtml om varför det inte gatas på ShowATAFeature. */
.bb-row-ata {
    color: var(--bb-text-muted);
}

/* Den berättande raden återanvänder bb-cell-sub för typografin men inte för linjen: border-top
   finns där för att kreditnotans andrarad ska läsas som en rad genom hela tabellen, och den här
   står ensam i en cell — ett ensamt stycke linje hade läst som en rendering som gått sönder. */
.bb-narrative,
.bb-row-buyer {
    min-height: 0;
    margin-top: 3px;
    padding-top: 0;
    padding-left: 0;
    border-top: 0;
}

/* Tonerna följer samma färgregel som statusbadgarna (theme.css): röd = du måste agera, gul =
   väntar på dig, grön = klart. neutral och muted delar färg — skillnaden är att muted-raden
   (makulerad/krediterad) dessutom dämpas av bb-row-dim. */
.bb-narrative--danger { color: var(--bb-status-danger-fg); }
.bb-narrative--warn { color: var(--bb-status-warning-fg); }
.bb-narrative--ok { color: var(--bb-status-success-fg); }
.bb-narrative--neutral,
.bb-narrative--muted { color: var(--bb-text-muted); }

/* Avslutade rader (makulerade/krediterade) dämpas i sin helhet: beloppet är återtaget och raden
   kräver ingenting — den ska gå att läsa men inte konkurrera med de aktiva raderna (skissens
   .dim). Klassen sitter på cellerna, inte på <tr>: rad-swappen byter radens innerHTML, så en
   klass på <tr>-noden hade överlevt en swap till ett odämpat läge. */
td.bb-row-dim,
td.bb-row-dim a {
    color: var(--bb-text-muted);
    font-weight: 400;
}

/* Skissens .strike: pengarna är återtagna, och en siffra som ser aktiv ut läser som en fordran. */
.bb-amount-strike {
    text-decoration: line-through;
}

/* ==================== Radåtgärder (#188) ==================== */

/* De lösa ikonerna per rad blir en meny med textetiketter. Ikoner utan text tvingar
   användaren att hovra för att förstå vad raden erbjuder, och de äter radbredd som
   beloppen behöver bättre. */

.bb-row-actions .dropdown-item {
    display: flex;
    align-items: center;
    gap: var(--bb-space-2);
}

.bb-row-actions .dropdown-item i {
    color: var(--bb-text-muted);
}

/* ==================== Tomt läge (#188) ==================== */

/* Listorna hade inget empty state: en tom tabell säger inte om något gick fel, om
   filtret är för snävt eller om man helt enkelt inte kommit igång än. */

/* position: sticky, centrerad i den SYNLIGA delen av scrollytan — inte i hela raden. Tom-
   tillståndets <td colspan> är lika bred som hela den fixed-layout-tabellen (990px+, se
   .bb-table-fixed i theme.css) för att huvudraden ska se likadan ut oavsett radantal. Ett vanligt
   text-align:center hade centrerat meddelandet mitt i den fulla bredden — på en smal skärm, där
   containern är smalare än tabellen, hamnar det då helt utanför den synliga rutan av
   .table-responsive/dataTables_wrapper och kräver manuell horisontell scroll för att ens
   upptäckas. left: 50% + translateX(-50%) på en sticky-positionerad, innehållsbred box centrerar
   den i stället om den aktuella synliga bredden — center på desktop (synligt = hela raden, som
   innan), vänsterförskjuten men alltid synlig även efter scroll på mobil. */
.bb-empty {
    position: sticky;
    left: 50%;
    transform: translateX(-50%);
    display: inline-block;
    max-width: 100%;
    padding: var(--bb-space-6) var(--bb-space-4);
    text-align: center;
}

.bb-empty-title {
    font-size: var(--bb-text-lg);
    font-weight: 600;
    margin-bottom: var(--bb-space-1);
}

.bb-empty-text {
    color: var(--bb-text-muted);
    margin-bottom: var(--bb-space-4);
}

/* ==================== Fakturerat-progress (#188) ==================== */

/* Två separata tal kräver att läsaren räknar i huvudet. En stapel gör förhållandet
   mellan fakturerat och avtalat omedelbart. */

.bb-progress {
    height: .5rem;
    border-radius: var(--bb-radius);
    background: var(--bb-surface-raised);
    overflow: hidden;
}

.bb-progress-bar {
    height: 100%;
    background: var(--bb-status-success-fg, #2e7d32);
}

/* Stapeln kapas vid 100 % så den inte ritar bredare än sitt spår. Utan en egen
   färg blev ett överfakturerat avtal därmed identiskt med ett färdigfakturerat
   (#273). Färgen bär inte betydelsen ensam — captionen under stapeln visar det
   sanna talet, t.ex. "251 %". */
.bb-progress-bar--over {
    background: var(--bb-status-danger-fg, #96261c);
}

.bb-progress-caption {
    margin-top: var(--bb-space-2);
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
    font-variant-numeric: tabular-nums;
}

/* ==================== Bekräftelsedialog ==================== */

/* Dialogen (#199) öppnas ibland ovanpå fakturamodalen. Bootstrap 5.3 ger alla modaler samma
   --bs-modal-zindex (1055), så utan det här avgörs stapelordningen enbart av DOM-ordningen —
   den håller idag, eftersom _ConfirmDialog renderas sist i <body>, men det är en ordning en
   framtida layoutändring kan råka kasta om utan att någon märker det förrän dialogen är oklickbar. */

#bbConfirmDialog {
    --bs-modal-zindex: 1065;
}

/* Frågan är dialogens enda innehåll och står ensam mellan rubriken och knapparna. Bootstraps
   modal-body har ingen egen typografi, så utan den här raden ärver den brödtextens täta radavstånd
   från en kontext där den satt i löpande text. */
#bbConfirmDialog .modal-body {
    padding-top: var(--bb-space-2);
    padding-bottom: var(--bb-space-4);
    color: var(--bb-text);
    line-height: 1.5;
}

/* ==================== Filterrad över listan (#188, del 2) ==================== */

/* Sökrutan och det aktiva filtret på en rad, mellan KPI-korten och tabellkortet. Marginalen
   nedåt är mindre än KPI-radens: raden hör ihop med tabellen under sig, inte med korten över. */
.bb-filter-bar {
    display: flex;
    align-items: center;
    gap: var(--bb-space-3);
    margin-bottom: var(--bb-space-3);
}

/* Full bredd på små skärmar, men aldrig bredare än en sökruta behöver vara. En sökruta som
   spänner över hela tabellen ser ut som ett textfält man ska fylla i, inte som ett filter. */
.bb-filter-search {
    flex: 1 1 auto;
    max-width: 360px;
}

/* htmx tänder .htmx-request på indikatorn under pågående anrop. Utan display: none syns
   spinnern hela tiden; utan visibility skulle raden hoppa när den dyker upp. */
#invoice-search-spinner {
    visibility: hidden;
}

#invoice-search-spinner.htmx-request {
    visibility: visible;
}

/* gap, inte marginal på barnen: blocket bär både chipet och krediteringslänken, och antingen kan
   saknas. Med marginal hade det blivit ett hängande mellanrum i det vanligaste läget (inget chip). */
.bb-filter-state {
    display: inline-flex;
    align-items: center;
    gap: var(--bb-space-3);
}

/* Krediterade fakturor är en sidoväg, inte en huvudåtgärd: en dämpad textlänk och inte en knapp.
   Den ska gå att hitta när man letar efter den, inte konkurrera med filtren om uppmärksamheten. */
.bb-filter-link {
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
    text-decoration: none;
    white-space: nowrap;
}

.bb-filter-link:hover,
.bb-filter-link:focus-visible {
    color: var(--bb-text);
    text-decoration: underline;
}

/* Chipet svarar på "varför ser jag bara de här raderna?". Det är därför det, och inte kortet,
   bär det aktiva läget — korten byts ut out-of-band av varje mutation och kan inte hålla det. */
.bb-filter-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--bb-space-2);
    padding: var(--bb-space-1) var(--bb-space-3);
    border-radius: 999px;
    background: var(--bb-surface-alt, var(--bb-surface));
    box-shadow: var(--bb-shadow-sm);
    font-size: var(--bb-text-sm);
    white-space: nowrap;
}

.bb-filter-chip-clear {
    color: var(--bb-text-muted);
    text-decoration: none;
    font-size: 1.1em;
    line-height: 1;
}

.bb-filter-chip-clear:hover,
.bb-filter-chip-clear:focus-visible {
    color: var(--bb-text);
    text-decoration: none;
}

/* ==================== Upptaget-läge på knappar och länkar ==================== */

/* Ersätter den globala helskärmsloadern som htmx-setup.js tidigare tände vid varje anrop
   (se avsnitt 3 där för varför den är borta). htmx sätter .htmx-request på det element som
   utlöste anropet, så feedbacken hamnar där användaren redan tittar i stället för över hela
   sidan.

   Bara knappar och länkar, aldrig .bb-poll: pollmarkören är display:none och ska förbli
   osynlig — en opacity-regel på den hade inte synts, men pointer-events hör inte hemma där.

   Ingen storleks- eller display-ändring: elementet får inte byta mått mitt i ett klick.
   pointer-events spärrar dubbelklick medan anropet pågår — det är halva poängen på
   Kreditera, som är oåterkallelig.

   Ingen cursor-regel: pointer-events: none gör elementet oträffbart för hit-testet, så
   pekaren hämtar sin form från elementet under. En cursor: progress här hade aldrig synts. */
button.htmx-request,
a.htmx-request {
    opacity: 0.55;
    pointer-events: none;
}

/* Pollmarkören för rader i Pending (#188, del 2). Den bär bara hx-attributen som driver
   15-sekunderspollen och ska aldrig synas — men den måste ligga kvar i DOM:en, för det är
   den som håller timern vid liv tills servern svarar med en rad som inte längre pollar. */
.bb-poll {
    display: none;
}

/* ==================== Skalet: sidhuvud, sidomeny, header (#205, fas 6) ====================
   "Skalet först" i connect.min.css-avvecklingen (UI_UX_STRATEGI.md, Område 5): egna regler
   här vinner över Connect utan att röra den vendorade filen. Alla under Features:ModernUI. */

/* Sidhuvudet: sidtitel + valfri underrubrik. Primäråtgärden är INTE en del av denna
   komponent — .bb-page-actions är en syskon-div som varje anropande vy fyller själv.
   .bb-page-header-row binder ihop dem till EN rad: titel vänster, åtgärd höger. Det är
   hela poängen med princip 1 (Område 5) — knappen ska ha en fast plats, inte hamna under
   rubriken. Vyer utan primäråtgärd (Fakturor, Registrering) behöver ingen rad-wrapper. */
.bb-page-header-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--bb-space-4);
    flex-wrap: wrap;
}

.bb-page-header {
    margin-bottom: var(--bb-space-4);
}

.bb-page-header-row .bb-page-header,
.bb-page-header-row .bb-page-actions {
    margin-bottom: 0;
}

.bb-page-header-row {
    margin-bottom: var(--bb-space-4);
}

/* text-align sätts explicit: app.css har en global `h1 { text-align: center }` som annars
   ärvs hit och centrerar sidtiteln. Precis den sortens Connect-/app.css-arv som skalet ska
   vinna över (Område 5) — utan !important, bara högre specificitet via klassen. */
.bb-page-header-title {
    margin: 0;
    font-size: var(--bb-text-2xl);
    font-weight: 600;
    color: var(--bb-text);
    text-align: left;
}

.bb-page-header-subtitle {
    margin: var(--bb-space-1) 0 0;
    font-size: var(--bb-text-base);
    color: var(--bb-text-muted);
    text-align: left;
}

.bb-page-actions {
    margin-bottom: var(--bb-space-4);
}

/* Skapa avtal och Skapa faktura renderar sidhuvudet UTANFÖR sitt <form class="mx-3">, så
   titeln låg kant i kant med skärmen medan korten under den var indragna 1 rem. Listorna har
   inte problemet — där ligger både titel och kort inuti samma .mx-3. (fas 8, #210) */
.bb-page-shell > .bb-page-header,
.bb-page-shell > .bb-page-header-row {
    margin-left: 1rem;
    margin-right: 1rem;
}

/* Min profil ligger i .main-wrapper, inte i .page-content. Det är .page-content som bär
   Connects margin-top: 91px — den enda anledningen till att listornas innehåll klarar den
   66 px höga navbaren. Utan motsvarande klarning hamnar sidtiteln bakom navbaren här.
   Explicit px, som 3px-markören ovan: ett legacy-mått att kompensera, inte en designtoken.
   Raden kan tas bort när Connect-layouten avvecklas (Område 5, steg 4). */
.profile .bb-page-header {
    margin-top: 91px;
}

/* Sidomenyns aktiv-markering: yta OCH kantmarkör, inte bara textfärg (princip 2, Område 5).
   3px är en strukturell engångssiffra i den här filens etablerade stil (jfr 999px-pillret för
   .bb-filter-chip ovan) — ingen ny designtoken för det. */
.bb-nav-item--active {
    background: var(--bb-surface-raised);
}

/* Kantmarkören sitter på <a>, inte på <li>: Connect fyller själva länken med en mörk
   accentyta som helt täcker li:ets bakgrund och kant. Markören måste därför ligga ovanpå
   den ytan, och i --bb-accent-contrast — samma färg som texten på accentytan, alltså den
   enda som per definition har kontrast mot den. */
.bb-nav-item--active > a,
.bb-nav-item--active > a:hover,
.bb-nav-item--active > a:focus-visible {
    border-left: 3px solid var(--bb-accent-contrast);
}

.page-sidebar-inner .accordion-menu li > a:hover,
.page-sidebar-inner .accordion-menu li > a:focus-visible {
    color: var(--bb-accent-hover);
}

/* Kompaktläget som ett verkligt läge (#205) — layouten, inte bara bredden.

   Connect skrev sitt kompaktläge för en sidomeny som ligger i flödet: den sätts till
   `position: static` och innehållet får `margin-left: 0` med `width: calc(100% - 80px)`.
   app.css gör sedan länge sidomenyn `position: fixed` i stället (rad 247), och det är den
   fixeringen som bär det normala läget — där håller `.page-container { margin-left: 280px }`
   undan innehållet.

   I kompaktläget slog Connect-regeln alltså bort fixeringen, sidomenyn blev ett vanligt
   block, och hela .page-container hamnade UNDER den i stället för bredvid: ~357 px tomrum
   överst och en header som la sig i vänsterkant. Kompaktläget var i praktiken obrukbart.

   Samma media query som Connects egen regel — under 1200 px finns inget kompaktläge, där är
   sidomenyn off-canvas. Filen laddas efter connect.min.css, så samma specificitet räcker och
   inget !important behövs. */
@media (min-width: 1200px) {
    .compact-sidebar .page-sidebar {
        position: fixed;
    }

    .compact-sidebar .page-container {
        margin-left: 80px;
        width: calc(100% - 80px);
    }
}

/* Header: dämpad organisationsindikator överst i kontomenyn. Den ligger i .dropdown-menu,
   inte bredvid avataren — Connect döljer alla <span> i .nav-link.dropdown-toggle via en
   media query (samma regel som redan döljer användarnamnet), så en indikator där hade varit
   osynlig. Issue #205 säger dessutom "kontomeny med organisationsnamn". */
.bb-org-name {
    display: block;
    padding: var(--bb-space-2) var(--bb-space-4);
    font-size: var(--bb-text-xs);
    color: var(--bb-text-muted);
    border-bottom: 1px solid var(--bb-border);
    white-space: nowrap;
}

/* Reserverad plats för notifieringsklockan (#189) — ingen ikon eller JS ännu, bara mått
   så headerns layout inte hoppar när den läggs till. */
.bb-notification-slot {
    display: inline-block;
    width: var(--bb-space-6);
    height: var(--bb-space-6);
}

/* ==================== Aktivitetslogg (#189, fas 5) ====================

   Tidslinjen underst i faktura- och avtalsmodalen. Rent additiv: markupen renderas bara med
   Features:HtmxEnhancements på, och laddas in i efterhand via hx-get. */

/* <ol>, inte <ul> — ordningen bär betydelse här, den ÄR kronologin. list-style tas bort men
   elementet behålls, så en skärmläsare fortfarande annonserar "lista med N poster". */
.bb-timeline {
    list-style: none;
    margin: 0;
    padding: 0;
    position: relative;
}

.bb-timeline-item {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: baseline;
    gap: var(--bb-space-3);
    padding: var(--bb-space-2) 0;
    position: relative;
}

/* Den vertikala linjen ritas som en pseudo-border på varje post utom den sista, inte som ett
   absolutpositionerat streck över hela listan. Med en enda linje bakom allt sticker den ut
   nedanför sista prickens mitt, vilket läses som "det finns mer som inte visas". */
.bb-timeline-item:not(:last-child)::before {
    content: "";
    position: absolute;
    left: calc(var(--bb-space-4) / 2);
    top: var(--bb-space-6);
    bottom: 0;
    width: 1px;
    background: var(--bb-border);
}

/* Pricken. Egen färgfamilj (bb-timeline-marker--*) trots att den läser samma
   --bb-status-*-tokens som statuspillret: samma KÄLLA till färgen, egen form. Att återanvända
   .bb-status--success här hade gett pricken pillrets padding och radie. */
.bb-timeline-marker {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--bb-space-4);
    height: var(--bb-space-4);
    border-radius: 50%;
    font-size: var(--bb-text-xs);
    flex: 0 0 auto;
    /* Ovanpå linjen, annars går strecket tvärs igenom pricken. */
    position: relative;
    z-index: 1;
}

.bb-timeline-marker--neutral {
    background: var(--bb-status-neutral-bg);
    color: var(--bb-status-neutral-fg);
}

.bb-timeline-marker--info {
    background: var(--bb-status-info-bg);
    color: var(--bb-status-info-fg);
}

.bb-timeline-marker--success {
    background: var(--bb-status-success-bg);
    color: var(--bb-status-success-fg);
}

.bb-timeline-marker--warning {
    background: var(--bb-status-warning-bg);
    color: var(--bb-status-warning-fg);
}

.bb-timeline-marker--danger {
    background: var(--bb-status-danger-bg);
    color: var(--bb-status-danger-fg);
}

.bb-timeline-text {
    font-size: var(--bb-text-sm);
    color: var(--bb-text);
}

/* tabular-nums av samma skäl som beloppen i listorna: datumen står i en kolumn och ska
   linjera tecken för tecken. */
.bb-timeline-date {
    font-size: var(--bb-text-xs);
    color: var(--bb-text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.bb-timeline-empty {
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
    margin: 0;
}

/* ==================== Notifieringscenter (#189, fas 5) ====================

   Klockan i headern och dess panel. Ersätter .bb-notification-slot's rena platshållarmått från
   #205 — slotten står kvar och bär nu innehåll. */

.bb-bell {
    position: relative;
    display: inline-block;
}

/* Räknaren ankras mot länken, inte mot slotten: slotten har fasta mått medan länken är det som
   faktiskt bär ikonen, och en absolut position mot en tom container hade lagt pillret fel så
   snart Connects nav-link-padding ändrades. */
.bb-bell-link {
    position: relative;
    display: inline-flex;
    align-items: center;
}

/* min-width + padding i stället för fast bredd: "9+" är bredare än "3", och ett pill som klipper
   sin egen text är värre än ett som växer. */
.bb-bell-count {
    position: absolute;
    top: 0;
    right: 0;
    transform: translate(35%, -35%);
    min-width: 1rem;
    padding: 0 .25rem;
    border-radius: 999px;
    background: var(--bb-status-danger-fg);
    color: #fff;
    font-size: .625rem;
    /* Se [data-bs-theme="dark"]-regeln nedan: --bb-status-danger-fg är en
       FÖRGRUNDSton, och i mörkt läge är den ljus. Vit text på den ger 2.66:1. */
    line-height: 1rem;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* Notisräknaren använder --bb-status-danger-fg som PLATTA, men tokenet är en
   förgrundston: mörkröd i ljust läge (vit text ger 8.11:1, bra) och ljusröd i
   mörkt (vit text ger 2.66:1, underkänt). I mörkt läge vänds därför texten till
   bakgrundstonen i stället — 6.04:1, och samma konstruktion som mörkerlägets
   övriga badges: ljus platta, mörk text. (#224) */
[data-bs-theme="dark"] .bb-bell-count {
    color: var(--bb-body-bg);
}

.bb-bell-panel {
    width: 22rem;
    max-width: calc(100vw - var(--bb-space-4));
    padding: var(--bb-space-2) 0;
    background: var(--bb-surface);
    border: 1px solid var(--bb-border);
    box-shadow: var(--bb-shadow-md);
}

/* Mobil (fas 8, #210): max-width ensam räcker inte. Bootstrap positionerar dropdownen absolut
   från klockans vänsterkant, och på 375 px hamnar den kanten så långt åt höger att panelen
   sticker ut ~117 px utanför skärmen — klippt, utan att sidan får scroll som avslöjar det.
   position: fixed lyfter den ur klockans koordinatsystem och spänner den mellan skärmkanterna.
   !important för att slå Poppers inline transform/left, som sätts på elementet självt. */
@media (max-width: 575.98px) {
    .bb-bell-panel.dropdown-menu {
        position: fixed !important;
        left: var(--bb-space-2) !important;
        right: var(--bb-space-2) !important;
        top: 60px !important;
        width: auto;
        max-width: none;
        transform: none !important;
    }
}

.bb-bell-list {
    list-style: none;
    margin: 0;
    padding: 0;
    /* Taket är tio poster, men projektnamn radbryter — så höjden är inte given av antalet.
       En panel som växer förbi viewporten kan inte scrollas tillbaka upp i en dropdown. */
    max-height: 60vh;
    overflow-y: auto;
}

.bb-bell-item {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: start;
    gap: var(--bb-space-3);
    /* Höger padding lämnar plats för avfärda-krysset. */
    padding: var(--bb-space-3) var(--bb-space-6) var(--bb-space-3) var(--bb-space-4);
    color: var(--bb-text);
    text-decoration: none;
}

.bb-bell-item:hover,
.bb-bell-item:focus-visible {
    background: var(--bb-surface-raised);
    color: var(--bb-text);
}

/* Raden bär både länken och avfärda-knappen. Relativ, så krysset kan ligga i hörnet utan att äta
   av länkens klickyta — hela raden ska gå att klicka, inte bara texten. */
.bb-bell-row {
    position: relative;
}

/* Läst: dämpad, men INTE dold. En läst notis står kvar tills tillståndet upphör eller användaren
   avfärdar den — opacity är skillnaden mot att ha läst bort den. Under .6 blir texten oläslig
   mot ytan i mörkt läge. */
.bb-bell-row.is-read .bb-bell-item {
    opacity: .6;
}

/* Krysset syns på hover, fokus och på touch (där hover aldrig inträffar). Alltid i DOM:en och
   alltid fokuserbart — opacity, inte display: none, så tangentbord och skärmläsare når det även
   när musen inte är där. */
.bb-bell-dismiss {
    position: absolute;
    top: var(--bb-space-2);
    right: var(--bb-space-2);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.25rem;
    height: 1.25rem;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--bb-text-muted);
    font-size: var(--bb-text-xs);
    line-height: 1;
    opacity: 0;
    transition: opacity .12s ease-in-out;
}

.bb-bell-row:hover .bb-bell-dismiss,
.bb-bell-row:focus-within .bb-bell-dismiss,
.bb-bell-dismiss:focus-visible {
    opacity: 1;
}

@media (hover: none) {
    /* Utan hover finns ingen väg att få fram knappen — visa den permanent. */
    .bb-bell-dismiss {
        opacity: 1;
    }
}

.bb-bell-dismiss:hover,
.bb-bell-dismiss:focus-visible {
    background: var(--bb-status-neutral-bg);
    color: var(--bb-text);
}

.bb-bell-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--bb-space-5);
    height: var(--bb-space-5);
    border-radius: 50%;
    font-size: var(--bb-text-xs);
    flex: 0 0 auto;
}

.bb-bell-icon--neutral {
    background: var(--bb-status-neutral-bg);
    color: var(--bb-status-neutral-fg);
}

.bb-bell-icon--info {
    background: var(--bb-status-info-bg);
    color: var(--bb-status-info-fg);
}

.bb-bell-icon--success {
    background: var(--bb-status-success-bg);
    color: var(--bb-status-success-fg);
}

.bb-bell-icon--warning {
    background: var(--bb-status-warning-bg);
    color: var(--bb-status-warning-fg);
}

.bb-bell-icon--danger {
    background: var(--bb-status-danger-bg);
    color: var(--bb-status-danger-fg);
}

.bb-bell-body {
    display: flex;
    flex-direction: column;
    gap: .125rem;
    /* min-width: 0 på ett grid-barn, annars vägrar det krympa under sitt innehåll och
       projektnamn med långa ord spränger panelens bredd i stället för att radbryta. */
    min-width: 0;
}

.bb-bell-text {
    font-size: var(--bb-text-sm);
}

.bb-bell-detail {
    font-size: var(--bb-text-xs);
    color: var(--bb-text-muted);
    overflow-wrap: anywhere;
}

.bb-bell-date {
    font-size: var(--bb-text-xs);
    color: var(--bb-text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.bb-bell-empty,
.bb-bell-more {
    margin: 0;
    padding: var(--bb-space-3) var(--bb-space-4);
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
}

.bb-bell-more {
    font-size: var(--bb-text-xs);
    border-top: 1px solid var(--bb-border);
}

/* ==================== Publika ytor (#209, fas 7) ====================

   Spårningssidan (Invoice/Track) är den enda ytan en beställare ser utan konto. Reglerna
   nedan gäller bara den — de publika vyerna delar layout med betalsidan (fas 11, #214), så
   inget här får kroka i något som är gemensamt. Därav .bb-track-* som eget prefix i stället
   för regler på .page-content. */
.bb-track {
    max-width: 34rem;
    margin: var(--bb-space-6) auto 0;
    text-align: left;
}

.bb-track-status {
    display: flex;
    justify-content: center;
    margin-bottom: var(--bb-space-6);
}

.bb-track-timeline {
    list-style: none;
    margin: 0;
    padding: 0;
}

.bb-track-step {
    position: relative;
    display: flex;
    gap: var(--bb-space-3);
    padding-bottom: var(--bb-space-5);
}

/* Förbindelselinjen mellan stegen ritas av steget själv och inte av en linje bakom hela
   listan: sista steget ska inte ha någon svans, och :last-child är den enda som vet det. */
.bb-track-step:not(:last-child)::before {
    content: '';
    position: absolute;
    left: 0.5rem;
    top: 1.5rem;
    bottom: 0;
    width: 2px;
    background: var(--bb-border);
}

.bb-track-step:last-child {
    padding-bottom: 0;
}

.bb-track-step-marker {
    flex: 0 0 auto;
    line-height: 1;
    color: var(--bb-text-muted);
}

.bb-track-step--done .bb-track-step-marker {
    color: var(--bb-status-success-fg);
}

.bb-track-step--current .bb-track-step-marker {
    color: var(--bb-status-progress-fg);
}

.bb-track-step-body {
    display: flex;
    flex-direction: column;
    gap: var(--bb-space-1);
}

.bb-track-step-title {
    font-weight: 600;
    color: var(--bb-text);
}

/* Kommande steg dämpas, men bara i vikt och färg — aldrig med opacity på hela raden, som
   tar med sig markören och gör kontrasten oförutsägbar mot både ljus och mörk yta. */
.bb-track-step--pending .bb-track-step-title {
    font-weight: 400;
    color: var(--bb-text-muted);
}

.bb-track-step-note {
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
}

/* Felsidan (#209). Läsbar radlängd och en tydlig väg tillbaka — den ligger i _Layout, alltså
   både innanför och utanför skalet beroende på om användaren är inloggad. */
.bb-error {
    max-width: 36rem;
    margin: var(--bb-space-8) auto;
    padding: 0 var(--bb-space-4);
}

/* 91px = headerns 66px plus normalt sektionsavstånd. Samma engångssiffra som
   .profile .bb-page-header ovan, av exakt samma skäl: headern är position: fixed och tas
   därför inte upp av flödet. */
.bb-error--in-shell {
    margin-top: 91px;
}

.bb-error-title {
    text-align: left;
    font-size: var(--bb-text-2xl);
    margin-bottom: var(--bb-space-3);
}

.bb-error-body {
    color: var(--bb-text-muted);
    margin-bottom: var(--bb-space-5);
}

.bb-error-ref {
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
}

/* Koden ska gå att markera och läsa upp i telefon — därför monospace och egen yta, inte
   dämpad brödtext som den tidigare statusraden i 50 % alfa. */
.bb-error-code {
    display: inline-block;
    padding: 0 var(--bb-space-2);
    background: var(--bb-surface-raised);
    border: 1px solid var(--bb-border);
    border-radius: var(--bb-radius);
    color: var(--bb-text);
    overflow-wrap: anywhere;
}

.bb-error-code-line {
    font-size: var(--bb-text-xs);
    color: var(--bb-text-muted);
}

/* Kontoflödena: inloggning, glömt och återställ lösenord (#209).

   De renderas i _Layouts utloggade gren och har alltså haft tokens sedan fas 1 — det som
   saknades var struktur. Connects .form-control-auth sätter varken bredd eller display, så
   fälten låg på webbläsarens defaultbredd i stället för kortets, och dess fokusring är
   mintgrön (#5fd0a5) — mallens accent, inte vår. */
.bb-auth-header {
    margin-bottom: var(--bb-space-5);
    text-align: center;
}

.bb-auth-title {
    font-size: var(--bb-text-xl);
    font-weight: 700;
    color: var(--bb-text);
    margin-bottom: var(--bb-space-2);
}

.bb-auth-subtitle {
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
    margin: 0;
}

.authentication .form-control-auth {
    display: block;
    width: 100%;
    background: var(--bb-surface);
    border-color: var(--bb-border);
    color: var(--bb-text);
    font-weight: 500;
}

.authentication .form-control-auth::placeholder {
    color: var(--bb-text-muted);
}

/* The focus ring in the accent colour, and as a ring rather than a coloured border, so
   "focus" and "error" stay tellable apart. color-mix gives the ring the accent's hue
   without a token of its own.

   This comment used to claim the border "already changes colour on a validation error".
   It did not: every error rule in app.css and theme.css is qualified with .form-control,
   .form-select or textarea, and none of them names .form-control-auth. Measured on Forgot
   password, a field with .input-validation-error had a grey border and its normal
   background when unfocused, and a green border plus a green ring when focused — the only
   error signal was the text below the field. The rules underneath are what make the
   assumption true (#458). */
.authentication .form-control-auth:focus {
    border-color: var(--bb-accent);
    box-shadow: 0 0 0 .2rem color-mix(in srgb, var(--bb-accent) 25%, transparent);
}

/* ---- Fields in error: red border and red ring (#458) ----

   THIS IS THE COPY MEANT TO SURVIVE. The same rules live in theme.css, which is linked
   regardless of the flag and is therefore the only copy that applies in prod while
   ModernUI is off. When theme.css is eventually retired that copy goes with the file and
   this one stays. Never change one without the other while both exist.

   No !important here, unlike in theme.css: these sit in the same file as .authentication
   .form-control-auth:focus above, at the same specificity (0,3,0), and later in source
   order. theme.css instead has to beat this file, which is loaded after it.

   The hues: the border is #dc3545, the same red as every other field in error in the app.
   The ring is --bb-danger-text, already contrast-measured against the page surfaces. The
   accent is green, and a green ring around the one field that is wrong reads as approval —
   a failed submit moves focus there, so of every field in error the one the user has to
   fix carried the only marking that looked positive. */
.form-control.invalid:focus-visible,
.form-select.invalid:focus-visible,
textarea.form-control.invalid:focus-visible,
.form-control.input-validation-error:focus-visible,
.form-select.input-validation-error:focus-visible,
textarea.form-control.input-validation-error:focus-visible {
    box-shadow: 0 0 0 2px var(--bb-surface), 0 0 0 4px var(--bb-danger-text, #96261c) !important;
}

.form-control-auth.invalid,
.form-control-auth.input-validation-error {
    border-color: #dc3545;
}

/* :focus, not :focus-visible: the rule above sets its green border on :focus, so a mouse
   click into the field would otherwise still come out green. */
.form-control-auth.invalid:focus,
.form-control-auth.input-validation-error:focus {
    border-color: #dc3545;
    box-shadow: 0 0 0 2px var(--bb-surface), 0 0 0 4px var(--bb-danger-text, #96261c);
}

/* Felmeddelandet under fältet: samma dämpade storlek som resten av hjälptexten, men i
   danger-tonen. Utan den här regeln ärver den Bootstraps .text-danger rakt av, som är
   framtagen mot vit yta och tappar kontrast i mörkt läge. */
.authentication .text-danger {
    display: block;
    margin-top: var(--bb-space-1);
    font-size: var(--bb-text-sm);
    color: var(--bb-status-danger-fg) !important;
}

/* Sedan #318 ligger "Skapa konto?" ovanför "Glömt ditt lösenord?" i stället för sist i
   formuläret. Marginalen nedåt är därför inte dekoration: de två länkarna är staplade
   och centrerade, och utan den låg deras 44 px höga träffytor kant i kant — två mål att
   missa med tummen i stället för ett att träffa. */
.authentication .auth-options {
    margin-top: var(--bb-space-4);
    margin-bottom: var(--bb-space-2);
    text-align: center;
}

/* Taket är satt i rem, inte i procent: på en bred skärm ska logotypen inte växa till en
   banderoll bara för att det finns plats, och img-fluid i markupen sköter golvet nedåt.

   Den vita plattan som stod här (#209) är borta. Den var en nödlösning för att Logo.PNG hade
   sin gräddfärgade botten inbakad och ingen ljus variant fanns — nu finns logo-light.png och
   logo-dark.png med utfrätt botten, och _Logo.cshtml väljer rätt av dem. Se theme.css. */
.bb-public-logo {
    max-width: min(100%, 18rem);
    height: auto;
}

/* Sekundär åtgärd på de publika ytorna (#209). Bootstraps btn-outline-secondary bygger på
   --bs-secondary (grå), som i mörkt läge hamnar på ~2:1 mot den mörka ytan. Tokens i stället:
   samma kant som korten, textfärgen från temat, så knappen har kontrast i båda lägena utan en
   egen färgskala. */
.bb-btn-quiet {
    background: transparent;
    border: 1px solid var(--bb-border);
    color: var(--bb-text);
}

.bb-btn-quiet:hover,
.bb-btn-quiet:focus-visible {
    background: var(--bb-surface-raised);
    color: var(--bb-text);
}

/* Villkorssidan (#209). Juridisk text i full sidbredd på en 1920-skärm blir ~200 tecken per
   rad; ögat tappar radbytet långt före det. 70ch är den vedertagna övre gränsen. Rubrikerna
   inuti panelerna är <h3> på varje paragrafnivå, vilket ger fem likadana nivåer i rad — de
   dämpas hit ner till sektionsrubriker så § syns som avdelare i stället för som brus. */
.bb-terms .et_pb_text_inner {
    max-width: 70ch;
}

/* Barnkombinator sedan #224: klausulernas titlar är numera <h3> inuti nästlade
   <details><summary>, som ligger djupare ned i .et_pb_text_inner. Utan `>` hade den här regeln
   dämpat och versaliserat alla 33 paragrafrubriker — den är avsedd för ingressen i
   återbetalningspolicyn, som är ett direkt barn. */
.bb-terms .et_pb_text_inner > :is(h2, h3) {
    margin-top: var(--bb-space-6);
    font-size: var(--bb-text-base);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .02em;
    color: var(--bb-text-muted);
}

.bb-terms .et_pb_text_inner p {
    margin-bottom: var(--bb-space-4);
    line-height: 1.7;
}

/* Nedan hör ihop med att <br>-taggarna togs bort (fas 8, #210). Texten flyter nu fritt, och
   då är styckeavståndet det enda som bär strukturen — därför får den typografin göra jobbet
   som de hårdkodade radbrytningarna gjorde dåligt. */

/* Scenariorubrikerna i återbetalningspolicyn ("1. Beställaren eller Utföraren vill avbryta…")
   är rubriker i texten, inte stycken. De är inte <h3> i markupen och ska inte bli det heller —
   det är innehållets ordval, inte dess nivå, som gör dem till rubriker. */
.bb-terms .et_pb_text_inner .bb-terms-lead {
    margin-top: var(--bb-space-6);
    margin-bottom: var(--bb-space-2);
    font-weight: 600;
    color: var(--bb-text);
}

/* §2.1 räknar upp fyra samarbetspartner, en per rad i originalet. Som löpande stycken tappade
   uppräkningen sin form; som lista behåller den den utan att ett enda ord ändras. */
.bb-terms .et_pb_text_inner .bb-terms-list {
    margin: 0 0 var(--bb-space-4);
    padding-left: 1.25rem;
    line-height: 1.7;
}

.bb-terms .et_pb_text_inner .bb-terms-list li {
    margin-bottom: var(--bb-space-1);
}

/* Långa ord (”personuppgiftsbiträdesavtal”, ”myndighetsbestämmelser”) spränger annars spalten
   på 375 px. Avstavning i stället för överflöd — lang="sv" står på <html>. */
.bb-terms .et_pb_text_inner {
    hyphens: auto;
    overflow-wrap: break-word;
}

/* Meddelandet när ett godkännande avbrutits (#209). Samma bredd som tidslinjen så texten inte
   spänner över hela skärmen, och vänsterställd — det är en förklaring att läsa, inte en rubrik. */
.bb-track-alert {
    max-width: 34rem;
    margin: var(--bb-space-5) auto 0;
    text-align: left;
}

/* ==================== Mobil och responsivt (fas 8, #210) ==================== */

/* Brytpunktsskalan bor i theme.css (--bb-bp-*) och är en konvention, inte en mekanism:
   custom properties går inte att använda i en @media-fråga, så pixelvärdena upprepas
   här. De numren och tokenvärdena MÅSTE hållas i synk.

   Vägvalet under golvet (issuens "ett beslut, inte två olika beteenden"): tabellerna
   blir kort under --bb-bp-md, båda två, via CSS på samma markup. Alternativet
   DataTables `responsive` gav en kollapsad detaljrad som bara fanns på fakturalistan
   — och den lägger till/tar bort <td>-noder vid varje resize, vilket krockar med att
   rad-swappen (htmx) byter <tr>-ens innerHTML under fötterna på den. `responsive: true`
   är därför borttagen ur bbInitInvoiceTable i samma veva. Mellan --bb-bp-md och
   tabellens min-width står .table-responsive:s horisontella scroll kvar oförändrad. */

/* ---- Touch-mål ----
   WCAG 2.5.5: 44x44 px. Ytorna nedan låg mellan 32 och 38 px och är de som faktiskt
   träffas fel på telefon. min-height (inte height) så en post som råkar radbryta
   fortfarande växer i stället för att klippa texten. */

.dropdown-menu .dropdown-item,
.dropdown-menu .dropdown-item-text {
    min-height: var(--bb-touch-target);
    display: flex;
    align-items: center;
    gap: var(--bb-space-2);
}

.bb-row-actions .btn {
    min-width: var(--bb-touch-target);
    min-height: var(--bb-touch-target);
}

.accordion-menu > li > a {
    min-height: var(--bb-touch-target);
    display: flex;
    align-items: center;
}

/* Hamburgaren, temaväxlaren, klockan och profilmenyn — headerns hela ikonrad. Ikonen förblir
   liten; det är träffytan runt den som växer, vilket är hela poängen med 2.5.5. Selektorn går
   på .page-header .navbar-nav > li > a och inte på enskilda klasser: klockan sitter som
   a.nav-link.bb-bell-link INUTI sin slot och missades av en selektor på slotten. */
.page-header .navbar ul.navbar-nav > li > a,
.page-header .navbar ul.navbar-nav .bb-bell-link,
.small-screens-sidebar-link a,
.logo-box > a#sidebar-close,
.logo-box > a#sidebar-state {
    min-width: var(--bb-touch-target);
    min-height: var(--bb-touch-target);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* Profilmenyns länk bär namn + avatar + pil och ska växa i höjd, inte tvingas till en kvadrat. */
.page-header .navbar ul.navbar-nav > li.nav-profile > a {
    justify-content: flex-start;
}

/* Touch-enheter och små skärmar: resten av de klickbara ytorna upp över golvet. Avgränsat hit
   för att en 44 px-höjd på varje .btn-sm hade ritat om desktopvyerna, där en muspekare inte har
   samma problem. (hover: none) fångar surfplattan som är bredare än brytpunkten. */
@media (max-width: 991.98px), (hover: none) {
    /* display rörs INTE här. Ett generellt inline-flex på .btn såg oskyldigt ut men slog sönder
       betalsidans två stora val-knappar: de är <div class="btn"> med en float:left-ikon, ett
       <br> och en bildtext under. I en flexcontainer ignoreras <br>, så ikon, etikett och
       bildtext blev tre kolumner bredvid varandra ovanpå varandra. Höjdgolvet är det enda som
       behövs för träffytan; centreringen hör hemma på de kontroller som faktiskt behöver den. */
    .pagination .page-link,
    .btn,
    .dataTables_wrapper .paginate_button,
    .nav-tabs .nav-link {
        min-height: var(--bb-touch-target);
    }

    .pagination .page-link,
    .dataTables_wrapper .paginate_button,
    .nav-tabs .nav-link {
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    /* Kontoflödenas fristående länkar ("Glömt ditt lösenord?", "Skapa konto?"). De är inte
       inline-länkar i en mening — de är sidans sekundära åtgärder och har samma krav som en
       knapp. Selektorn går på deras behållare i stället för på <a> generellt, som hade blåst
       upp varje länk i löptext.

       `.forgot-link` stod här som tredje selektor och är borttagen med #318: klassen hade
       exakt en användning i hela kodbasen, på "Skapa konto?"-länken, och den satt där bara
       för att ärva en float ur connect.min.css. När klassen försvann ur markupen blev
       selektorn död — och `.auth-options a` ovan täckte redan samma element. */
    .auth-options a,
    .auth-page .card-body .text-center > a {
        display: inline-flex;
        align-items: center;
        min-height: var(--bb-touch-target);
    }

    /* Formulärfälten är också träffytor — ett 32 px högt <select> är svårare att pricka än en
       knapp, eftersom det dessutom öppnar en lista. display rörs INTE här: en inline-flex på ett
       <input> är odefinierat territorium och block/inline-block är vad Bootstrap räknar med. */
    .form-control,
    .form-select,
    select.form-control,
    input[type="text"],
    input[type="email"],
    input[type="password"],
    input[type="number"],
    input[type="date"],
    input[type="tel"],
    select {
        min-height: var(--bb-touch-target);
    }
}

/* ---- Sidomenyn som drawer (< --bb-bp-lg) ----
   Connect gör sidomenyn 100 % bred under 991 px och tonar in den med opacity/visibility.
   Två problem: den äter hela skärmen (princip 5), och en osynlig men utlagd 100 %-panel
   ligger kvar över innehållet under uttoningen. Här blir den i stället en riktig drawer
   som skjuts in från vänster med en scrim bakom. */

@media (max-width: 991.98px) {
    .page-sidebar {
        width: min(320px, 86vw);
        max-width: 320px;
        padding: var(--bb-space-5) var(--bb-space-4);
        /* Åter till transform-mekaniken från 1199-brytpunkten: visibility/opacity ersätts
           så drawern verkligen ligger utanför skärmen när den är stängd. */
        visibility: visible;
        opacity: 1;
        transform: translateX(-100%);
        box-shadow: var(--bb-shadow-md);
        overflow-y: auto;
        /* iOS: låt drawern scrolla klart utan att sidan bakom börjar följa med. */
        overscroll-behavior: contain;
    }

    .small-screen-sidebar-active .page-sidebar {
        transform: translateX(0);
    }

    /* Scrimen. Pseudo-element på <body> i stället för en ny nod: markupen är delad av
       varje sida i appen, och ett klick på ::before träffar body — precis det som
       connect.min.js:s "klick utanför"-lyssnare redan tolkar som stäng. */
    .small-screen-sidebar-active::before {
        content: "";
        position: fixed;
        inset: 0;
        z-index: 9998;
        background: rgba(16, 24, 40, .45);
    }

    /* Sidan bakom drawern ska inte gå att scrolla medan den är öppen. */
    .small-screen-sidebar-active {
        overflow: hidden;
    }
}

@media (max-width: 991.98px) and (prefers-reduced-motion: reduce) {
    .page-sidebar {
        transition: none;
    }
}

/* ---- Listtabellerna som kort (< --bb-bp-md) ----
   Samma markup, ingen andra renderingsväg: cellerna får sin rubrik ur bb-cell-label och
   läggs om till etikett/värde-par. Det är avgörande för att rad-swappen ska fungera —
   servern svarar med exakt samma celler oavsett skärmbredd, så en swap på mobil kan
   inte hamna i fel form. bb-cell-label sätts bara när ModernUI är på (se _InvoiceRow /
   _ContractRow), så med flaggan av finns varken attributen eller de här reglerna. */

@media (max-width: 767.98px) {
    /* Golvet (min-width, inline på tabellen) och den fixerade layouten gäller bara så
       länge tabellen ÄR en tabell. Som kort ska den följa skärmen. */
    /* Inte `.table-responsive > table`: DataTables lindar tabellen i .dataTables_wrapper vid
       init, så tabellen är inte längre ett direkt barn. Med barnkombinatorn träffade regeln
       bara den oinitierade (tomma) tabellen — och den populerade behöll sitt 1270 px-golv och
       sköt korten utanför skärmen. */
    table.bb-table-fixed {
        min-width: 0 !important;
        table-layout: auto;
        width: 100% !important;
    }

    /* Bara de wrappers som innehåller en korttabell. En generell regel här stängde av
       horisontell scroll för ALLA .table-responsive — inklusive ÄTA-tabellen i avtalsmodalen
       och produktlistan på Skapa faktura, som fortsätter vara riktiga tabeller med scroll
       (princip 3) eftersom de är inmatningsrutnät och inte läslistor. */
    .table-responsive:has(table.bb-table-fixed) {
        overflow-x: visible;
    }

    /* Samma sak ett lager in. Regeln ovan träffar .table-responsive, men DataTables
       lindar tabellen i .dataTables_wrapper, och det är DEN som bär scrollen (app.css,
       #272). Lämnas den som overflow-x: auto i kortläget blir den en scrollcontainer —
       och eftersom en axel som inte är `visible` tvingar den andra till `auto` börjar
       den klippa i höjdled också. Fakturaradens åtgärdsmeny är en Bootstrap-dropdown
       utan fixed-strategi och hade fällts ut inuti just den containern. Avtalslistan
       har ingen sådan meny, vilket är hela skälet till att dess drawCallback kunde
       sätta overflow-x i åratal utan att någon märkte något.

       Inget !important behövs: sedan den drawCallbacken tagits bort finns ingen
       inline-stil att slå, och samma specificitet i en senare fil räcker. */
    #zero-conf_wrapper,
    #invoice-datatable_wrapper,
    #invitedUserstbl_wrapper {
        overflow-x: visible;
    }

    table.bb-table-fixed > colgroup,
    table.bb-table-fixed > thead {
        display: none;
    }

    table.bb-table-fixed,
    table.bb-table-fixed > tbody,
    table.bb-table-fixed > tbody > tr,
    table.bb-table-fixed > tbody > tr > td {
        display: block;
        width: 100%;
    }

    /* Kortet. Elevation i stället för radlinjer — samma språk som resten av fas 1. */
    table.bb-table-fixed > tbody > tr {
        margin-bottom: var(--bb-space-3);
        padding: var(--bb-space-3) var(--bb-space-4);
        border-radius: .75rem;
        background: var(--bb-surface);
        box-shadow: var(--bb-shadow-sm);
        border: 1px solid var(--bb-border);
    }

    /* Cellen: etikett vänster, värde höger. Etiketten kommer ur bb-cell-label; en cell utan
       attribut (åtgärdscellen, tomtillståndets colspan-cell) får ingen. */
    table.bb-table-fixed > tbody > tr > td {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: var(--bb-space-4);
        padding: var(--bb-space-2) 0;
        /* !important: datatables.css ritar radlinjen som "table.dataTable td { border-top }" /
           tbody-linjer på högre specificitet än den här selektorn, och i kortläget blir varje
           sådan linje ett streck tvärs över kortet. Kortets egen border står för avgränsningen. */
        border: 0 !important;
        /* Beloppen är text-end i tabelläget; i kortet styr flexboxen högerkanten och
           text-align skulle bara flytta texten inuti sin egen box. */
        text-align: left;
    }

    table.bb-table-fixed > tbody > tr > td[bb-cell-label]::before {
        content: attr(bb-cell-label);
        flex: 0 0 auto;
        font-size: var(--bb-text-xs);
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: .02em;
        color: var(--bb-text-muted);
    }

    /* Projektnamnet är kortets rubrik: ingen etikett, egen vikt, egen rad. */
    table.bb-table-fixed > tbody > tr > td:first-child {
        display: block;
        font-weight: 600;
        font-size: var(--bb-text-base);
        padding-top: 0;
        padding-bottom: var(--bb-space-2);
        /* !important för att vinna över border: 0 !important ovan — den nollställningen gäller
           alla celler, den här raden är kortets rubrikavdelare och ska stå kvar. */
        border-bottom: 1px solid var(--bb-border) !important;
        margin-bottom: var(--bb-space-2);
    }

    table.bb-table-fixed > tbody > tr > td:first-child::before {
        content: none;
    }

    /* Åtgärdscellen sist: full bredd, knappen högerställd och kvar över 44 px. */
    table.bb-table-fixed > tbody > tr > td.bb-row-actions-cell {
        justify-content: flex-end;
        padding-top: var(--bb-space-2);
    }

    /* Fakturerat-stapeln behöver hela bredden under sin etikett, inte 50 % bredvid den. */
    table.bb-table-fixed > tbody > tr > td.bb-cell-progress {
        display: block;
    }

    /* Tomtillståndet. Som kort finns ingen scrollyta kvar, så sticky-centreringen
       (lärdom 5) behövs inte här — men den får heller inte ligga kvar och förskjuta
       meddelandet i en yta som inte längre scrollar. */
    table.bb-table-fixed > tbody > tr.bb-empty-row {
        background: transparent;
        box-shadow: none;
        border: 0;
        padding: 0;
    }

    table.bb-table-fixed > tbody > tr.bb-empty-row > td {
        display: block;
        text-align: center;
        /* Tomtillståndets enda cell är också :first-child och fick annars kortrubrikens
           avdelarlinje — ett streck tvärs över ett meddelande utan något att avdela. */
        border-bottom: 0 !important;
        margin-bottom: 0;
        font-weight: 400;
    }

    table.bb-table-fixed > tbody > tr.bb-empty-row > td .bb-empty {
        position: static;
        left: auto;
        transform: none;
    }

    /* Summeringstabellen på Skapa faktura bär style="width: 50%" i markupen. På en telefon blir
       det 154 px för sex belopp, alltså scroll för innehåll som annars hade fått plats. !important
       krävs för att slå inline-stilen; regeln ligger inne i mobil-media-queryn så desktop behåller
       sina 50 %. */
    .table-responsive:has(> #summaryTable) {
        width: 100% !important;
    }

    /* Summeringstabellen är två kolumner — etikett och belopp — och får plats på 375 px så
       fort den slutar mäta sig efter innehållets naturliga bredd. Utan det här stod en
       horisontell scrollbar under den för ett tiotal pixlar. */
    #summaryTable {
        min-width: 0 !important;
        width: 100% !important;
    }

    /* ---- bb-cards-sm: en vanlig tabell som blir kort under brytpunkten ----
       Samma idé som listorna ovan, men för tabeller som inte är .bb-table-fixed och inte körs
       av DataTables. Först använd av pristabellen på Skapa faktura: fyra kolumner mot en lång
       radrubrik får inte plats på 375 px, och horisontell scroll är fel svar när talen är där
       just för att JÄMFÖRAS — man kan inte jämföra det man inte ser samtidigt. */
    table.bb-cards-sm {
        min-width: 0 !important;
        width: 100% !important;
        table-layout: auto;
    }

    .table-responsive:has(> table.bb-cards-sm) {
        overflow-x: visible;
    }

    table.bb-cards-sm > thead {
        display: none;
    }

    table.bb-cards-sm,
    table.bb-cards-sm > tbody,
    table.bb-cards-sm > tbody > tr,
    table.bb-cards-sm > tbody > tr > td {
        display: block;
        width: 100%;
    }

    table.bb-cards-sm > tbody > tr {
        margin-bottom: var(--bb-space-3);
        padding: var(--bb-space-3) var(--bb-space-4);
        border-radius: .75rem;
        background: var(--bb-surface);
        border: 1px solid var(--bb-border);
    }

    table.bb-cards-sm > tbody > tr > td {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: var(--bb-space-4);
        padding: var(--bb-space-2) 0;
        border: 0 !important;
        text-align: left;
        font-variant-numeric: tabular-nums;
        /* Bootstrap ger varje cell sin egen --bs-table-bg (i mörkt läge #212529, som INTE är
           --bb-surface). Cellerna målade då över kortets yta och kortet såg ut att komma från
           en annan palett än resten av sidan. Transparent låter <tr>:s ytfärg synas igenom. */
        background-color: transparent;
        --bs-table-bg: transparent;
        --bs-table-accent-bg: transparent;
    }

    table.bb-cards-sm > tbody > tr > td[bb-cell-label]::before {
        content: attr(bb-cell-label);
        flex: 0 0 auto;
        font-size: var(--bb-text-xs);
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: .02em;
        color: var(--bb-text-muted);
    }

    /* Radrubriken (första cellen) är kortets rubrik — ingen etikett, egen vikt, egen rad. */
    table.bb-cards-sm > tbody > tr > td:first-child {
        display: block;
        font-weight: 600;
        padding-top: 0;
        padding-bottom: var(--bb-space-2);
        border-bottom: 1px solid var(--bb-border) !important;
        margin-bottom: var(--bb-space-2);
    }

    /* En cell som innehåller ett inmatningsfält är inte ett etikett/värde-par utan ett
       formulärfält: etiketten ska stå ÖVER fältet och fältet ta hela bredden, precis som
       resten av formuläret. Gäller produktraden på Skapa faktura.

       Regeln står EFTER :first-child-regeln ovan med flit: den första cellen i en produktrad
       är Beskrivning-fältet, och kortrubrikens avdelarlinje ritade då ett streck under ett
       inmatningsfält — det såg ut som ett fel, inte som en rubrik. Ett fält är aldrig en
       kortrubrik. */
    table.bb-cards-sm > tbody > tr > td:has(input, select, textarea),
    table.bb-cards-sm > tbody > tr > td:first-child:has(input, select, textarea) {
        display: block;
        font-weight: 400;
        border-bottom: 0 !important;
        margin-bottom: 0;
        padding-bottom: var(--bb-space-2);
    }

    table.bb-cards-sm > tbody > tr > td:has(input, select, textarea)::before {
        display: block;
        margin-bottom: var(--bb-space-1);
    }

    table.bb-cards-sm > tbody > tr > td .form-control,
    table.bb-cards-sm > tbody > tr > td .form-select,
    table.bb-cards-sm > tbody > tr > td select {
        width: 100%;
    }

    /* Åtgärdscellen (Ta bort) sist: egen rad, knappen högerställd och avskild med en linje.
       Knappen är .btn-primary i markupen, alltså grön — i en tabellcell på desktop är den ett
       litet inslag, men i ett kort blir den det mest framträdande i hela kortet. Att ta bort
       en rad ska inte se ut som kortets huvudåtgärd. Dämpad här, oförändrad på desktop. */
    table.bb-cards-sm > tbody > tr > td.bb-cell-action {
        display: flex;
        justify-content: flex-end;
        padding-top: var(--bb-space-3);
        margin-top: var(--bb-space-2);
        border-top: 1px solid var(--bb-border) !important;
    }

    table.bb-cards-sm > tbody > tr > td.bb-cell-action .btn {
        background: transparent;
        border: 1px solid var(--bb-border);
        color: var(--bb-text-muted);
        box-shadow: none;
    }

    table.bb-cards-sm > tbody > tr > td.bb-cell-action .btn:hover,
    table.bb-cards-sm > tbody > tr > td.bb-cell-action .btn:focus-visible {
        background: var(--bb-surface-raised);
        color: var(--bb-text);
    }

    /* ---- Summeringstabellen ----
       Två kolumner där etiketten bär .text-nowrap: på 375 px tvingade den tabellen bredare än
       skärmen och klämde ihop beloppskolumnen. En summering är en etikett/värde-lista, så den
       renderas som en sådan: raden är en flexrad, cellramarna bort, en tunn linje mellan
       raderna och slutsumman avskild och tyngre. */
    #summaryTable,
    #summaryTable > tbody,
    #summaryTable > tbody > tr,
    #summaryTable > tbody > tr > td {
        display: block;
        width: 100%;
    }

    #summaryTable > tbody > tr {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: var(--bb-space-4);
        padding: var(--bb-space-2) 0;
        border-bottom: 1px solid var(--bb-border);
    }

    #summaryTable > tbody > tr > td {
        width: auto;
        padding: 0;
        border: 0 !important;
        background-color: transparent;
        --bs-table-bg: transparent;
        --bs-table-accent-bg: transparent;
        /* Etiketten måste få radbryta — det var text-nowrap som gjorde tabellen för bred. */
        white-space: normal !important;
    }

    #summaryTable > tbody > tr > td:last-child {
        flex: 0 0 auto;
        white-space: nowrap !important;
        font-variant-numeric: tabular-nums;
    }

    /* "Att betala" är radens enda slutsats och ska gå att hitta utan att läsa de fem ovanför. */
    #summaryTable > tbody > tr.fw-bold {
        margin-top: var(--bb-space-2);
        padding-top: var(--bb-space-3);
        border-top: 2px solid var(--bb-border);
        border-bottom: 0;
        font-size: var(--bb-text-base);
    }

    /* Korten sitter i en .card-body som redan har 1,5 rem padding — två inramningar i rad
       äter ~48 px av 375 och gör det yttre kortet till ren dekoration. */
    .card > .card-body {
        padding: var(--bb-space-3);
    }

    /* DataTables längdväljare är ett fritt växande fält i en float-layout och tar halva
       raden på en telefon. */
    .dataTables_wrapper .dataTables_length select {
        width: auto;
        min-width: 5rem;
        min-height: var(--bb-touch-target);
    }

    /* "Visa [10] poster" är en <label> med fältet inuti; utan flex sträcker fältet sig och
       trycker ut ordet "poster" utanför skärmkanten. */
    .dataTables_wrapper .dataTables_length label {
        display: flex;
        align-items: center;
        gap: var(--bb-space-2);
        margin-bottom: var(--bb-space-3);
    }

    /* DataTables lägger sina egna kontroller i en float-layout som inte får plats. */
    .dataTables_wrapper .dataTables_length,
    .dataTables_wrapper .dataTables_filter,
    .dataTables_wrapper .dataTables_info,
    .dataTables_wrapper .dataTables_paginate {
        float: none;
        text-align: left;
    }
}

/* ---- Sidhuvud, modalfot och sidknappar på små skärmar ---- */

@media (max-width: 575.98px) {
    .bb-page-header-row {
        flex-direction: column;
        align-items: stretch;
        gap: var(--bb-space-3);
    }

    .bb-page-actions .btn {
        width: 100%;
        min-height: var(--bb-touch-target);
    }

    /* Modalfoten är justify-content-between med en knappgrupp till höger. På 375 px radbryter
       gruppen så att den sista (och viktigaste) knappen står ensam, högerställd, medan de andra
       ligger kvar på raden ovanför — en trappa. Staplade fullbreddsknappar ger i stället en
       förutsägbar ordning och en träffyta över hela bredden. */
    .modal-footer {
        flex-direction: column;
        align-items: stretch;
    }

    .modal-footer > div {
        display: flex;
        flex-direction: column;
        align-items: stretch;
        width: 100%;
    }

    .modal-footer .btn {
        width: 100%;
    }

    /* "Se avtal"/"Se faktura" är en textlänk, inte en knapp — den ska inte bli ett fält. */
    .modal-footer .btn-link {
        width: auto;
        align-self: flex-start;
    }
}

/* ==================== Sammanfattning på Skapa avtal (fas 10, #212) ==================== */

/* Formuläret är sju kort på höjden. Utan panelen är enda sättet att veta om man är klar att
   scrolla tillbaka till toppen och leta tomma fält, eller att klicka Skicka och läsa felet —
   det senare är precis den upplevelse #145 och #154 handlade om.

   Bara ModernUI: markupen renderas inte alls med flaggan av, så de här reglerna har inget att
   träffa där. */

.bb-form-summary {
    position: sticky;
    /* Sidhuvudet är fixerat. Utan offset klistrar panelen fast UNDER det och toppen kapas. */
    top: calc(91px + var(--bb-space-4));
    min-width: 260px;
    max-width: 320px;
    padding: var(--bb-space-4);
    border: 1px solid var(--bb-border);
    border-radius: var(--bb-radius);
    background: var(--bb-surface);
}

.bb-form-summary-title {
    margin-bottom: var(--bb-space-3);
    font-size: var(--bb-text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--bb-text-muted);
}

.bb-form-summary-amount {
    display: flex;
    flex-direction: column;
    gap: var(--bb-space-1);
    padding-bottom: var(--bb-space-3);
    margin-bottom: var(--bb-space-3);
    border-bottom: 1px solid var(--bb-border);
}

.bb-form-summary-label {
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
}

/* tabular-nums: siffran ändras vid varje tangenttryckning i prisfälten, och med
   proportionella siffror hoppar hela beloppet i sidled medan man skriver. */
.bb-form-summary-value {
    font-size: var(--bb-text-xl);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.bb-form-summary-status {
    margin-bottom: var(--bb-space-3);
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
}

.bb-form-summary-status.is-complete {
    color: var(--bb-success-text, var(--bb-text));
}

.bb-form-summary-jump {
    width: 100%;
}

/* Formulärkolumnen på Skapa faktura måste få krympa. En flexrad ger sina barn min-width:auto,
   alltså "aldrig smalare än ditt innehåll" — och innehållet här är produktlistan med sitt
   pixelgolv på 1040 px. Kolumnen la därför beslag på hela raden och tryckte ned panelen på en
   egen rad även på en bred skärm, trots att col-xl-auto satt bredvid i markupen.
   min-width: 0 låter .table-responsive göra sitt jobb i stället: tabellen scrollar i sidled
   inuti sin egen ruta, som den redan var byggd för. Skapa avtal behövde inte det här — den har
   ingen tabell med golv. */
.row.invoice-create > .col-xl {
    min-width: 0;
}

/* Under 1200 px lägger Bootstraps col-xl panelen på egen rad. Sticky vore fel där: den hade
   ätit skärmhöjd på en telefon utan att visa något användaren inte kan scrolla till på ett
   ögonblick. Den blir en ingress i stället — och flyttas före formuläret med order, så den
   syns när sidan öppnas och inte efter sju kort. */
@media (max-width: 1199.98px) {
    .bb-form-summary {
        position: static;
        max-width: none;
    }

    .row.project-contract > aside,
    .row.invoice-create > aside {
        order: -1;
        margin-bottom: var(--bb-space-4);
    }
}

/* ==================== Dropzon för bilagor (fas 10, #212) ==================== */

/* Rutan finns redan (.bb-upload-card i theme.css). Det som saknades var att den syns ta emot
   något. Bara ModernUI: bb-dropzone renderas bakom flaggan, så utan den finns varken
   attributet eller de här reglerna att träffa. */

.bb-upload-hint {
    margin-top: var(--bb-space-3);
    margin-bottom: 0;
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
}

/* Streckad kant redan i viloläge: en heldragen ruta ser ut som vilket kort som helst, och
   streckad är den vedertagna signalen för "släpp här". */
.card.bb-upload-card[bb-dropzone] {
    border-style: dashed;
    transition: border-color .12s ease, background-color .12s ease;
}

.card.bb-upload-card[bb-dropzone].is-dragover {
    border-color: var(--bb-accent, var(--bs-primary));
    background-color: var(--bb-surface-hover, var(--bs-tertiary-bg));
}

@media (prefers-reduced-motion: reduce) {
    .card.bb-upload-card[bb-dropzone] {
        transition: none;
    }
}

/* ==================== BankID-dialogen på betalsidan (fas 11, #214) ==================== */

/* Betalsidan är den enda ytan där en främmande människa möter oss, och den enda vi inte hade
   rört. Panelen bakom BankID-dialogen var en hårdkodad ljusgrön platta (#cdedaa) som inte
   finns någon annanstans i produkten och som inte hör ihop med vare sig ljust eller mörkt tema.
   Reglerna nedan ligger i den flaggade filen: med ModernUI av står den gröna plattan kvar
   precis som idag (payment.css). */

/* .pages-checkout framför, och inte bara .bb-bankid-panel: payment.css laddas via
   @RenderSection("Styles") i vyn, alltså EFTER theme-modern.css i _PublicLayout. Vid samma
   specificitet vinner den som står sist, så en naken klass här hade förlorat mot payment.css.
   Kroppsklassen ger 0,2,0 mot dess 0,1,0 — och det är fortfarande inget !important, till
   skillnad från resten av betalsidans strider med connect.min.css. */
.pages-checkout .bb-bankid-panel {
    background: var(--bb-surface-raised);
    border: 1px solid var(--bb-border);
    border-radius: var(--bb-radius-lg);
    margin: 2px;
    padding: var(--bb-space-4);
    color: var(--bb-text);
}

/* Medgivandekortet stod vitt med svart text i BÅDA temalägena, med den uttryckliga
   motiveringen att panelen bakom det ändå var ljusgrön i båda. Nu när panelen följer temat
   gäller inte det längre — ett vitt block i mörkt läge hade lyst som en ficklampa. Texten är
   juridiskt fixerad och ändras inte; det är bara färgerna som byts. */
.bb-bankid-panel .mandate-terms {
    background: var(--bb-surface);
    color: var(--bb-text);
    border: 1px solid var(--bb-border);
    border-radius: var(--bb-radius-lg);
}

.bb-bankid-panel .mandate-terms h6,
.bb-bankid-panel .mandate-terms strong {
    color: var(--bb-text);
}

/* Brödtexten och mottagarraden måste pekas ut var för sig. Regeln ovan sätter färgen
   på KORTET, men payment.css har egna regler för just de här två — `.mandate-terms p`
   (#333) och `.mandate-terms-payee` (#111) — och en egen regel slår alltid arv, hur
   specifik containerns regel än är.

   Följden var att medgivandetexten blev #333 mot kortets mörka rgb(39,43,51):
   uppmätt 1,12:1 över tolv stycken. Det är autogiromedgivandet — den text beställaren
   ska läsa innan hen signerar bort pengar — och den var i praktiken osynlig i mörkt
   läge. Med flaggan AV står kortet kvar vitt och #333 ger 12,6:1, så den vägen är
   oförändrad; det här gäller bara där theme-modern.css gjort kortet mörkt. */
.bb-bankid-panel .mandate-terms p,
.bb-bankid-panel .mandate-terms-payee {
    color: var(--bb-text);
}

/* Utfällaren målade sig ljus (#f4f6fa med #111) och blev en vit platta mitt i det
   mörka kortet. Läsbar, men den såg ut som ett hål i ytan. */
.bb-bankid-panel .mandate-terms summary {
    background: var(--bb-surface-raised);
    color: var(--bb-text);
    border-color: var(--bb-border);
}

.bb-bankid-panel .mandate-terms summary:hover,
.bb-bankid-panel .mandate-terms summary:focus-visible {
    background: var(--bb-surface);
}

.bb-bankid-panel .mandate-terms-summary-hint {
    color: var(--bb-text-muted);
}

/* payment.css mörkar länkarna på betalsidan, eftersom dess ytor står kvar ljusa med
   flaggan av. Här är ytorna tokenbaserade och alltså mörka, och då blir samma mörka
   länkfärg 1,1:1.

   Värdet sätts som VARIABEL och inte som en egen color-regel: payment.css laddas via
   @section Styles, alltså efter den här filen, så en regel med samma selektor skulle
   förlora oavsett hur specifik den är. Variabeln ärvs ned och läses av payment.css där
   färgen faktiskt sätts. */
[data-bs-theme="dark"] .pages-checkout {
    --bb-payment-link: var(--bb-accent);
    --bb-payment-link-hover: var(--bb-accent);
}

.bb-bankid-scantext {
    font-weight: 600;
    color: var(--bb-text);
}

/* Snurran satt med float:right och margin-left:90% — alltså "nästan längst till höger" oavsett
   hur bred rutan var. På en telefon hamnade den halvvägs utanför rutan. Flex i stället: texten
   till vänster, snurran vid kanten, båda kvar på skärmen. */
.bb-bankid-wait {
    display: flex;
    align-items: center;
    gap: var(--bb-space-3);
    padding: var(--bb-space-3);
    color: var(--bb-text);
}

.bb-bankid-wait .bb-bankid-wait-spinner {
    float: none;
    margin-left: auto;
    flex: 0 0 auto;
    width: 1.5rem;
    height: 1.5rem;
}

/* Enhetsvalen är knappar nu och inte div:ar. De ligger i en .row, så utan bredd står de sida
   vid sida och bryter texten mitt i "samma enhet". Full bredd, staplade — det är två likvärdiga
   val, inte en primär och en sekundär. */
.bb-bankid-device {
    width: 100%;
}

/* Färgen är Bootstraps standardblå (#0d6efd), inte vald — knapparna är .btn-primary och ingen
   har sagt något annat. Den är också inte BankID:s blå, så den läser varken som vår produkt
   eller som BankID; den läser som ett formulär. Samma accent som sidans övriga knappar, vilket
   dessutom gör de två enhetsvalen jämbördiga med varandra i stället för att båda skrika.

   Bara bakom ModernUI: med flaggan av är knapparna blå precis som idag. */
body.pages-checkout .bb-bankid-device.btn-primary {
    background-color: var(--bb-accent);
    border-color: var(--bb-accent);
    color: var(--bb-accent-contrast);
    border-radius: var(--bb-radius);
}

/* ==================== Betalsidans yta (fas 11, #214) ==================== */

/* Sidan låg utanför hela översynen och syntes: lila sidbakgrund (#9485a2), ljusblå kortram
   (#e8eaf6) och Poppins pålagt med en universalselektor. Ingen av färgerna finns någon
   annanstans i produkten, och typsnittet är inte vårt. Allt nedan ligger bakom ModernUI — med
   flaggan av ser betalsidan ut precis som idag (payment.css). */

/* Universalselektorn i payment.css (* { font-family: Poppins }) träffar varje element på sidan
   och vinner mot theme.css genom att laddas senare. Här vinner vi tillbaka med samma
   universalselektor under kroppsklassen: 0,1,0 mot 0,0,0. Ordbilden och BankID-loggan är
   bilder och påverkas inte. */
/* :not(...) — annars slår regeln ut ikontypsnitten. Font Awesome ritar sina ikoner med en
   egen font-family och ett tecken i privatanvändningsområdet; byter man typsnitt på just de
   elementen blir varje ikon en tom ruta. Det var precis vad som hände i första försöket. */
.pages-checkout *:not([class*="fa-"]):not([class*="bi-"]) {
    font-family: 'Lato', system-ui, -apple-system, sans-serif;
}

.pages-checkout h1,
.pages-checkout h2,
.pages-checkout h3,
.pages-checkout h4,
.pages-checkout h5,
.pages-checkout h6 {
    font-family: 'Montserrat', 'Lato', system-ui, sans-serif;
}

/* html[data-bs-theme] framför: payment.css har både body.pages-checkout (0,1,1) och
   [data-bs-theme="dark"] body.pages-checkout (0,2,1), och laddas senare. 0,2,2 vinner över
   båda i båda temalägena. */
html[data-bs-theme] body.pages-checkout {
    background: var(--bb-surface-raised);
}

/* Avsändaren överst. Sidan bad om ett autogiromedgivande utan att säga vem som frågar. */
.bb-checkout-header {
    display: flex;
    justify-content: center;
    padding: var(--bb-space-5) var(--bb-space-4) 0;
}

.bb-checkout-header .bb-public-logo {
    max-width: min(100%, 13rem);
    height: auto;
}

/* Kortramen var en ljusblå platta runt de vita korten. Med tokens blir den ytan bakom korten,
   och korten själva får kant i stället för att förlita sig på färgkontrasten mot lila. */
/* 900 px var rimligt när sidan var två kolumner. Med beställarkortet inflyttat i rutan är den
   en kolumn, och en 900 px bred textremsa med ett femtontal rader i är svårläst — ögat tappar
   raden på vägen tillbaka. 560 px är ungefär den bredd innehållet faktiskt hade förut. */
.pages-checkout .container-checkout {
    background-color: transparent;
    padding: var(--bb-space-4);
    max-width: 560px;
}

.pages-checkout .box-right,
.pages-checkout .box-right-newpay,
.pages-checkout .box-left {
    background-color: var(--bb-surface);
    border: 1px solid var(--bb-border);
    border-radius: var(--bb-radius-lg);
    color: var(--bb-text);
}

/* Payment.css tvingar mörk text på sina ljusa ytor i mörkt läge (#214) — korten där är
   hårdkodat vita, och theme.css:s ljusa mörkerlägestext blev osynlig på dem. Här är korten
   INTE vita, de är --bb-surface, som är mörk i mörkt läge. Utan den här återställningen hade
   den regeln lagt #212529 på en mörk platta, alltså gjort exakt det den skulle laga.

   html[data-bs-theme] framför: payment.css laddas efter det här arket, och dess selektor ligger
   på 0,3,0. 0,3,1 vinner. Samma grepp som för kroppsbakgrunden längre upp i filen. */
html[data-bs-theme] .pages-checkout .container-checkout,
html[data-bs-theme] .pages-checkout .box-right,
html[data-bs-theme] .pages-checkout .box-right-newpay,
html[data-bs-theme] .pages-checkout .box-left,
html[data-bs-theme] .pages-checkout .bg-blue,
html[data-bs-theme] .pages-checkout .bb-bankid-panel,
html[data-bs-theme] .pages-checkout .bb-bankid-panel :is(h1, h2, h3, h4, h5, h6),
html[data-bs-theme] .pages-checkout .container-checkout :is(h1, h2, h3, h4, h5, h6) {
    color: var(--bb-text);
}

/* Sidfoten på betalsidan, tillbaka på tokenen (#224).

   payment.css sätter den till ett fast mörkt värde, och det är RÄTT med den här flaggan AV:
   sidan är då en fast lila (#9485a2) i båda temana, och mot den mellantonen klarar bara mörk
   text kravet — vitt ger 3,42:1.

   Med flaggan PÅ är sidan inte lila. Den här filen gör den temamedveten, och ytan blir appens
   egen — rgb(50,55,66) i mörkt läge. Där blir samma mörka värde 1,50:1, alltså precis det fel
   det skulle lösa, med omvänt förtecken.

   html[data-bs-theme] framför av samma skäl som blocket ovan: payment.css laddas via
   @@RenderSection("Styles") i vyn, alltså EFTER den här filen, så en regel med samma
   specificitet förlorar. 0,3,1 mot payment.css 0,3,0. */
html[data-bs-theme] .pages-checkout .bb-footer-row .footer-text,
html[data-bs-theme] .pages-checkout .bb-footer-row a {
    color: var(--bb-text-muted);
}

html[data-bs-theme] .pages-checkout .bb-footer-row a:hover,
html[data-bs-theme] .pages-checkout .bb-footer-row a:focus-visible {
    color: var(--bb-text);
}

html[data-bs-theme] .pages-checkout .container-checkout .form-control,
html[data-bs-theme] .pages-checkout .container-checkout .form-control[readonly],
html[data-bs-theme] .pages-checkout .container-checkout input[readonly] {
    background-color: var(--bb-surface-raised) !important;
    color: var(--bb-text) !important;
    -webkit-text-fill-color: var(--bb-text) !important;
    border-color: var(--bb-border) !important;
}

.pages-checkout .textmuted,
.pages-checkout .p-grey {
    color: var(--bb-text-muted);
}

/* Bekräftelserutan och kontoraden var #e3f2fd — ljusblått på vitt, osynligt i mörkt läge. */
.pages-checkout .bg-blue {
    background-color: var(--bb-surface-raised);
    color: var(--bb-text);
    border-radius: var(--bb-radius);
}

/* Betalsättsknapparna är AVSIKTLIGT orörda här. Färgerna (svart platta, vit text) skrivs som
   inline-stilar av payment.js ResetPaymentModes/SetPaymentMethod, och en inline-stil går inte
   att nå från en stilmall. Ett försök att byta dem mot klasser gjordes och backades: klassen
   satt, regeln matchade och var mer specifik med !important — och knappen förblev ändå svart,
   alltså något jag inte förstod. Att gissa i betalflödets tillståndsvisning är fel ställe att
   gissa på. Knapparna hör hemma i steget där payment.js DOM-lager bryts ut och testas. */

/* Avsändarrutan (fas 11, #214). Definitionslista och inte en tabell: det är fyra till sex
   etikett/värde-par, inte data att jämföra. dl ger dessutom kopplingen mellan etikett och värde
   åt hjälpmedel utan att markupen behöver aria. */
.bb-payment-about-title {
    font-size: var(--bb-text-lg);
    font-weight: 700;
    margin-bottom: var(--bb-space-3);
    color: var(--bb-text);
}

.bb-payment-about-list {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--bb-space-2) var(--bb-space-4);
    margin: 0 0 var(--bb-space-3);
}

.bb-payment-about-list dt {
    color: var(--bb-text-muted);
    font-size: var(--bb-text-sm);
    font-weight: 400;
}

.bb-payment-about-list dd {
    margin: 0;
    color: var(--bb-text);
    font-weight: 600;
    /* Belopp och datum ska stå i rak kolumn när ögat vandrar nedför listan. */
    font-variant-numeric: tabular-nums;
}

.bb-payment-about-note {
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
    line-height: 1.5;
}

/* Under 480 px blir två kolumner för trångt för "Betalningsmottagare" — etiketten bryts mitt i
   ordet och värdet hamnar på en egen rad ändå. Staplat är ärligare där. */
@media (max-width: 479.98px) {
    .bb-payment-about-list {
        grid-template-columns: 1fr;
        gap: 0;
    }

    .bb-payment-about-list dd {
        margin-bottom: var(--bb-space-3);
    }
}

/* Datumväljaren (flatpickr, #319 — tidigare jQuery UI). Den ärver inget av vårt tema, och på
   betalsidan är intervallet snävt — tidigast MinDate, senast förfallodatum — så nästan varje dag i
   månaden är utgråad. Det såg ut som en trasig kalender snarare än en begränsad. Kontrasten mellan
   valbar och inte valbar är det enda som verkligen behöver bli tydligt.

   Selektorerna är oscopeade med flit: flatpickr flyttar sin kalender till <body>, alltså utanför
   både .pages-checkout och formulärets träd.

   Noten som stod här om att dagarna är <a>-element och därför behövde !important mot theme.css
   (`a { color: var(--bb-accent) !important }`) gäller INTE längre: flatpickr ritar sina dagar som
   <span class="flatpickr-day">. Kontrollerat, inte antaget — det är därför inget !important
   behövs nedan. Skulle någon byta bibliotek igen är det första att kolla. */
.flatpickr-calendar {
    border: 1px solid var(--bb-border);
    border-radius: var(--bb-radius-lg);
    background: var(--bb-surface);
    box-shadow: var(--bb-card-shadow);
    padding: var(--bb-space-2);
    width: auto;
}

/* Pilen som pekar mot fältet ritas av biblioteket i hårdkodat vitt, alltså en ljus tunga mot mörk
   bakgrund i mörkt läge. */
.flatpickr-calendar.arrowTop:before,
.flatpickr-calendar.arrowBottom:before {
    border-bottom-color: var(--bb-border);
    border-top-color: var(--bb-border);
}

.flatpickr-calendar.arrowTop:after,
.flatpickr-calendar.arrowBottom:after {
    border-bottom-color: var(--bb-surface);
    border-top-color: var(--bb-surface);
}

.flatpickr-months,
.flatpickr-month,
.flatpickr-current-month,
.flatpickr-weekdays,
.flatpickr-days,
.flatpickr-weekdaycontainer,
.dayContainer {
    background: transparent;
}

.flatpickr-months .flatpickr-month,
.flatpickr-current-month input.cur-year,
.flatpickr-current-month .flatpickr-monthDropdown-months {
    color: var(--bb-text);
    fill: var(--bb-text);
    font-weight: 700;
}

/* Månadsmenyn är en riktig <select>; utan bakgrund ärver dess lista systemets vita. */
.flatpickr-current-month .flatpickr-monthDropdown-months,
.flatpickr-current-month .flatpickr-monthDropdown-months .flatpickr-monthDropdown-month {
    background: var(--bb-surface);
}

/* Månadspilarna ritar flatpickr som SVG som följer fill, inte som en gråsprite med en bortgömd
   etikett bredvid. Det var just den etiketten som blev den bokstavliga texten
   "FöregåendeNästaaugusti 2026" i jQuery UI-kalendern (#319). */
.flatpickr-months .flatpickr-prev-month,
.flatpickr-months .flatpickr-next-month {
    fill: var(--bb-text);
    color: var(--bb-text);
}

.flatpickr-months .flatpickr-prev-month:hover svg,
.flatpickr-months .flatpickr-next-month:hover svg {
    fill: var(--bb-accent);
}

span.flatpickr-weekday {
    color: var(--bb-text-muted);
    font-weight: 600;
}

/* Valbar dag: vår accentfärg som text på en lugn yta. */
.flatpickr-day {
    background: var(--bb-surface-raised);
    border: 1px solid var(--bb-border);
    border-radius: var(--bb-radius);
    color: var(--bb-accent);
    font-weight: 700;
}

/* Utgråad dag: samma yta, dämpad text — synlig, men uppenbart inte klickbar. Gäller både dagar
   utanför min/max (.flatpickr-disabled) och de grå kanterna från intilliggande månader. */
.flatpickr-day.flatpickr-disabled,
.flatpickr-day.flatpickr-disabled:hover,
.flatpickr-day.prevMonthDay,
.flatpickr-day.nextMonthDay {
    background: transparent;
    border-color: transparent;
    color: var(--bb-text-muted);
    font-weight: 400;
    opacity: .55;
}

.flatpickr-day.today {
    border-color: var(--bb-accent);
}

.flatpickr-day:hover,
.flatpickr-day:focus,
.flatpickr-day.selected,
.flatpickr-day.selected:hover,
.flatpickr-day.selected:focus {
    background: var(--bb-accent);
    border-color: var(--bb-accent);
    color: var(--bb-accent-contrast);
}

/* Hovring får inte se ut som ett val på en dag man ändå inte kan välja. */
.flatpickr-day.flatpickr-disabled:hover,
.flatpickr-day.prevMonthDay:hover,
.flatpickr-day.nextMonthDay:hover {
    background: transparent;
    color: var(--bb-text-muted);
    cursor: default;
}

/* Kalenderrubriken på pekskärm (#367).
 *
 * Månadsnamnet och årtalet skars av på mitten. Orsaken är att två korrekta regler drar åt olika
 * håll: träffytegolvet längre upp i den här filen ger varje select och input min-height 44 px
 * (WCAG 2.5.5), och det träffar även flatpickrs egen månadsmeny och årsfält — medan flatpickrs
 * CSS låser rubrikraden vid height: 34px. 44 px innehåll i en 34 px hög box, alltså 17 px bort.
 *
 * Uppmätt: .flatpickr-current-month hade clientHeight 34 och scrollHeight 51. På desktop med mus
 * slår mediavillkoret inte till, min-height blir 0 och rubriken är 25 px — därför syntes felet
 * bara på mobil och touch.
 *
 * Det är den fasta höjden som ger vika, inte träffytan. Att i stället undanta kalendern från
 * träffytegolvet hade krympt månadsmenyn till 25 px på just de enheter där regeln finns för att
 * fingret ska träffa rätt.
 *
 * position: static på rubriken är nödvändigt, inte städning: flatpickr lägger den absolut
 * (width: 75%, left: 12.5%), och ett absolut placerat barn bidrar inte till förälderns höjd — så
 * raden hade fortsatt vara 34 px hög hur mycket innehållet än växte. .flatpickr-month måste med
 * av samma skäl; rubriken sitter i den, inte direkt i .flatpickr-months. */
@media (max-width: 991.98px), (hover: none) {
    .flatpickr-months {
        min-height: var(--bb-touch-target);
        align-items: stretch;
    }

    .flatpickr-months .flatpickr-month {
        height: auto;
        display: flex;
        align-items: center;
    }

    .flatpickr-current-month {
        position: static;
        width: auto;
        left: auto;
        height: auto;
        padding: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        flex: 1 1 auto;
    }

    /* Månadspilarna är 34x34 px hos flatpickr — under de 44 px vi kräver av allt annat man ska
       kunna träffa med ett finger. Att rubriken intill fick sitt golv men pilarna inte hade varit
       att laga halva raden. */
    .flatpickr-months .flatpickr-prev-month,
    .flatpickr-months .flatpickr-next-month {
        min-width: var(--bb-touch-target);
        min-height: var(--bb-touch-target);
        display: flex;
        align-items: center;
        justify-content: center;
    }

    /* Årtalets upp/ner-pilar ligger på opacity: 0 tills man hovrar över fältet. På en pekskärm
       finns ingen hovring, så de gick aldrig att få fram — året kunde bara ändras genom att
       skriva i fältet, vilket ingenting antyder. Synliga direkt i stället. */
    .numInputWrapper span.arrowUp,
    .numInputWrapper span.arrowDown {
        opacity: 1;
    }
}

/* "Hämta bankkontonummer". Svart platta, satt med !important i payment.css
   (.bankIdSignInProcess .btn-primary). Till skillnad från betalsättsknapparna kommer färgen
   från en stilmall och inte från en inline-stil, så den GÅR att nå: samma !important, en klass
   mer i selektorn. Det avstängda läget behåller sin gråtoning, den betyder något — knappen är
   låst tills villkoren bockats i. */
body.pages-checkout .bankIdSignInProcess .btn-primary {
    background-color: var(--bb-accent) !important;
    border-color: var(--bb-accent) !important;
    color: var(--bb-accent-contrast) !important;
    border-radius: var(--bb-radius);
}

body.pages-checkout .bankIdSignInProcess .btn-primary.disabled,
body.pages-checkout .bankIdSignInProcess .btn-primary:disabled {
    background-color: var(--bb-accent) !important;
    border-color: var(--bb-accent) !important;
    opacity: .5 !important;
}

/* Etiketten bredvid kryssrutan är nu ett <label> och alltså klickbar — det ska synas. */
body.pages-checkout label[for="chkAgree"] {
    cursor: pointer;
    padding-left: var(--bb-space-2);
}

/* Beloppet är sidans enda tal som kräver ett beslut. Det ska gå att läsa på armlängds avstånd
   utan att först hitta det bland fem andra siffror. */
.bb-payment-amount-label {
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
    margin-bottom: 0;
}

.bb-payment-amount {
    font-size: var(--bb-text-2xl);
    font-weight: 700;
    color: var(--bb-text);
    font-variant-numeric: tabular-nums;
    margin-bottom: var(--bb-space-4);
    line-height: 1.2;
}

/* Kontraktets siffror är sammanhang, inte uppmaning: de handlar om avtalet och inte om den här
   betalningen. Avskilda med en linje och satta mindre — nåbara för den som undrar, i vägen för
   ingen. */
.bb-payment-context {
    margin-top: var(--bb-space-4);
    padding-top: var(--bb-space-3);
    border-top: 1px solid var(--bb-border);
    margin-bottom: 0;
}

.bb-payment-context dt,
.bb-payment-context dd {
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
    font-weight: 400;
}

/* Betalsättsknapparna (fas 11, #214). Svart platta med vit text mitt bland krämvita kort var
   det som syntes mest av att betalsidan låg utanför översynen. Färgerna kom tidigare som
   inline-stilar ur payment.js och gick inte att nå härifrån; de är nu klasser med samma värden
   i payment.css, så flaggan av ger exakt dagens utseende.

   body.pages-checkout framför: payment.css laddas via @RenderSection("Styles") i vyn, alltså
   EFTER den här filen, och .paymentSchedulProcess .btn-primary ligger där på 0,2,0. */
/* Andra vändan: från två accentplattor till två kort.

   Att måla dem i accentfärgen löste det ursprungliga problemet — de var svarta — men skapade
   ett nytt. Accentfärgen är sidans "gör det här"-signal, och den satt nu på tre element i rad:
   Direktbetalning, Schemalägg betalning och Fortsätta. Två av dem är ett VAL och den tredje är
   en ÅTGÄRD, men de skrek lika högt, och i mörkt läge blev det dessutom två stora ljusgröna
   plattor på en mörk sida.

   Nu bär accenten bara två saker: det man har valt, och det man kan göra härnäst. Valen i vila
   är kort — samma yta, kant och text som resten av sidans kort. */
body.pages-checkout .directpaymentbtn,
body.pages-checkout .scheduledpaymentbtn,
body.pages-checkout .duedatepaymentbtn {
    background-color: var(--bb-surface-raised);
    /* --bb-border och inget annat var första försöket, och det mätte 1,30:1 mot det vita kortet
       bakom i ljust läge. Ytan hjälper inte heller — #f7f5f0 mot #fff är 1,09:1. Kortet syntes
       alltså knappt som ett kort, och WCAG 1.4.11 vill ha 3:1 på det som avgränsar en kontroll.

       Kanten blandas därför fram ur textfärgen mot den egna ytan i stället för att vara en fast
       token: samma uttryck ger en mörk kant i ljust läge och en ljus i mörkt, utan en regel per
       tema. 45 % mätt till 3,05:1 respektive 3,42:1. */
    border: 1px solid color-mix(in srgb, var(--bb-text) 50%, var(--bb-surface-raised));
    color: var(--bb-text);
    border-radius: var(--bb-radius);
}

/* Hovring flyttar bara kanten till accentfärgen. Ytan skiftar knappt — kortet ska svara på
   pekaren utan att se valt ut, för valt är ett tillstånd som överlever att musen flyttas. */
body.pages-checkout .directpaymentbtn:hover,
body.pages-checkout .scheduledpaymentbtn:hover,
body.pages-checkout .duedatepaymentbtn:hover {
    border-color: var(--bb-accent);
    background-color: color-mix(in srgb, var(--bb-accent) 7%, var(--bb-surface-raised));
}

/* Ikonen i accentfärgen: kortet ska ha en kulör utan att hela ytan tar den. */
body.pages-checkout .bb-pay-method-icon {
    color: var(--bb-accent);
}

/* Underraden mot en ljus yta. opacity: .85 i payment.css är rätt MOT ACCENTPLATTAN — där kan
   den inte dämpas med en tokenfärg utan att tappa kontrast. Mot kortet är förhållandet det
   omvända: den dämpade tontokenen är mätt mot just den ytan, opaciteten är inte det. */
body.pages-checkout .bb-pay-method-hint {
    opacity: 1;
    color: var(--bb-text-muted);
}

/* På det VALDA kortet går underraden upp till full textfärg. Toningen i ytan drar kontrasten åt
   var sitt håll i de två temana — den mörknar ytan i ljust läge och ljusnar den i mörkt — och den
   dämpade tonen är mätt mot den otonade ytan, inte mot den här. Uppmätt med tonad yta och dämpad
   ton: 4,58:1 i ljust och 4,23:1 i mörkt, alltså under kravet i det ena.

   Att i stället ta bort toningen hade också fungerat, men den är det som gör att kortet läses som
   valt på en halvmeters håll. Full textfärg på ett kort man just valt är dessutom rimligt: den
   raden är inte längre en dämpad förklaring bland flera, den beskriver det man bestämt sig för. */
body.pages-checkout .bb-pay-selected .bb-pay-method-hint {
    color: var(--bb-text);
}

/* Valt läge: accentkant och en svag accenttoning i ytan.

   Kanten går från 1 till 2 px och paddingen från 11 till 10, så kortet inte hoppar en pixel
   när man väljer det — .paymentSchedulProcess .btn-primary sätter padding: 11px.

   8 % och inte 14: toningen ska räcka för att se skillnad, men texten ligger kvar ovanpå. Vid
   14 % mätte underraden 3,99:1 i ljust läge — accenten är mörk där, så varje procent toning tar
   kontrast från den dämpade tonen. Vid 8 % är den 4,62:1. Att sänka toningen kostar inget: det
   är accentkanten och bocken som bär tillståndet, inte ytan. */
body.pages-checkout .paymentSchedulProcess .btn-primary.bb-pay-selected {
    background-color: color-mix(in srgb, var(--bb-accent) 6%, var(--bb-surface-raised));
    border: 2px solid var(--bb-accent);
    padding: 10px;
    color: var(--bb-text);
}

/* Bocken. Färg är inte ett tillåtet ENDA sätt att bära betydelse (WCAG 1.4.1), och skillnaden
   mellan de två korten skulle annars vara just en kulör — dessutom är grannkortet oftast dolt
   i valt läge, så det finns inget att jämföra med.

   content: "" och en ritad bock, inte tecknet ✓: ett ::after med text läses upp av skärmläsare
   och hade lagt ett lösryckt "bock" sist i knappens namn. Tillståndet går i stället via
   aria-pressed, som payment.js växlar med klassen — det är så en skärmläsare vill höra det.

   margin-left: auto i en flexrad skjuter bocken till kortets bortre kant. */
body.pages-checkout .bb-pay-method.bb-pay-selected::after {
    content: "";
    flex: 0 0 auto;
    align-self: center;
    margin-left: auto;
    width: 7px;
    height: 12px;
    border-right: 2px solid var(--bb-accent);
    border-bottom: 2px solid var(--bb-accent);
    transform: rotate(45deg);
}

/* "Fortsätta" i schemaläggningen var den enda knappen på sidan i Bootstrap-blått — resten är
   sidans accentfärg. Färgen låg som inline-stil och gick inte att nå; den bor nu i
   .bb-pay-continue i payment.css, som laddas efter den här filen, därav body.pages-checkout. */
body.pages-checkout .paymentSchedulProcess .btn-primary.bb-pay-continue {
    background: var(--bb-accent);
    border-color: var(--bb-accent);
    color: var(--bb-accent-contrast);
    border-radius: var(--bb-radius);
}

/* Raden som säger vilket datumintervall som gäller. Den ska läsas som en förklaring till
   kalendern ovanför, inte konkurrera med den. */
.bb-pay-date-hint {
    margin-top: var(--bb-space-2);
    margin-bottom: 0;
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
    line-height: 1.5;
}

/* ==================== Publika ändstationer (fas 11, #214) ==================== */

/* De fyra sidorna centrerade sitt innehåll med .vh-100 + .my-auto. På en låg telefonskärm i
   liggande läge betyder full vyhöjd att innehållet inte får plats och klipps av ovanifrån —
   centrering utan minsta höjd, i stället för exakt höjd, låter sidan växa när den behöver. */
.bb-outcome {
    max-width: 32rem;
    margin: 0 auto;
    padding: var(--bb-space-6) var(--bb-space-4);
    /* Inte 100vh: _PublicLayout lägger en fotnot med villkorslänkarna EFTER innehållet, så
       full vyhöjd här gör sidan 75 px längre än fönstret och ger en scrollist för ingenting.
       Uppmätt: 900 px vy, 975 px sida. min-height med utrymme för foten i stället. */
    min-height: calc(100vh - 5rem);
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    text-align: center;
    color: var(--bb-text);
}

.bb-outcome-logo {
    margin-bottom: var(--bb-space-6);
}

.bb-outcome-logo .bb-public-logo {
    max-width: min(100%, 12rem);
    height: auto;
}

.bb-outcome-icon {
    font-size: 2.5rem;
    line-height: 1;
    margin-bottom: var(--bb-space-4);
}

.bb-outcome--success .bb-outcome-icon {
    color: var(--bb-accent);
}

.bb-outcome--pending .bb-outcome-icon {
    color: var(--bb-text-muted);
}

.bb-outcome--problem .bb-outcome-icon {
    color: var(--bb-danger-text, #b02a37);
}

.bb-outcome-title {
    font-size: var(--bb-text-2xl);
    font-weight: 700;
    margin-bottom: var(--bb-space-3);
}

/* Beskedet är det man läser först och ska kunna läsas ensamt. Det som följer är sammanhang. */
.bb-outcome-lead {
    font-size: var(--bb-text-lg);
    line-height: 1.5;
    margin-bottom: var(--bb-space-4);
}

.bb-outcome-next,
.bb-outcome-action {
    font-size: var(--bb-text-base);
    line-height: 1.6;
    color: var(--bb-text-muted);
    margin-bottom: var(--bb-space-3);
    max-width: 28rem;
}

/* Vägen vidare, när det finns exakt en (#275). Egen klass och inte .bb-outcome-action —
   invoiceProcessing.js letar efter den klassen för att veta om sidan redan har ett
   åtgärdsstycke. Accentytan i stället för Bootstraps blå av samma skäl som BankID-knapparna
   ovan: sidan ska läsa som vår produkt i båda ljuslägena. min-height ger fingret något att
   träffa på telefon. */
.bb-outcome-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: var(--bb-touch-target);
    margin-top: var(--bb-space-3);
    padding: var(--bb-space-2) var(--bb-space-5);
    background: var(--bb-accent);
    border: 1px solid var(--bb-accent);
    border-radius: var(--bb-radius);
    color: var(--bb-accent-contrast);
    font-size: var(--bb-text-base);
    text-decoration: none;
}

.bb-outcome-link:hover,
.bb-outcome-link:focus-visible {
    background: var(--bb-accent-hover);
    border-color: var(--bb-accent-hover);
    color: var(--bb-accent-contrast);
}

/* Samma halo som .btn-primary i theme.css — tangentbordsfokus ska se likadant ut överallt.
   Webbläsarens egen ring ligger kvar ovanpå; den tas inte bort här. */
.bb-outcome-link:focus-visible {
    box-shadow: 0 0 0 .2rem var(--bb-focus-ring);
}

/* ==================== Delad fakturavy: sektionskort (#391 etapp 2) ==================== */

/* Reskontran är inte ett kalkylblad. Före etapp 2 låg allt i EN sorterbar tabell, och den som
   ville veta vad som brådskade fick sortera fram det själv — varje besök, på nytt. Sidan är nu
   delad i det som kräver något och det som redan hänt (docs/design/fakturaytor-mockup.html).

   Färgerna kommer ur --bb-*-tokens, som byter värde under [data-bs-theme="dark"] i theme.css:
   ljust och mörkt tema följer därför med utan egna regler här. */

.bb-invoice-sections {
    display: block;
}

.bb-section-card {
    background: var(--bb-surface);
    border: 1px solid var(--bb-border);
    border-radius: var(--bb-radius);
    box-shadow: var(--bb-shadow-sm);
    margin-bottom: var(--bb-space-4);
    overflow: hidden;
}

/* Åtgärdssektionen bär sin egen kant. Inte en full röd yta: åtta rader mot rosa bakgrund läser som
   ett fel i systemet, inte som en arbetslista. Kanten räcker för att skilja sektionerna åt.

   GRADERAD SEDAN #391 ETAPP 2B, och kanten HÄRLEDS UR RUBRIKENS TONKLASS i stället för ur en egen
   klass på <section>. Det är inte en stilistisk finess utan lösningen på ett swap-problem:

   Varje mutation byter ut <header id="invoice-action-head"> out-of-band, men aldrig <section> —
   att swappa hela kortet skulle riva den <li id="invoice-row-N"> som radswappen i SAMMA svar
   siktar på med hx-target. Satt kanten som en egen klass på kortet skulle den alltså aldrig
   uppdateras: krediterar man den sista förfallna fakturan tonas rubriken ned till varm medan ramen
   står kvar röd runt tre utkast. Med :has() följer kanten rubriken gratis, i den swap som redan
   sker, utan att kontraktet behöver ändras.

   Samma mekanism stänger dessutom det glapp som stod dokumenterat som halvlöst i
   _InvoiceRowWithKpi: upptäcker pollningen en nekad dragning under den gröna kvittensen swappas en
   röd rubrik in, och kortet följer nu med i stället för att stå kvar grönt.

   :not([hidden]) är nödvändigt. Den tomma kön renderar en DOLD rubrik som landningsplats för
   OOB-swappen, och utan undantaget hade den tänt kanten runt själva kvittensen.

   Specificiteten (0,2,0) vinner över .bb-section-card--calm (0,1,0), vilket är precis vad som
   behövs när en tom kö plötsligt får en röd rubrik swappad in i sig.

   BEROENDE PÅ MARKUPEN: barnkombinatorn kräver att <header class="bb-section-head"> förblir DIREKT
   barn till .bb-section-card. Den dagen en sektion får en sökruta och rubriken flyttas ned i en
   .bb-section-head-bar-wrapper (som åtgärdssektionen redan har) slutar regeln matcha, och ramfärgen
   försvinner tyst — inget fel syns, kanten blir bara neutral igen. Flyttas rubriken måste
   selektorn följa med (eller kombinatorn bytas mot en efterkommandeselektor). Gäller båda
   varianterna nedan, --danger och --warn. */
.bb-section-card:has(> .bb-section-head--danger:not([hidden])) {
    border-color: var(--bb-status-danger-fg);
}

/* Varmt läge: det enda som ligger i sektionen är utkast och/eller väntande klarmarkeringar. Båda
   är uppgifter, inte fel — och radernas egna berättelser är redan warn-tonade av samma skäl. Att
   larma rött över tre oskickade utkast gör att rött slutar betyda något den dag det verkligen
   gäller. Samma token som berättelseradernas warn-ton, ingen ny färg. */
.bb-section-card:has(> .bb-section-head--warn:not([hidden])) {
    border-color: var(--bb-status-warning-fg);
}

.bb-section-head--warn .bb-section-head-title {
    color: var(--bb-status-warning-fg);
    font-weight: 600;
}

.bb-section-head {
    display: flex;
    align-items: baseline;
    gap: var(--bb-space-2);
    flex-wrap: wrap;
    padding: var(--bb-space-3) var(--bb-space-4);
    border-bottom: 1px solid var(--bb-border);
    font-size: var(--bb-text-base);
}

/* En tom rubrik renderas dold — den finns bara som landningsplats för OOB-swappen (se
   _InvoiceSectionHead). display:flex ovan skulle annars vinna över webbläsarens
   [hidden]{display:none}, som är en ren typregel. Bootstrap 5 har samma regel i sin reset, men
   den här ytan ska inte vara beroende av att den ligger kvar. */
.bb-section-head[hidden] {
    display: none;
}

.bb-section-head--danger .bb-section-head-title {
    color: var(--bb-status-danger-fg);
    font-weight: 600;
}

/* HETER INTE .bb-section-title. Det namnet är upptaget sedan #224 av formulärrubrikerna
   "Beställare 2" och "Utförare", som theme.css ger versaler, Montserrat och 12 px. Etapp 2
   återanvände namnet och ärvde den typografin utan att någon märkte det förrän rubrikerna stod
   som "ALLA FAKTUROR · 31" på dev. text-transform och font-family sätts därför också uttryckligen
   här: klassnamnet är nytt, men connect.min.css versaliserar gärna spans på egen hand. */
.bb-section-head-title {
    font-weight: 500;
    font-size: var(--bb-text-base);
    font-family: inherit;
    text-transform: none;
    letter-spacing: normal;
    color: var(--bb-text);
}

/* Summeringen ("214 300 kr förfallet · 3 utkast") skjuts ut till höger och är medvetet dämpad:
   den är en fotnot till rubriken, inte en andra rubrik. */
.bb-section-head-summary {
    margin-left: auto;
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
    font-variant-numeric: tabular-nums;
    text-transform: none;
    letter-spacing: normal;
}

/* Kvittensraden: åtgärdssektionen försvinner aldrig, den blir grön. En sektion som försvinner när
   den är tom lämnar frågan obesvarad; den här svarar på den. */
.bb-section-card--calm {
    border-color: var(--bb-status-success-fg);
}

.bb-section-receipt {
    display: flex;
    align-items: center;
    gap: var(--bb-space-2);
    padding: var(--bb-space-3) var(--bb-space-4);
    color: var(--bb-status-success-fg);
    font-size: var(--bb-text-base);
}

.bb-section-receipt-mark {
    font-weight: 700;
}

/* Rubriken och sökrutan på samma rad (#391 etapp 2b). Padding och underkant flyttar hit från
   .bb-section-head, som blir ett rent innehållselement när den ligger i baren — annars hade
   kortet fått två inramade rader ovanpå varandra. */
.bb-section-head-bar {
    display: flex;
    align-items: center;
    gap: var(--bb-space-3);
    flex-wrap: wrap;
    padding: var(--bb-space-2) var(--bb-space-4);
    border-bottom: 1px solid var(--bb-border);
}

.bb-section-head-bar > .bb-section-head {
    padding: 0;
    border-bottom: 0;
    flex: 0 1 auto;
}

/* Sökrutan skjuts ut till höger i rubriken, som i mocken. min-width är brytpunkten under vilken
   flex-wrap lägger den på egen rad i stället för att klämma ihop den till oläslighet. */
.bb-section-search {
    display: flex;
    align-items: center;
    gap: var(--bb-space-3);
    margin-left: auto;
    flex: 1 1 auto;
    min-width: 12rem;
    justify-content: flex-end;
}


/* ==================== Fakturaradens form (#391 etapp 2b) ==================== */

/* Mockens rad, och inget mer än den: nummer och projekt över en berättelserad till vänster,
   beloppet med beställaren under sig till höger, ⋯ längst ut. Etapp 2 behöll kolumntabellen som
   radform — statusbadge, fakturadatum, förfallodatum och beställarkolumn — och underkändes på dev.

   Måtten är mockens (padding 12/16, gap 14, radhöjd av innehållet) uttryckta i --bb-*-tokens, inte
   dess hårdkodade hex och px. Färgerna byter därför värde av sig själva under
   [data-bs-theme="dark"] i theme.css, och mörkt tema behöver inga egna regler här. */

.bb-rows {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Raden är <li> och bär bara ramen. Allt klickbart ligger i barnen — se .bb-row-main. */
.bb-row {
    display: flex;
    align-items: center;
    gap: var(--bb-space-3);
    padding-right: var(--bb-space-4);
    border-bottom: 1px solid var(--bb-border);
    position: relative;
}

.bb-row:last-child {
    border-bottom: 0;
}

/* Radens klickyta ÄR en knapp. Det är därför Enter och mellanslag fungerar utan a11y.js radhack,
   och varför fokusringen är den webbläsaren redan ritar på knappar.

   all: unset hade tagit bort för mycket (display, textriktning, ärvda typsnitt beter sig olika i
   Safari), så knappen nollställs explicit i stället.

   `color: inherit` är den enda av nollställningarna som är kontrastkritisk och inte kosmetisk:
   app.css har en naken `button { background-color: var(--bb-accent); color: var(--bb-accent-contrast) }`
   som annars gör radens text nästan svart på mörkt kort i mörkt tema. Klassen vinner på
   specificitet, men tas raden bort faller texten tillbaka dit — inte till temats textfärg. */
.bb-row-main {
    display: flex;
    align-items: center;
    gap: var(--bb-space-3);
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    padding: var(--bb-space-3) 0 var(--bb-space-3) var(--bb-space-4);
    border: 0;
    background: transparent;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

/* Hover-affordansen som etapp 2 tappade: raden ska se ut som något man kan trycka på. Hela <li>
   tonas, inte bara knappen, så ⋯-ytan inte blir en ljus fyrkant i en tonad rad. */
.bb-row:hover {
    background: var(--bb-surface-raised);
}

/* box-shadow och inte outline: connect.min.css sätter outline: 0 !important brett, och den
   striden är redan förlorad en gång. Skuggan ritas innanför och flyttar ingenting. */
.bb-row-main:focus-visible {
    outline: none;
    box-shadow: inset 0 0 0 2px var(--bb-accent);
    border-radius: var(--bb-radius);
}

.bb-row-body {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;
}

/* Projektraden får aldrig radbryta i två spalter — ett långt projektnamn kapas i stället, och
   hela namnet står i panelen som raden öppnar. */
.bb-row-t {
    font-size: var(--bb-text-base);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Ingen color här med flit. Berättelseradens ton sätts av .bb-narrative--* ovan, och de reglerna
   har samma specificitet som den här — en färg på .bb-row-sub hade vunnit på att stå senare i
   filen och tyst gjort varje rad grå, inklusive de röda som är hela poängen. Varje rad får en ton,
   och neutral/muted är just --bb-text-muted. */
.bb-row-sub {
    font-size: var(--bb-text-sm);
    margin-top: 3px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Beloppskolumnen krymper aldrig: siffrorna är radens andra ärende, och en avkortad krontal är
   värdelös. tabular-nums så kronorna står i lodrät linje mellan raderna. */
.bb-row-amt {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    text-align: right;
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
}

.bb-row-a {
    font-size: var(--bb-text-base);
    font-weight: 600;
}

.bb-row-who {
    font-size: var(--bb-text-xs);
    color: var(--bb-text-muted);
    margin-top: 2px;
    max-width: 12rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Avslutade rader (makulerade/krediterade) dämpas i sin helhet: beloppet är återtaget och raden
   kräver ingenting — den ska gå att läsa men inte konkurrera med raderna som fortfarande handlar
   om pengar på väg (mockens .dim). Klassen sitter på knappen och inte på <li>: rad-swappen byter
   li:ts innerHTML, så en klass på li-noden hade överlevt en swap till ett odämpat läge. */
.bb-row-main--dim .bb-row-t,
.bb-row-main--dim .bb-row-a {
    color: var(--bb-text-muted);
    font-weight: 400;
}

/* CREDITED AND CANCELLED ARE BOTH DIMMED, AND USED TO LOOK IDENTICAL. Same grey, same struck
   amount — but one means "booked, then taken back with a credit note that exists and can be
   opened", and the other means "never became real". Two different facts should not share one
   appearance in a ledger.

   The left edge carries the difference, reusing .bb-row-highlight's inset grammar rather than a
   border, so the row's box model and text alignment are untouched. Credited keeps a visible
   marker because something REMAINS — the credit note is a document, linked from the row.
   Cancelled deliberately gets no marker at all: nothing remains to point at, and its absence is
   the signal.

   The colour is its own token and not a status foreground — see --bb-row-credited-edge in
   theme.css for why the status palette gave the wrong answer here in both themes. */
.bb-row-main--credited {
    box-shadow: inset 3px 0 0 0 var(--bb-row-credited-edge);
}

/* Cancelled dims one step further than the shared rule. It is the only state on this page where
   the row is pure history: it cannot be paid, sent, edited or credited, and no document exists. */
.bb-row-main--cancelled .bb-row-t,
.bb-row-main--cancelled .bb-row-a,
.bb-row-main--cancelled .bb-row-sub {
    opacity: .75;
}

/* Raden en notis pekade ut, ?open=<id> (#189). Samma inset-markering som i tabellvägen. */
.bb-row-highlight {
    box-shadow: inset 3px 0 0 0 var(--bb-accent);
}

/* ⋯-knappens yta. Den är undantagen från radklicket redan strukturellt — den är syskon till
   radknappen, inte barn — så här behövs bara att den inte ärver radens pekare. */
.bb-row-actions-cell {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
    cursor: default;
}

/* ⋯-knappen får mockens mått (34×30, egen kant, samma radie som korten) i stället för att se ut
   som en btn-light som råkat hamna där. Fast storlek: knappen ska stå i lodrät linje mellan
   raderna, och en knapp som växer med sitt ikonpsnitt gör den linjen ojämn. */
.bb-row .bb-row-actions .btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 30px;
    padding: 0;
    border: 1px solid var(--bb-border);
    border-radius: var(--bb-radius);
    background: var(--bb-surface-raised);
    color: var(--bb-text-muted);
}

/* Every interaction state, with !important on background/color (#449): connect.min.css puts
   !important on .btn.btn-light:hover/:active/:focus and .show > .btn-light.dropdown-toggle
   (#f8faf9 / #f0f0f0), so the base override above silently loses exactly while the user hovers
   or has the menu open — in dark mode the button flashed WHITE. Same pitfall as documented in
   the #109 refactor: connect.min.css can only be beaten in these states with !important of our
   own. Tokens, not hexes, so both light and dark theme resolve correctly. [aria-expanded] is
   kept alongside .show for robustness — Bootstrap sets both on the open dropdown. */
.bb-row .bb-row-actions .btn:hover,
.bb-row .bb-row-actions .btn:focus,
.bb-row .bb-row-actions .btn:active,
.bb-row .bb-row-actions .btn.active,
.bb-row .bb-row-actions .btn[aria-expanded="true"],
.bb-row .bb-row-actions .show > .btn.dropdown-toggle {
    background: var(--bb-surface-raised) !important;
    border-color: var(--bb-accent);
    color: var(--bb-accent) !important;
    box-shadow: none;
}

/* ÄTA-badgen står kvar för en ÄTA utan visningsnamn (se _InvoiceRowModern) och ska då inte
   knuffa isär radhöjden. */
.bb-row-ata-badge {
    margin-left: var(--bb-space-2);
    vertical-align: baseline;
}

/* MOBILEN ANVÄNDER SAMMA RADFORM. Det är inte en förenkling utan mockens uttryckliga val
   (mobil-E): en rad som redan är två textkolumner med tre rader text ryms på 375 px, medan de
   staplade etikettkorten etapp 2 ärvde från tabellvägen ("BESTÄLLARE", "FAKTURADATUM",
   "FÖRFALLODATUM", "BELOPP", "STATUS" under varandra) gjorde varje faktura till en halv skärm.

   Det ENDA som staplas är kortrubriken, så sökrutan får full bredd under "Alla fakturor · 107". */
@media (max-width: 767.98px) {
    .bb-section-head-bar {
        flex-direction: column;
        align-items: stretch;
        gap: var(--bb-space-2);
    }

    /* Summeringen ("251 200,00 kr förfallet · 1 nekad dragning") flyttar ner på EGEN RAD under
       rubriken och ställs vänster. På desktop skjuts den ut till höger av margin-left:auto och det
       är rätt där — men vid 375 px får den inte plats bredvid "Kräver åtgärd · 6", och flex-wrap
       lade den då i en obekväm mittposition: varken under rubriken eller i kant med den.

       Regeln sitter på .bb-section-head och inte på åtgärdssektionen ensam, så åtgärdsrubriken och
       "Alla fakturor" beter sig likadant — de ÄR samma komponent, och att bara den ena staplade
       hade varit en skillnad utan orsak. Historikrubriken saknar summering idag, så för den är det
       bara framförhållning.

       Klassbaserat med flit: rubriken byts ut i sin helhet out-of-band vid varje mutation
       (_InvoiceSectionHead), och servern renderar samma klasser varje gång. Den staplade formen
       överlever därför en swap utan att något behöver återställas i JavaScript. */
    .bb-section-head {
        flex-direction: column;
        align-items: flex-start;
        gap: var(--bb-space-1);
    }

    .bb-section-head-summary {
        margin-left: 0;
    }

    .bb-section-search {
        margin-left: 0;
        justify-content: flex-start;
        flex-wrap: wrap;
    }

    /* max-width från .bb-filter-search (360 px) gäller desktop; här ska fältet fylla rubriken. */
    .bb-section-search .bb-filter-search {
        max-width: none;
        width: 100%;
    }

    /* Smalare luft och en kortare beställarrad — namnet kapas hellre än att tränga undan
       kronbeloppet, som är det raden handlar om. */
    .bb-row {
        padding-right: var(--bb-space-2);
        gap: var(--bb-space-2);
    }

    .bb-row-main {
        padding-left: var(--bb-space-3);
        gap: var(--bb-space-2);
    }

    .bb-row-who {
        max-width: 7rem;
    }

    /* Berättelseraden får RADBRYTA på mobilen i stället för att kapas. Mocken kortar i stället ner
       texten där ("Förfaller om 8 dagar" i stället för "Skickad 3 aug — förfaller om 8 dagar"),
       men serverns mening skrivs utan att veta något om skärmbredden — och "Skickad 3 aug —
       förfaller om…" är inte en kortare mening, det är en mening där själva svaret är bortklippt.
       Två rader text är billigare än en avkapad upplysning. */
    .bb-row-sub {
        white-space: normal;
        overflow: visible;
    }
}

/* "Visa fler — 82 till": en fotknapp i full bredd, inte en paginering. Paginering ber användaren
   hålla reda på vilken sida hon står på; det här ber henne bara om ett tryck till. */
.bb-section-more-wrap:empty {
    display: none;
}

.bb-section-more {
    display: block;
    width: 100%;
    padding: var(--bb-space-3);
    border: 0;
    border-top: 1px solid var(--bb-border);
    background: var(--bb-surface-raised);
    color: var(--bb-accent);
    font-size: var(--bb-text-base);
    cursor: pointer;
}

.bb-section-more:hover,
.bb-section-more:focus-visible {
    background: var(--bb-border);
}

/* Tomtillståndet i ett sektionskort står i ett vanligt block, inte i en tabellcell: sticky-tricket
   i .bb-empty finns för att centrera meddelandet i den SYNLIGA delen av en bredare scrollande
   tabell, och utan tabellen omkring gör det bara att rutan hamnar snett. */
.bb-empty--block {
    position: static;
    left: auto;
    transform: none;
    display: block;
}

/* Röd tint på "Förfallet" — bara när något faktiskt är förfallet, se _KpiCardsInner. Egen
   modifierare och inte .bb-kpi--danger, som även bär avtalslistans "Avvisade"-kort. */
.bb-kpi--alert {
    background: var(--bb-status-danger-bg);
}

.bb-kpi--alert .bb-kpi-label,
.bb-kpi--alert .bb-kpi-sub {
    color: var(--bb-status-danger-fg);
}

/* ==========================================================================
   FAKTURAPANELEN (#391 etapp 3, docs/design/fakturapanelen-mockup.html)

   Panelen ÄR en Bootstrap-modal — samma nod, samma id, samma öppningsmekanik och samma fokusfälla
   som förut. Allt nedan handlar bara om att placera den: CENTRERAD på desktop, mot underkanten på
   mobil. Inget JavaScript positionerar någonting.

   CENTRERAD OCH INTE LÄNGRE EN LÅDA I HÖGERKANTEN (ägarens beslut). Den ändringen är avsiktligt
   bara presentation: markupen fick klassen .modal-dialog-centered och ingenting annat, och
   panelens innehåll, åtgärdslogik och a11y-mekanik är orörda.

   FÖLJDEN FÖR SVEPGESTEN, som är den enda som inte är rent kosmetisk: svep i sidled bläddrar
   fortfarande, och den gesten hänger inte på att panelen sitter i kanten — den mäts på
   .bb-panel-content och betyder "nästa/föregående faktura", precis som pilknapparna. Den lever
   dessutom bara på mobil, där panelen ÄR kvar som bottensheet. På desktop har svepet aldrig varit
   den avsedda vägen (mus + bläddrarknappar), så centreringen tar ingenting ifrån den.

   Färgerna kommer uteslutande ur --bb-*-tokens, som redan har både ljust och mörkt värde i
   theme.css. Panelen behöver därför ingen egen mörk gren annat än där den lägger en egen ton
   ovanpå (kvittensrutan, som ärver status-tokens och alltså följer med av sig själv).
   ========================================================================== */

/* Bredare än drawerns 430px: en centrerad dialog har ingen kant att luta sig mot, och 430px mitt
   på en bred skärm blir en smal remsa med mycket luft omkring. 560px rymmer beloppsraden och
   knappraden utan att texten blir för lång att läsa. */
.bb-panel .bb-panel-dialog {
    max-width: 560px;
    width: 100%;
    margin: 1.75rem auto;
}

.bb-panel-content {
    /* Höjden följer innehållet i stället för fönstret — men aldrig så långt att foten hamnar
       utanför skärmen. .bb-panel-body har redan overflow:auto och tar skrollningen. */
    max-height: calc(100vh - 3.5rem);
    border: 0;
    border-radius: var(--bb-radius-lg);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: var(--bb-surface);
    box-shadow: 0 24px 60px rgba(0, 0, 0, .28);
}

/* Panelen kommer NEDIFRÅN OCH UPP, några tiondelar. Den gled förut in från höger, åt samma håll
   som lådan satt — en centrerad dialog har ingen sådan riktning, och ett kort lyft är den rörelse
   som läser som "det här är nytt och ligger ovanpå". Bootstraps .fade sköter opaciteten; det här
   lägger förflyttningen ovanpå. */
.bb-panel.fade .bb-panel-dialog {
    transform: translateY(1rem);
    transition: transform .18s ease-out;
}

.bb-panel.show .bb-panel-dialog {
    transform: none;
}

/* prefers-reduced-motion: ingen glidning, bara övertoningen. Klassen bb-panel--still sätts
   dessutom av invoicePanel.js, så regeln gäller även om inställningen ändras efter sidladdningen. */
@media (prefers-reduced-motion: reduce) {
    .bb-panel.fade .bb-panel-dialog {
        transform: none;
        transition: none;
    }
}

.bb-panel--still .bb-panel-dialog {
    transform: none !important;
    transition: none !important;
}

/* ==========================================================================================
   PANELENS NEDRÄKNING (#391 etapp 3b, docs/design/fakturapanelen-mockup.html sektion E–F)
   ==========================================================================================

   Noderna byggs av invoicePanel.js och inte av Razor — se noten i _InvoiceDetailsPanel om varför
   (nedräkningen är betingad på modalitet och rörelsepreferens). Formen bor här. */

.bb-panel-countdown {
    display: inline-flex;
    align-items: center;
    gap: var(--bb-space-2);
    margin-left: var(--bb-space-2);
    font-size: 0.8rem;
    color: var(--bb-text-muted);
    font-variant-numeric: tabular-nums; /* siffran ska inte rycka när 5 blir 4 */
}

/* RINGEN ÄR REN DEKORATION och ritas bara när rörelse är välkommen — den finns inte i DOM:en alls
   vid prefers-reduced-motion, så det här behöver ingen egen undantagsregel för att sluta snurra.
   NedräkningsTEXTEN står kvar i båda lägena: man ska kunna se hur lång tid som är kvar utan att
   något rör sig. */
.bb-panel-countdown-ring {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    border: 2px solid var(--bb-border);
    border-top-color: var(--bb-accent, #2f6b4f);
    animation: bb-countdown-spin 1s linear infinite;
    flex-shrink: 0;
}

@keyframes bb-countdown-spin {
    to { transform: rotate(360deg); }
}

/* "Stanna kvar" är en SYNLIG väg ut ur nedräkningen. Dämpad mot "Nästa" — den är utvägen, inte
   handlingen — men en riktig knapp med riktig träffyta, inte en textlänk i förbifarten. */
.bb-panel-stay {
    margin-right: var(--bb-space-2);
    color: var(--bb-text-muted);
    text-decoration: underline;
}

/* ==========================================================================================
   RADENS UT- OCH INGLIDNING (#391 etapp 3b)
   docs/design/radens-avslut-mockup.html och radens-ankomst-mockup.html
   ==========================================================================================

   FORMEN BOR HÄR, TIDERNA I invoiceRowMotion.js. Det är en delning som måste hållas: hållet före
   flytten (5 s) är ett produktbeslut som proven mäter, medan kollapsens längd finns på BÅDA
   ställena — 260 ms här och COLLAPSE_MS där — eftersom noden ska flyttas när animationen är slut
   och inte mitt i. Ändras det ena måste det andra följa med. */

/* Den gröna markören till vänster visar VAD som ändrades. Samma indragna regel i båda riktningar,
   för det är samma sorts besked: "det här är raden det gäller". */
.bb-row--leaving,
.bb-row--arriving {
    background: var(--bb-surface-raised);
    box-shadow: inset 3px 0 0 var(--bb-success, #2f6b4f);
}

.bb-row--leaving {
    transition: opacity 200ms ease;
}

/* Kollapsen: höjden går till noll och raden viker ihop sig nedåt. overflow döljer innehållet under
   vägen så texten inte trycks ut ur sitt utrymme. */
.bb-row--collapsing {
    overflow: hidden;
    opacity: 0;
    max-height: 0;
    padding-top: 0;
    padding-bottom: 0;
    border-bottom-width: 0;
    transition: max-height 260ms ease, opacity 200ms ease, padding 260ms ease;
}

/* Radens korta besked om vart den tar vägen. Dämpad och liten: den är en fotnot till raden, inte
   en andra berättelserad. */
.bb-row-inlinemsg {
    display: block;
    width: 100%;
    margin-top: var(--bb-space-1);
    font-size: 0.78rem;
    color: var(--bb-text-muted);
    font-style: italic;
}

/* PILLRET. Sitter mellan rubrikraden och <ul>, där ingenting nedanför flyttar sig av att det dyker
   upp — det är hela regeln ankomsten bygger på. */
.bb-section-pillwrap {
    padding: var(--bb-space-2) var(--bb-space-4);
    border-bottom: 1px solid var(--bb-border-soft, var(--bb-border));
    background: var(--bb-surface-raised);
}

.bb-section-pill {
    display: inline-flex;
    align-items: center;
    gap: var(--bb-space-2);
    border: 1px solid var(--bb-success-border, #c6ded1);
    background: var(--bb-success-bg, #eaf3ed);
    color: var(--bb-success-text, #1f4d39);
    border-radius: 999px;
    padding: 5px 12px 5px 10px;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
}

.bb-section-pill-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--bb-success, #2f6b4f);
    flex-shrink: 0;
}

.bb-section-pill-cue {
    font-weight: 400;
    text-decoration: underline;
}

/* Fullbredd och tumvänligt på mobil (mockens telefonvy). */
@media (max-width: 576px) {
    .bb-section-pill {
        width: 100%;
        justify-content: center;
        padding: 8px 12px;
    }
}

/* prefers-reduced-motion: INGEN animation, men SAMMA tider. Raden dröjer kvar lika länge och tas
   sedan bort direkt — det är samma regel som utgången och ankomsten redan följer i JS, och den
   står här för att formen inte ska motsäga den. */
@media (prefers-reduced-motion: reduce) {
    .bb-row--leaving,
    .bb-row--arriving,
    .bb-row--collapsing {
        transition: none;
    }
}

.bb-panel-grip,
.bb-panel-dots {
    display: none;
}

.bb-panel-header {
    display: block;
    background: var(--bb-surface-raised);
    border-bottom: 1px solid var(--bb-border);
    padding: var(--bb-space-4) var(--bb-space-5);
}

.bb-panel-head-top {
    display: flex;
    align-items: center;
    gap: var(--bb-space-2);
    flex-wrap: wrap;
}

/* Numret får inte drunkna i badgen: rubriken är större och tyngre, badgen är en pil bredvid den.
   I den gamla modalen var titeln projektnamnet och numret stod nertryckt i undertiteln. */
.bb-panel-title {
    font-size: var(--bb-text-lg);
    font-weight: 600;
    margin: 0;
    color: var(--bb-text);
}

.bb-panel-close {
    margin-left: auto;
}

.bb-panel-subtitle {
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
    margin-top: var(--bb-space-1);
}

.bb-panel-project {
    color: var(--bb-accent);
    text-decoration: none;
    border-bottom: 1px solid var(--bb-border);
}

.bb-panel-project:hover,
.bb-panel-project:focus-visible {
    color: var(--bb-accent-hover);
}

/* Bläddraren, variant 3 ur mockens sektion D. */
.bb-panel-pager {
    margin-top: var(--bb-space-3);
}

.bb-pager {
    display: inline-flex;
    align-items: center;
    border: 1px solid var(--bb-border);
    border-radius: var(--bb-radius);
    overflow: hidden;
    background: var(--bb-surface);
    font-size: var(--bb-text-sm);
}

.bb-pager-btn {
    border: 0;
    background: none;
    padding: .3rem .65rem;
    color: var(--bb-accent);
    font-size: var(--bb-text-sm);
    cursor: pointer;
}

/* Utgråad vid ändarna. Bläddraren varvar aldrig runt — den som gått igenom kön ska få veta att
   hon är klar, inte hamna i början igen. */
.bb-pager-btn:disabled {
    color: var(--bb-text-muted);
    opacity: .5;
    cursor: default;
}

.bb-pager-mid {
    padding: .3rem .7rem;
    border-left: 1px solid var(--bb-border);
    border-right: 1px solid var(--bb-border);
    color: var(--bb-text-muted);
    white-space: nowrap;
}

/* Grannvisningen: vad som kommer härnäst, så man kan avgöra om det är värt att bläddra vidare. */
.bb-pager-peek {
    font-size: var(--bb-text-xs);
    color: var(--bb-text-muted);
    margin-top: var(--bb-space-1);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.bb-pager-peek:empty {
    display: none;
}

/* Svepraden hör mobilen till — på desktop står bläddraren redan i ord ovanför. */
.bb-panel-swipehint {
    display: none;
}

.bb-panel-body {
    flex: 1;
    overflow: auto;
    padding: var(--bb-space-4) var(--bb-space-5);
    /* Let the panel scroll to its end WITHOUT the page behind starting to move (#423).

       Without this the wheel — or a finger, which is where it is worst — carries straight on
       into the list behind the moment the panel's own content runs out, with the pointer never
       having left the panel. Measured on dev before the fix: panel parked at its end, ten wheel
       notches over the panel, window.scrollY 0 -> 1000. With the rule: 0 -> 0.

       THIS IS NOT THE SAME AS FREEZING THE BACKGROUND, and that distinction is the repo owner's
       decision (#408, closed): scrolling the page with the pointer OVER the background is wanted
       and still works — measured unchanged at 0 -> 1000 with this rule in place. What is stopped
       is only the spill, where something other than the thing being pointed at starts moving.

       Same reason and same one-liner as .page-sidebar's mobile drawer above and payment.css; the
       panel was simply missed when it was built in #391 stage 3. */
    overscroll-behavior: contain;
}

.bb-panel-box {
    background: var(--bb-surface-raised);
    border-radius: var(--bb-radius-lg);
    padding: var(--bb-space-3) var(--bb-space-4);
}

.bb-panel-box--dim {
    opacity: .75;
}

.bb-panel-amount {
    font-size: var(--bb-text-2xl);
    font-weight: 600;
    margin-top: 2px;
    color: var(--bb-text);
}

.bb-panel-amount.is-over {
    color: var(--bb-status-danger-fg);
}

/* Berättelseraden bor I beloppsboxen, direkt under talet: samma mening som listraden, och den
   förklarar just det belopp den står under. Färgen sätts av .bb-narrative--* längre upp. */
.bb-panel-narrative {
    font-size: var(--bb-text-sm);
    margin-top: var(--bb-space-1);
}

.bb-panel-dates {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--bb-space-3);
    margin-top: var(--bb-space-3);
    padding-top: var(--bb-space-3);
    border-top: 1px solid var(--bb-border);
}

.bb-panel-kv {
    font-size: var(--bb-text-sm);
    color: var(--bb-text-muted);
    line-height: 1.7;
}

.bb-panel-buyer-name {
    color: var(--bb-text);
    font-weight: 600;
}

.bb-panel-address {
    color: var(--bb-text-muted);
}

.bb-panel-footer {
    border-top: 1px solid var(--bb-border);
    background: var(--bb-surface);
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--bb-space-2);
    padding: var(--bb-space-3) var(--bb-space-5);
}

.bb-panel-actions-secondary,
.bb-panel-actions-primary {
    display: flex;
    flex-wrap: wrap;
    gap: var(--bb-space-2);
    align-items: center;
}

/* Krediteringens knapp: destruktiv ton utan Bootstraps btn-outline-danger, som ritar #dc3545 mot
   vit yta och fälls av axe på kontrast. --bb-status-danger-fg är samma röda som berättelseraden
   och statusrutorna, och den är vald för att klara AA i både ljust och mörkt tema. */
.bb-panel-danger {
    color: var(--bb-status-danger-fg);
    border-color: var(--bb-status-danger-fg);
}

.bb-panel-danger:hover,
.bb-panel-danger:focus-visible {
    color: var(--bb-status-danger-fg);
    background: var(--bb-status-danger-bg);
    border-color: var(--bb-status-danger-fg);
}

/* Pågår-läget: övriga knappar slutar svara. Utan det trycker folk två gånger, och krediteringen
   går via Billecta och tar någon sekund. */
.bb-panel-inert {
    opacity: .4;
    pointer-events: none;
}

[aria-busy="true"].bb-panel-inert {
    opacity: .6;
}

/* Kvittensrutan ligger OVANFÖR innehållet och inuti panelen — inte som en toast i ett hörn.
   Resultatet ska stå där handlingen skedde. */
.bb-panel-receipt-slot:empty {
    display: none;
}

.bb-panel-receipt {
    display: flex;
    gap: var(--bb-space-3);
    align-items: flex-start;
    margin: var(--bb-space-4) var(--bb-space-5) 0;
    padding: var(--bb-space-3);
    border-radius: var(--bb-radius-lg);
    border: 1px solid var(--bb-status-success-fg);
    background: var(--bb-status-success-bg);
    color: var(--bb-status-success-fg);
    font-size: var(--bb-text-sm);
}

.bb-panel-receipt--danger {
    border-color: var(--bb-status-danger-fg);
    background: var(--bb-status-danger-bg);
    color: var(--bb-status-danger-fg);
}

.bb-panel-tick {
    width: 1.2rem;
    height: 1.2rem;
    flex-shrink: 0;
    border-radius: 50%;
    background: currentColor;
    color: var(--bb-surface);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--bb-text-xs);
}

.bb-panel-receipt-note {
    margin-top: 2px;
    font-size: var(--bb-text-xs);
    opacity: .85;
}

/* Kvittenslägets fot. */
.bb-panel-nextfoot {
    border-top: 1px solid var(--bb-border);
    background: var(--bb-status-success-bg);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--bb-space-2);
    padding: var(--bb-space-3) var(--bb-space-5);
}

.bb-panel-count {
    display: inline-flex;
    align-items: center;
    gap: var(--bb-space-2);
    font-size: var(--bb-text-sm);
    color: var(--bb-status-success-fg);
}

.bb-panel-nextbtns {
    display: inline-flex;
    gap: var(--bb-space-2);
}

/* NEDRÄKNINGSRINGEN FINNS INTE HÄR. Den hörde till mockens femsekundersnedräkning (sektion E och
   F), som är utbruten till en egen etapp — skälet står överst i wwwroot/js/invoice/invoicePanel.js.
   Kvittensfoten visar en rad text och en vanlig "Nästa"-knapp. */

/* ---------- Mobil: bottensheet ---------- */
@media (max-width: 575.98px) {
    /* SHEETEN ÄR KVAR. Centreringen gäller desktop; på mobil är underkanten dit tummen når, och
       svepet — som bara finns här — hör ihop med den placeringen.

       display/min-height nollar Bootstraps .modal-dialog-centered, som markupen nu bär för
       desktopcentreringens skull. Utan de två raderna centrerar den lodrätt även här och sheeten
       lyfter från underkanten. */
    .bb-panel .bb-panel-dialog {
        display: block;
        min-height: 0;
        max-width: none;
        margin: 0;
        height: auto;
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0;
    }

    .bb-panel-content {
        height: auto;
        max-height: 88vh;
        border-radius: var(--bb-radius-lg) var(--bb-radius-lg) 0 0;
        box-shadow: 0 -10px 34px rgba(0, 0, 0, .26);
    }

    /* Sheeten kommer underifrån, dit tummen når. */
    .bb-panel.fade .bb-panel-dialog {
        transform: translateY(2rem);
    }

    /* OCH DEN LANDAR. Utan den här raden blev förskjutningen PERMANENT på mobil, och det är
       ingen kosmetisk detalj: .bb-panel.show .bb-panel-dialog { transform: none } står längre upp
       i filen, har SAMMA specificitet (0,3,0) och förlorar därför mot regeln ovanför bara för att
       den kommer senare. Sheeten stod alltså kvar 2rem NEDANFÖR underkanten hela tiden den var
       öppen, med åtgärdsfoten — Kreditera och den primära knappen — delvis utanför skärmen.

       Mätt i harnessen vid 375px: dialogens underkant låg på 840px i ett 812px högt fönster, med
       transform: matrix(1, 0, 0, 1, 0, 28). Felet är äldre än centreringen och har inget med den
       att göra; det hittades när den här etappen mätte upp mobilvyn. */
    .bb-panel.show .bb-panel-dialog {
        transform: none;
    }

    .bb-panel-grip {
        display: block;
        width: 38px;
        height: 4px;
        border-radius: 2px;
        background: var(--bb-border);
        margin: var(--bb-space-2) auto 0;
    }

    /* Punktraden ÄR räknaren på mobil: den flyttar sig ett steg när nästa faktura kommer in, så
       förflyttningen syns även för den som tittade bort. */
    .bb-panel-dots {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 7px;
        padding: 6px 0 2px;
    }

    .bb-panel-dot {
        width: 5px;
        height: 5px;
        border-radius: 50%;
        background: var(--bb-border);
    }

    .bb-panel-dot--on {
        width: 16px;
        border-radius: 3px;
        background: var(--bb-accent);
    }

    .bb-panel-header {
        background: var(--bb-surface);
        padding: var(--bb-space-3) var(--bb-space-4);
    }

    /* På mobil är bläddringen SVEP, inte pilar — men texten säger det rakt ut i ord, för utan minst
       en av mockens tre upptäckbarhetssaker är svep en funktion bara den hittar som råkar prova. */
    .bb-pager,
    .bb-pager-peek {
        display: none;
    }

    .bb-panel-swipehint {
        display: block;
        text-align: center;
        font-size: var(--bb-text-xs);
        color: var(--bb-text-muted);
        padding-top: var(--bb-space-1);
    }

    .bb-panel-body {
        padding: var(--bb-space-3) var(--bb-space-4);
    }

    /* Primärknappen i full bredd närmast tummen; de sekundära delar raden ovanför. */
    .bb-panel-footer {
        flex-direction: column-reverse;
        align-items: stretch;
        padding: var(--bb-space-3) var(--bb-space-4);
    }

    /* nowrap är inte kosmetik. En kolumnriktad flexbehållare med flex-wrap:wrap bryter till en ny
       KOLUMN när höjden tar slut, och då slutar column-reverse att lägga primärknappen underst —
       den hamnade ovanför Kreditera, alltså längst BORT från tummen, vilket är precis tvärtemot
       hela skälet att den ligger här. */
    /* Selektorn bär panelfoten med flit. Den generella mobilregeln längre upp i filen
       (.modal-footer > div, specificitet 0-1-1) staplar ALLA modalfötters knappgrupper i vanlig
       kolumnordning, och den vann över en ensam .bb-panel-actions-primary. Följden var att
       primärknappen hamnade ovanför Kreditera, alltså längst BORT från tummen — tvärtemot hela
       skälet att den ligger här. */
    .bb-panel-footer .bb-panel-actions-primary {
        flex-direction: column-reverse;
        flex-wrap: nowrap;
        align-items: stretch;
    }

    /* De sekundära delar en rad: de är vägar bort från panelen (PDF, avtalet), inte åtgärder på
       fakturan, och ska inte se ut som en knappstapel. */
    .bb-panel-footer .bb-panel-actions-secondary {
        flex-direction: row;
        flex-wrap: wrap;
        justify-content: center;
        gap: var(--bb-space-4);
    }

    .bb-panel-actions-primary .btn {
        width: 100%;
        padding: .7rem;
    }

    .bb-panel-nextfoot {
        flex-direction: column;
        align-items: stretch;
        gap: var(--bb-space-2);
        padding: var(--bb-space-3) var(--bb-space-4);
    }

    .bb-panel-count {
        justify-content: center;
    }

    /* "Nästa" tar full bredd i stället för att trängas i en rubrik — och den är inaktiv de första
       tiondelarna precis som på webben. Här är risken större, eftersom tummen redan står på
       knappraden. */
    .bb-panel-nextbtns {
        display: flex;
    }

    .bb-panel-nextbtns .btn {
        flex: 1;
    }

    .bb-panel-receipt {
        margin: var(--bb-space-3) var(--bb-space-4) 0;
    }
}

/* Instruktionsbubblan. Visas en gång, första gången panelen öppnas, och sedan aldrig mer. */
.bb-panel-coach {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    bottom: 9rem;
    z-index: 20;
    background: var(--bb-text);
    color: var(--bb-surface);
    font-size: var(--bb-text-xs);
    padding: .5rem .9rem;
    border-radius: 20px;
    box-shadow: var(--bb-shadow-md);
    pointer-events: none;
}

@media (min-width: 576px) {
    /* Bubblan handlar om svep. På desktop finns bläddraren i ord och bubblan vore brus. */
    .bb-panel-coach {
        display: none;
    }
}

/* ==========================================================================
   AVTALSVYN (#391 etapp 4, docs/design/fakturaytor-mockup.html scen F)

   Bara nya klassnamn. Ingenting här rör en selektor som prods väg (app.css,
   List.cshtml:s tabellgren) använder — filen laddas dessutom bara när
   Features:ModernUI är på, så flaggan av är byte för byte oförändrad.
   ========================================================================== */

/* --- Breadcrumb, heading and actions ------------------------------------ */

.bb-contract-crumb {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--bb-space-2);
    margin-bottom: var(--bb-space-3);
}

.bb-crumb-back {
    color: var(--bb-text-muted);
    text-decoration: none;
    font-size: var(--bb-text-sm);
    /* The hit area is small and sits in a row of buttons. The floor is WCAG 2.5.5. */
    min-height: var(--bb-touch-target);
    display: inline-flex;
    align-items: center;
}

.bb-crumb-back:hover,
.bb-crumb-back:focus-visible {
    color: var(--bb-accent);
    text-decoration: underline;
}

.bb-crumb-sep {
    color: var(--bb-border);
}

/* <h1> with the list heading's weight rather than a page heading's: the project
   name sits in a breadcrumb, not above an empty page. The size is typographic, not
   semantic — the element must be an h1 for the page to have a heading at all (cf. #224). */
.bb-crumb-title {
    margin: 0;
    font-size: var(--bb-text-lg);
    font-weight: 600;
    color: var(--bb-text);
}

/* margin-left:auto pushes the actions to the right edge, exactly as in the mock.
   It wraps to its own line on narrow screens rather than squeezing the buttons. */
.bb-contract-actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: var(--bb-space-2);
    flex-wrap: wrap;
}

@media (max-width: 768px) {
    .bb-contract-actions {
        margin-left: 0;
        width: 100%;
    }
}

/* --- Infostripen -------------------------------------------------------- */

/* THE CONTRACT'S FACTS: a labelled grid, not a wrapping strip.
   The strip worked while there were three short values. With two buyers there are up to eleven,
   and a flex row wrapped them into ragged lines where nothing marked the boundary between one
   fact and the next. Columns give each fact a fixed home, so the eye lands on "Beställare" rather
   than scanning for a middot. Labels replaced the emoji entirely — a word is unambiguous, renders
   identically everywhere, and is what a screen reader needs anyway. */
/* THE LAYOUT'S OWN 30px OF HEADROOM, CANCELLED HERE. .main-wrapper carries padding-top:30px from
   the global layout, written for pages where the wrapper IS the top of the page. On the contract
   view the heading, the actions and the facts are rendered above it, so those 30px landed on top
   of the facts' own margin and opened a hole between the contract's details and its economy.
   Scoped to this page rather than changed globally: every other page still starts with the
   wrapper, and there the padding is doing its job. */
.invoice-contract-view .main-wrapper {
    padding-top: 0;
}

/* THE FILTER-STATE NODE TAKES A LINE IT DOES NOT NEED, here. It is inline-flex, and on the invoice
   list that is right — it sits in the filter row carrying chips. On the contract view the chip was
   removed in stage 4, so all it holds is the hidden contractId, and an empty inline box still
   generates a 24px line box. That line was sitting between the ledger and the contract's history,
   looking like deliberate spacing.
   display:none does not affect the node's two jobs: htmx swaps it out-of-band regardless, and a
   hidden input's value travels with hx-include whether or not CSS renders it. */
.invoice-contract-view .bb-filter-state {
    display: none;
}

/* COLUMNS, NOT GRID CELLS. A grid makes every cell in a row as tall as the tallest one, and the
   tallest is "Beställare" with two buyers — which left a block of dead space under the address and
   the period, pushing the economy panel down for no reason. Independent columns each take only the
   height they need, so the block ends where its content ends. */
.bb-contract-facts {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--bb-space-3) var(--bb-space-5);
    margin: 0 0 var(--bb-space-3);
    padding: 0;
}

/* The short facts share one column and stack; the buyers get their own. Two columns and not three
   for the same reason: the address and the period are one line each, and giving each of them a
   third of the width spread three short values across the whole page. */
.bb-facts-col {
    display: flex;
    flex-direction: column;
    gap: var(--bb-space-2);
    min-width: 0;
    flex: 1 1 220px;
}

.bb-fact {
    min-width: 0;
}

/* Same label treatment as the economy panel's cells — the two blocks sit within a screen of each
   other and should read as one system, not two. */
.bb-fact-label {
    display: block;
    font-size: var(--bb-text-xs);
    color: var(--bb-text-muted);
    margin: 0 0 2px;
}

.bb-fact-value {
    display: block;
    margin: 0;
    font-size: var(--bb-text-sm);
    color: var(--bb-text);
    overflow-wrap: anywhere;
}

/* One buyer per block, name above contact details. Two people on one line was the unreadable
   shape; stacking them makes "how many buyers are there" answerable at a glance. */
.bb-fact-party {
    display: block;
}

.bb-fact-party + .bb-fact-party {
    margin-top: var(--bb-space-2);
}

.bb-fact-party-name {
    display: block;
}

/* Contact details are subordinate to the name they belong to, and separated by space rather than
   punctuation — the middots were doing the work that whitespace does better here. */
.bb-fact-party-contact {
    display: flex;
    flex-wrap: wrap;
    gap: 0 var(--bb-space-3);
    font-size: var(--bb-text-xs);
    color: var(--bb-text-muted);
}

/* Links that do not announce themselves as links. Four coloured, underlined contact details in a
   block of facts pull the eye away from the facts; the affordance appears on hover and focus,
   where it is actually needed. Colour is forced over the global anchor rule on purpose. */
.bb-fact-link {
    color: inherit;
    text-decoration: none;
}

.bb-fact-link:hover,
.bb-fact-link:focus-visible {
    color: var(--bb-accent);
    text-decoration: underline;
}

/* --- Ekonomipanelen ----------------------------------------------------- */

/* CARD BACKGROUND, NOT THE RAISED TINT — the mock's .econ uses --card, and the tint is reserved
   for group headings alone. Sharing --bb-surface-raised with .bb-group-head made the economy
   panel and the ledger's group bands read as the same kind of thing, so the eye ran the two
   together instead of seeing a summary above a list. One tint, one meaning: "this is a heading
   inside a card". */
.bb-econ {
    background: var(--bb-surface);
    border: 1px solid var(--bb-border);
    border-radius: var(--bb-radius-lg);
    padding: var(--bb-space-4);
    margin-bottom: var(--bb-space-4);
}

/* auto-fit + minmax instead of three fixed columns: the cells stack by themselves
   on mobile without a breakpoint of their own, and a lone cell (a contract without
   ÄTA, where "varav ÄTA" is omitted) does not become a third of the width with empty space beside it. */
.bb-econ-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: var(--bb-space-4);
}

.bb-econ-label {
    font-size: var(--bb-text-xs);
    color: var(--bb-text-muted);
    text-transform: none;
    margin-bottom: var(--bb-space-1);
}

/* tabular-nums: the numbers sit in a column under each other and must not jump
   sideways when a digit changes in an OOB swap. */
.bb-econ-value {
    font-size: var(--bb-text-xl);
    font-weight: 600;
    color: var(--bb-text);
    font-variant-numeric: tabular-nums;
}

.bb-econ-sub {
    font-size: var(--bb-text-xs);
    color: var(--bb-text-muted);
    margin-top: var(--bb-space-1);
}

.bb-econ-sub--over {
    color: var(--bb-status-danger-fg);
    font-weight: 600;
}

.bb-econ-bar {
    margin-top: var(--bb-space-4);
    height: 8px;
    border-radius: 999px;
    background: var(--bb-border);
    overflow: hidden;
}

.bb-econ-bar > i {
    display: block;
    height: 100%;
    background: var(--bb-accent);
    /* The width comes from the server and is capped at 100 % there. The transition
       makes an OOB swap after a credit note read as a change rather than a jump —
       but only for those who have not asked for reduced motion. */
    transition: width .3s ease;
}

@media (prefers-reduced-motion: reduce) {
    .bb-econ-bar > i {
        transition: none;
    }
}

/* OVER-INVOICED colours the bar and not the number: the number already carries its
   minus sign, and two red signals for the same thing in one panel is one too many.
   The width is capped at 100 %, so the red bar is full — it is the caption below
   ("Fakturerat 130 %") that carries how much too much. */
.bb-econ.is-over .bb-econ-bar > i {
    background: var(--bb-status-danger-fg);
}

.bb-econ-bar-text {
    margin-top: var(--bb-space-2);
    font-size: var(--bb-text-xs);
    color: var(--bb-text-muted);
}

/* ROT block inside the economy panel (#440). A bordered sub-block, not its own card:
   it belongs to the contract's money but is Skatteverket's money, so it sits under
   the bar, visually separated by the top border. The figures reuse .bb-econ-grid;
   the flex row keeps space on the right for the ROT-XML download link, whose subtext
   stays put when the link is hidden (zero paid ROT invoices). */
.bb-econ-rot {
    margin-top: var(--bb-space-4);
    padding-top: var(--bb-space-4);
    border-top: 1px solid var(--bb-border);
}

.bb-econ-rot-title {
    font-size: var(--bb-text-sm);
    font-weight: 600;
    color: var(--bb-text);
    margin-bottom: var(--bb-space-2);
}

.bb-econ-rot-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--bb-space-2) var(--bb-space-4);
    align-items: end;
    justify-content: space-between;
}

.bb-econ-rot-figures {
    flex: 1 1 auto;
}

.bb-econ-rot-download {
    flex: 0 1 auto;
    text-align: right;
}

.bb-econ-rot-download .btn-link {
    font-size: var(--bb-text-sm);
}

/* --- Avtalsgrupperna ---------------------------------------------------- */

/* A group heading, not a section heading: it sits BETWEEN row lists inside the same
   card, so it is a tinted BAND rather than a bare line of text (the mock's .ghead).
   The tint is what makes it read as a divider — without it the heading floated on the
   same white as the rows and the ÄTA group looked like one more row rather than the
   start of a new section. Raised surface and not a hand-picked hex so it follows the
   dark theme; borders on both sides close the band against the rows above and below. */
.bb-group-head {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--bb-space-2);
    padding: var(--bb-space-3) var(--bb-space-4);
    background: var(--bb-surface-raised);
    border-top: 1px solid var(--bb-border);
    border-bottom: 1px solid var(--bb-border);
    font-size: var(--bb-text-sm);
    font-weight: 600;
    color: var(--bb-text);
}

.bb-contract-groups > .bb-group-head:first-child {
    border-top: 0;
}

/* The ÄTA band is SUBORDINATE to the main contract's, and says so twice: lighter
   weight, and an indent that lines its text up with the indented rows it heads. */
.bb-group-head--ata {
    font-weight: 500;
    padding-left: var(--bb-space-5);
}

.bb-group-head-title {
    display: inline-flex;
    align-items: center;
    gap: var(--bb-space-2);
    flex-wrap: wrap;
}

.bb-group-head-arrow {
    color: var(--bb-text-muted);
}

/* Underlined by default, not only on hover. This is the ÄTA's only route to its own contract, and
   a link that looks like text is a link nobody finds — the accent colour alone was carrying the
   whole affordance, and against a tinted band it did not carry it. */
.bb-group-head-link {
    color: var(--bb-accent);
    text-decoration: underline;
    text-underline-offset: 2px;
    font-weight: 400;
    font-size: var(--bb-text-xs);
    white-space: nowrap;
}

.bb-group-head-link:hover,
.bb-group-head-link:focus-visible {
    color: var(--bb-accent);
    text-decoration-thickness: 2px;
}

/* The arrow means "opens elsewhere". It is not underlined with the words — an underline running
   under an arrow glyph reads as a typo. */
.bb-group-head-link-arrow {
    display: inline-block;
    margin-left: 2px;
    text-decoration: none;
}

/* The subtotal on the right. Lighter than the group's name — it is an aside, not the heading —
   but NOT as faint as a row's secondary line. It was dimmed to exactly the same degree as the
   narrative under each invoice, which put a summary of the whole group on the same visual level
   as one row's detail. Medium weight and the body colour restore the order: name, then summary,
   then rows. */
.bb-group-head-summary {
    margin-left: auto;
    font-weight: 500;
    color: var(--bb-text);
    font-variant-numeric: tabular-nums;
}

.bb-group-head.is-over .bb-group-head-summary {
    color: var(--bb-status-danger-fg);
    font-weight: 600;
}

@media (max-width: 576px) {
    .bb-group-head-summary {
        margin-left: 0;
        width: 100%;
    }
}

/* ÄTA-radernas indrag, mockens .indent. Bara vänsterkanten — raden i sig är
   oförändrad (_InvoiceRowModern återanvänds som den är). */
.bb-rows--indent > .bb-row {
    padding-left: var(--bb-space-6);
}

@media (max-width: 576px) {
    /* On mobile the space belongs to the row, not to the hierarchy: the group heading
       is already directly above and says which ÄTA it concerns. */
    .bb-rows--indent > .bb-row {
        padding-left: var(--bb-space-4);
    }
}

/* --- The contract history ----------------------------------------------- */

/* Same rhythm as the gap between the facts and the economy panel. It was a step larger, which read
   as a section break on a page that is one contract from top to bottom. */
.bb-contract-activity {
    margin-top: var(--bb-space-3);
}

/* ONE HEADING GRAMMAR ON THE PAGE. The ledger's groups announce themselves with a tinted band;
   the activity card announced itself with bare text on white. Same kind of thing — "a heading
   inside a card" — said two different ways, which is what made the activity section read as
   something bolted on after the invoices rather than the same contract seen over time.
   Now both are bands, so the eye reads down the page in one language. */
.bb-contract-activity .bb-section-head {
    background: var(--bb-surface-raised);
    border-radius: var(--bb-radius-lg) var(--bb-radius-lg) 0 0;
}

/* THE TIMELINE NEEDS THE CARD'S OWN INSET, and does not bring one.
   Everywhere else it has been used — the invoice panel, the contract modal — it sat inside a
   body that already had padding. Dropped straight into a section card it inherits none, so the
   right-aligned date ended up one pixel from the card border and read as clipped.
   The horizontal value is the section heading's (--bb-space-4), so text and dates line up with
   the heading above them instead of merely being "not touching the edge". */
.bb-contract-activity .bb-timeline {
    padding: var(--bb-space-2) var(--bb-space-4) var(--bb-space-3);
}

/* --- Signeringsbrickan -------------------------------------------------- */

/* A small badge of our own rather than Bootstrap's .badge: the latter is uppercase
   and filled, and this one sits inside a heading row where it should read as a note
   beside the name — not as a label on top of it. The colour pairs are theme.css's
   status tokens, i.e. the same as the rest of the interface and already contrast-
   measured in both themes. */
.bb-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--bb-space-1);
    padding: .1rem .45rem;
    border-radius: var(--bb-radius);
    font-size: var(--bb-text-xs);
    font-weight: 500;
    white-space: nowrap;
}

.bb-badge--success {
    background: var(--bb-status-success-bg);
    color: var(--bb-status-success-fg);
}

.bb-badge--warn {
    background: var(--bb-status-warning-bg);
    color: var(--bb-status-warning-fg);
}

/* ==========================================================================
   VÅRA PROJEKT — the shared contract view (#391 stage 5, scene G of the mock)

   Almost nothing here is new, and that is deliberate: .bb-rows, .bb-row, .bb-row-main,
   .bb-section-*, .bb-kpi*, .bb-narrative--*, .bb-row-main--dim and .bb-empty* are reused unchanged
   from the invoice list. Two surfaces that speak the same language must not have two sets of rules
   that can drift apart — and no new colour literals are introduced, everything goes through the
   --bb-* tokens, which change value under [data-bs-theme="dark"] in theme.css.
   ========================================================================== */

.bb-contract-sections {
    display: block;
}

/* NO NEW RULE FOR THE FULL-WIDTH "Skapa avtal" ON MOBILE, which the stage plan asked for. One
   already exists and covers this page: `.bb-page-actions .btn { width: 100%; min-height:
   var(--bb-touch-target) }` under @media (max-width: 575.98px), added in phase 8 (#210) — and
   ContractList.cshtml already renders the button inside .bb-page-actions in the ModernUI branch, so
   it applies unchanged. A second rule would only have been another place to keep in step.

   WHAT IS NOT DONE, and is a deliberate omission rather than an oversight: the mock puts the mobile
   button at the BOTTOM of the page, below the project card. Moving it there means changing the DOM
   order for one breakpoint, which changes the tab order and the reading order for everyone. Out of
   scope for a stage that is otherwise purely visual; the button is full width and reachable where
   it is. */

/* THE ROW IS AN <a> HERE AND A <button> ON THE INVOICE LIST, and that is the whole difference in
   CSS.

   The target is another PAGE, not a panel, so the link must be a real link: middle-click,
   ctrl-click, "copy link address" and the keyboard become the browser's job instead of a11y.js's.
   The price is that .bb-row-main's button reset is not enough — an <a> inherits neither the button
   rules for background nor for font, but it does carry an underline and a link colour, and both
   have to go. Without these two declarations the whole project list reads as blue underlined text.

   color: inherit is the contrast-critical one of the two: the theme's link colour is measured
   against the page background, not against a card, and the row's narrative line sets its OWN colour
   through .bb-narrative--*. Inherited colour lets that rule win. */
a.bb-row-main {
    text-decoration: none;
    color: inherit;
}

a.bb-row-main:hover,
a.bb-row-main:focus {
    text-decoration: none;
    color: inherit;
}

/* The bar in the row is the same .bb-progress as the contract view's economy panel, in miniature —
   the mock's "stapeln är samma som avtalsvyns ekonomipanel i miniatyr". The variant does three
   things:

     1. Gives it a fixed width. Without one the track shrinks with the project name's length, and
        two rows above each other cannot be compared by eye — which is the bar's only job.
     2. Drops the caption. .bb-progress-caption belongs to the table cell; here the percentage and
        the kronor are already stated in words on the narrative line, and printing the same numbers
        twice is noise.
     3. Makes it a flex item that neither grows nor shrinks, since .bb-row-main is flex and the bar
        would otherwise compete with the amount for space.

   The element is a <span> and not a <div>: it sits inside the link, which may only contain phrasing
   content. display:block is therefore set explicitly. */
.bb-progress--row {
    display: block;
    width: 130px;
    flex: 0 0 auto;
    align-self: center;
}

.bb-progress--row > .bb-progress-bar {
    display: block;
}

/* Finished and declined projects are dimmed in full (the mock's .dim). .bb-row-main--dim already
   tones down the title and the amount; the bar has to follow, or a full bright green bar stays as
   the loudest thing on a row that is meant to be history. Opacity and no colour of its own — grey
   would have been a new literal, and a dimmed green still reads as "done". */
.bb-row-main--dim .bb-progress--row {
    opacity: .45;
}

@media (max-width: 767.98px) {
    /* The bar moves INTO the row on mobile (the mock's "G · Våra projekt — stapeln flyttar in i
       raden"). Here it is enough that it stops competing for width: 130 fixed pixels next to a
       project name and an amount leaves nothing for the name at 375 px. It is hidden instead — the
       percentage and the kronor are already in words on the narrative line, which is the load-
       bearing information and which is allowed to wrap on mobile rather than be truncated.

       HIDDEN RATHER THAN MOVED BELOW THE TEXT, as the mock draws it: the bar sits inside the link,
       after .bb-row-body, and moving it there would mean rebuilding the row's structure for a
       single breakpoint. It carries no information that is not already in text — it is aria-hidden
       for exactly that reason — so leaving it out costs nothing. */
    .bb-progress--row {
        display: none;
    }
}

/* THE EMPTY SIGNING SECTION IS RENDERED IN FULL AND COLLAPSED BY THIS RULE (#391 stage 5).

   The card, the heading and an empty <ul> are always in the document, even when nothing awaits a
   signature — otherwise the poll's response has nowhere to land. htmx silently drops an out-of-band
   fragment whose id is not in the DOM, and a lone heading without a card also produced a floating
   "Väntar på signering · 1" above the project card, with no border and no row beneath it.

   The rule hangs off the HEADING's [hidden] and not off an attribute on <section>. That is what
   lets the poll get by with the swap it already makes: it swaps in a heading WITH a title, i.e.
   without [hidden], and the card appears in the same response with its <ul> already in place. A
   hidden attribute on <section> would have required the poll to swap the section too — tearing out
   the very <ul> the row swap in that same response is aimed at.

   ITS OWN MODIFIER and not a bare :has(> .bb-section-head[hidden]): the invoice list's empty action
   section also renders a hidden heading as a direct child of its card, and an unscoped rule would
   have hidden the green receipt "Inget kräver åtgärd just nu". The class says which cards are
   allowed to disappear by themselves.

   display:none and not visibility: the card must leave both the layout and the accessibility tree —
   an empty card that gets announced is worse than no card. */
.bb-section-card--auto-hide:has(> .bb-section-head[hidden]) {
    display: none;
}
