/* Specific token: commons are in base.css */
:root {
    --sidebar-width: 210px;

    --bg-sidebar:    rgba(7, 11, 15, 0.96);
    --bg-card:       rgba(13, 19, 24, 0.9);
    --bg-card-hover: rgba(25, 32, 38, 0.98);
    --bg-topbar:     rgba(7, 11, 15, 0.74);
    --bg-dropdown:   rgba(8, 12, 16, 0.98);
    --bg-item:       rgba(12, 18, 24, 0.75);
    --bg-item-hover: rgba(20, 28, 36, 0.90);
    --bg-input:      rgba(7, 11, 15, 0.85);
    --border-subtle: rgba(255, 255, 255, 0.09);

    --text-dim:        #818890;
    --text-footer:     #aab2bd;
    --text-nav:        #c9d0d8;
    --text-item:       #b8c2cc;

    --accent-warm-dim:  #c4a489;
    --accent-warm-link: #c79472;
    --text-price:       #ffb27a;
    --text-badge:       #ffb078;
    --text-dropdown:    #d8dde4;
    --text-cardbtn:     #fdf0e4;

    --radius-card: 15px;
}

/* ── LIGHT THEME OVERRIDES ── */
:root[data-theme="light"] {
    --bg-sidebar:      rgba(248, 244, 239, 0.97);
    --bg-card:         rgba(255, 252, 248, 0.95);
    --bg-card-hover:   rgba(255, 248, 240, 0.99);
    --bg-topbar:       rgba(250, 247, 243, 0.88);
    --bg-dropdown:     rgba(255, 253, 250, 0.99);
    --bg-item:         rgba(255, 255, 255, 0.70);
    --bg-item-hover:   rgba(255, 248, 240, 0.95);
    --bg-input:        rgba(255, 255, 255, 0.80);
    --border-subtle:   rgba(0, 0, 0, 0.08);

    --text-dim:         #9ca3af;
    --text-footer:      #6b7280;
    --text-nav:         #374151;
    --text-item:        #4b5563;
    --accent-warm-dim:  #c2773a;
    --accent-warm-link: #b86420;
    --text-price:       #c05e10;
    --text-badge:       #c06010;
    --text-dropdown:    #374151;
}

.layout {
    display: flex;
    min-height: 100vh;
}


/* SIDEBAR */

.sidebar {
    position: fixed;
    top: 0;
    left: 0;
    width: var(--sidebar-width);
    height: 100vh;
    padding: 14px 12px;
    background: var(--bg-sidebar);
    border-right: 1px solid var(--border-light);
    backdrop-filter: blur(18px);
    overflow-y: auto;
    z-index: 20;
}

.brand {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 160px;
    margin-bottom: 24px;
    padding: 8px 4px;
    border-bottom: 1px solid var(--border-light);
    overflow: hidden;
}

.brand-logo {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

/* ── THE LOGO IN THE GUEST BAR ──
   .brand above is the sidebar's logo block: 160px tall, a rule under it, room to breathe. The guest
   surface has no sidebar (its navigation is a topbar), so it borrowed the class and inherited a
   block three times the height of the 56px bar holding it. The badge was clipped by the top of the
   window and the block's border-bottom drew a stray line across the corner. Only this surface puts a
   brand in a topbar, so scoping it by the bar is enough; the two sidebars keep theirs. */

.topbar .brand {
    height: 36px;
    margin: 0;
    padding: 0;
    border-bottom: none;
    flex-shrink: 0;
}

.topbar .brand-logo {
    width: auto;
    height: 100%;
}

.menu {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.menu-title {
    margin: 26px 12px 10px;
    color: var(--text-subtle);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
}

.menu-item {
    display: flex;
    align-items: center;
    gap: 12px;
    min-height: 44px;
    padding: 0 14px;
    color: var(--text-nav);
    border-radius: var(--radius-small);
    font-size: 15px;
    transition: color 0.2s ease, background-color 0.2s ease, box-shadow 0.2s ease;
}

.menu-item:hover,
.menu-item.active {
    color: #ffffff;
    background: linear-gradient(90deg, var(--orange), #e46018);
    box-shadow: 0 10px 26px rgba(244, 123, 32, 0.28);
}

.menu-icon {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}


/* MAIN */

.main {
    flex: 1;
    min-width: 0;
    min-height: 100vh;
    margin-left: var(--sidebar-width);
    display: flex;
    flex-direction: column;
}

.topbar {
    position: sticky;
    top: 0;
    z-index: 10;
    height: 56px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 22px;
    background: var(--bg-topbar);
    border-bottom: 1px solid var(--border-light);
    backdrop-filter: blur(18px);
}

.hamburger {
    width: 38px;
    height: 38px;
    border: 1px solid transparent;
    border-radius: var(--radius-small);
    background: transparent;
    color: var(--text-main);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

.hamburger:hover {
    border-color: var(--border-light);
    background: rgba(255, 255, 255, 0.05);
}

[data-theme="light"] .hamburger:hover {
    background: rgba(0, 0, 0, 0.05);
}

@media (min-width: 561px) {
    .hamburger { display: none; }
}


/* LOGIN MENU */

.topbar-end {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-left: auto;
}

/* TOKEN PILL */

.token-pill {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 5px 11px 5px 8px;
    border: 1px solid var(--border-orange);
    border-radius: 999px;
    background: rgba(244, 123, 32, 0.07);
    user-select: none;
    white-space: nowrap;
}

.token-pill [data-lucide] {
    width: 15px;
    height: 15px;
    color: var(--orange);
    flex-shrink: 0;
}

.token-pill-label {
    font-size: 11px;
    font-weight: 500;
    color: var(--text-muted);
    letter-spacing: 0.02em;
}

.token-pill-value {
    font-size: 14px;
    font-weight: 700;
    color: var(--text-badge);
    line-height: 1;
}

@media (max-width: 560px) {
    .token-pill-label { display: none; }
}

.login-menu {
    position: relative;
}

.login-button {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 7px 12px 7px 7px;
    border: 1px solid var(--border-orange);
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.04);
    color: #ffffff;
    cursor: pointer;
}

[data-theme="light"] .login-button { color: var(--text-main); }

.avatar {
    width: 30px;
    height: 30px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: linear-gradient(135deg, #f4b27b, #6d7d91);
}

.login-dropdown {
    position: absolute;
    top: calc(100% + 12px);
    right: 0;
    width: 190px;
    padding: 10px;
    border: 1px solid var(--border-orange);
    border-radius: 14px;
    background: var(--bg-dropdown);
    box-shadow: 0 18px 50px rgba(0, 0, 0, 0.45);

    opacity: 0;
    transform: translateY(-8px);
    pointer-events: none;
    transition: opacity 0.18s ease, transform 0.18s ease;
}

.login-menu.open .login-dropdown {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}

.login-dropdown a,
.login-dropdown button.logout {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px;
    border-radius: 9px;
    color: var(--text-dropdown);
    font-size: 14px;
}

.login-dropdown a:hover,
.login-dropdown button.logout:hover {
    background: rgba(244, 123, 32, 0.14);
    color: #ffffff;
}

[data-theme="light"] .login-dropdown a:hover,
[data-theme="light"] .login-dropdown button.logout:hover { color: var(--text-main); }

.login-dropdown .logout {
    margin-top: 6px;
    border: 1px solid var(--border-light);
    text-align: center;
    justify-content: center;
    width: 100%;
    background: transparent;
    font-family: inherit;
    cursor: pointer;
}


/* CONTENT */

.content {
    position: relative;
    flex: 1;
    display: flex;
    flex-direction: column;
    padding: 28px 22px 40px;
}

.page-heading {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 24px;
    margin-bottom: 22px;
}

/* Two actions in a heading that is a `space-between` row: they go in one box, or the three children
   spread out and the first button lands in the middle of the page. The account page is the only one
   with two today, and it has them because crediting tokens and editing the account are both reached
   from the account. No colour of its own: the buttons bring theirs. */
.page-heading__actions {
    display: flex;
    align-items: center;
    gap: 12px;
}

.subtitle {
    margin: 0 0 8px;
    color: var(--accent-warm-dim);
    font-size: 12px;
    font-weight: 800;
    letter-spacing: 0.16em;
    text-transform: uppercase;
}

.page-heading h1 {
    margin: 0;
    font-family: Georgia, "Times New Roman", serif;
    font-size: clamp(34px, 4vw, 52px);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--accent-warm);
    text-shadow: 0 0 28px rgba(244, 123, 32, 0.2);
}

.home-link {
    color: var(--accent-warm-link);
    font-size: 14px;
}

.section-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 18px;
}

.section-header h2 {
    margin: 0;
    font-size: clamp(24px, 2.4vw, 34px);
    font-weight: 500;
}

.btn-action {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 9px 22px;
    border: none;
    border-radius: 999px;
    background: linear-gradient(180deg, #ff9951, #c85b1d 52%, #8a320c);
    color: #fff;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.05em;
    cursor: pointer;
    box-shadow: 0 6px 18px rgba(244, 123, 32, 0.32), inset 0 1px 0 rgba(255,255,255,0.16);
    transition: filter 0.18s ease, box-shadow 0.18s ease;
    flex-shrink: 0;
}

.btn-action:hover {
    filter: brightness(1.1);
    box-shadow: 0 10px 28px rgba(244, 123, 32, 0.46), inset 0 1px 0 rgba(255,255,255,0.16);
}

/* CARDS */

.cards-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(165px, 1fr));
    gap: 14px;
}

.car-card {
    display: flex;
    flex-direction: column;
    text-decoration: none;
    cursor: pointer;
    border: 1px solid var(--border-light);
    border-radius: var(--radius-card);
    background:
        linear-gradient(180deg, rgba(255, 255, 255, 0.035), transparent),
        var(--bg-card);
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.24);
    overflow: hidden;
    transition: transform 0.2s ease, border-color 0.2s ease,
                background-color 0.2s ease, box-shadow 0.2s ease;
}

.car-card:hover {
    transform: translateY(-3px);
    border-color: var(--border-orange);
    background: var(--bg-card-hover);
    box-shadow:
        0 20px 44px rgba(0, 0, 0, 0.36),
        0 0 22px rgba(244, 123, 32, 0.13);
}

.car-image {
    width: 100%;
    height: 148px;
    flex-shrink: 0;
}

.car-image img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.car-body {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    flex: 1;
    gap: 10px;
    padding: 12px 14px 14px;
}

.car-card h3 {
    margin: 0;
    font-size: 17px;
    font-weight: 700;
    color: var(--text-main);
    line-height: 1.15;
}

.card-button {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 34px;
    border: none;
    border-radius: 999px;
    background: linear-gradient(180deg, #ff9951, #c85b1d 52%, #8a320c);
    color: var(--text-cardbtn);
    font-size: 13px;
    font-weight: 600;
    letter-spacing: 0.02em;
    cursor: pointer;
    box-shadow:
        0 8px 18px rgba(244, 123, 32, 0.34),
        inset 0 1px 0 rgba(255, 255, 255, 0.18);
}

.card-button:hover {
    color: #ffffff;
    filter: brightness(1.1);
}


/* FOOTER */

.footer {
    display: flex;
    justify-content: space-between;
    gap: 20px;
    margin-top: auto;
    padding-top: 16px;
    border-top: 1px solid var(--border-subtle);
    color: var(--text-footer);
    font-size: 13px;
}

.footer-links {
    display: flex;
    gap: 16px;
}


/* SIDEBAR BACKDROP (mobile only) */

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

@media (max-width: 560px) {
    .sidebar-backdrop.open { display: block; }
}


/* ── LUCIDE ICONS ── */

.hamburger svg           { width: 20px; height: 20px; pointer-events: none; }
.avatar svg              { width: 15px; height: 15px; }
.login-dropdown a svg    { width: 14px; height: 14px; flex-shrink: 0; }


/* ── CODINGS HISTORY ──────────────────────────────────────────────────────── */

/* Card container */

.history-card {
    background:
        linear-gradient(160deg, rgba(244, 123, 32, 0.04) 0%, transparent 40%),
        var(--bg-card);
    border: 1px solid var(--border-orange);
    border-radius: 18px;
    box-shadow:
        0 0 0 1px rgba(244, 123, 32, 0.06),
        0 0 40px rgba(244, 123, 32, 0.07),
        0 16px 48px rgba(0, 0, 0, 0.45);
    padding: 22px 24px;
    margin-bottom: 22px;
}

/* White at 3.5% over --bg-card, which is rgba(255,252,248,0.95) in the light theme: the sheen that
   lifts a card off the grid in the dark theme is invisible against a near-white one, and the card
   loses its top edge with nothing saying so. The same defect .revenue-period had, found by the same
   sweep. Black at 2.5%, which is the same gesture the other way up, and the same shape .history-card
   uses immediately below. */
[data-theme="light"] .car-card {
    background:
        linear-gradient(180deg, rgba(0, 0, 0, 0.025), transparent),
        var(--bg-card);
}

[data-theme="light"] .history-card {
    background:
        linear-gradient(160deg, rgba(217, 107, 20, 0.03) 0%, transparent 40%),
        var(--bg-card);
    box-shadow:
        0 0 0 1px rgba(217, 107, 20, 0.05),
        0 0 40px rgba(217, 107, 20, 0.06),
        0 16px 48px rgba(0, 0, 0, 0.08);
}

/* Controls row */

.dt-controls {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
    margin-bottom: 18px;
    flex-wrap: wrap;
}

.dt-show-label,
.dt-search-label {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--text-muted);
    font-size: 13px;
}

.dt-select,
.dt-search-input {
    background: var(--bg-input);
    border: 1px solid var(--border-orange);
    border-radius: var(--radius-small);
    color: var(--text-main);
    font-size: 13px;
    font-family: inherit;
    padding: 6px 10px;
    transition: border-color 0.18s ease, box-shadow 0.18s ease;
}

.dt-select:focus,
.dt-search-input:focus {
    outline: none;
    border-color: var(--orange);
    box-shadow: 0 0 0 2px rgba(244, 123, 32, 0.12);
}

.dt-select {
    padding-right: 28px;
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23f47b20' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 8px center;
    cursor: pointer;
}

.dt-search-input {
    width: 200px;
}

/* Loading / error placeholders inside the table container */

.dt-loading-msg,
.dt-error-msg {
    text-align: center;
    padding: 32px;
    color: var(--text-muted);
}

.dt-error-msg {
    color: var(--error);
}

/* Table (Tabulator; see js/core/dataTable.js) */

.dt-table-wrap {
    overflow: hidden;
    border-radius: 10px;
    border: 1px solid var(--border-subtle);
}

[data-theme="light"] .dt-select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23374151' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}

/* Tabulator container: strip its default theme, the wrap above supplies the border/radius */
.tabulator {
    background: transparent;
    border: none;
    color: var(--text-item);
    font-family: inherit;
    font-size: 13.5px;
}

.tabulator .tabulator-header {
    background: rgba(244, 123, 32, 0.06);
    border-bottom: 1px solid rgba(244, 123, 32, 0.18);
    color: var(--text-subtle);
}

[data-theme="light"] .tabulator .tabulator-header {
    background: rgba(217, 107, 20, 0.05);
    border-bottom-color: rgba(217, 107, 20, 0.15);
}

.tabulator .tabulator-header .tabulator-col {
    background: transparent;
    border-right: none;
}

/* .tabulator-col is required in the selector to outweigh Tabulator's own
   default (same chain + .tabulator-col, padding:4px): dropping it means
   the override silently loses to the CDN stylesheet. */
.tabulator .tabulator-header .tabulator-col .tabulator-col-content {
    padding: 12px 16px;
}

.tabulator .tabulator-header .tabulator-col-content .tabulator-col-title {
    color: var(--text-subtle);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    white-space: nowrap;
}

.tabulator .tabulator-header .tabulator-col.tabulator-sortable:hover {
    background: rgba(244, 123, 32, 0.08);
    cursor: pointer;
}

.tabulator .tabulator-header .tabulator-col-content .tabulator-col-sorter .tabulator-arrow {
    border-bottom-color: var(--text-subtle);
}

.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort="ascending"]  .tabulator-col-content .tabulator-col-sorter .tabulator-arrow,
.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort="descending"] .tabulator-col-content .tabulator-col-sorter .tabulator-arrow {
    border-bottom-color: var(--orange);
    border-top-color:    var(--orange);
}

.tabulator .tabulator-tableholder,
.tabulator .tabulator-tableholder .tabulator-table {
    background: transparent;
}

.tabulator .tabulator-tableholder .tabulator-placeholder {
    color: var(--text-muted);
    padding: 32px;
}

.tabulator-row {
    background: transparent;
    border-bottom: 1px solid var(--border-subtle);
    min-height: unset;
    transition: background-color 0.15s ease;
}

.tabulator-row.tabulator-row-even {
    background: transparent;
}

/* A table whose rows open a page says so under the pointer. The class is added by wireRowLinks, so a
   table that does not navigate never gets the cursor: the promise and the behaviour are one thing. */
.rows-open-their-own-page .tabulator-row {
    cursor: pointer;
}

.tabulator-row:hover {
    background: rgba(244, 123, 32, 0.04) !important;
}

.tabulator-row .tabulator-cell {
    border-right: none;
    padding: 13px 16px;
    color: var(--text-item);
    vertical-align: middle;
    white-space: normal;
}

/* Custom .dt-controls/.dt-footer drive paging: Tabulator's own footer stays hidden */
.tabulator .tabulator-footer {
    display: none;
}

/* State badges */

.state-badge {
    display: inline-flex;
    align-items: center;
    padding: 3px 9px;
    border-radius: 4px;
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.02em;
    white-space: nowrap;
}

.state-completed {
    background: rgba(110, 231, 183, 0.04);
    color: #6ee7b7;
    border: 1px solid rgba(110, 231, 183, 0.20);
    box-shadow: 0 0 10px rgba(110, 231, 183, 0.08);
}

.state-processing {
    background: rgba(251, 191, 36, 0.04);
    color: #fbbf24;
    border: 1px solid rgba(251, 191, 36, 0.20);
    box-shadow: 0 0 10px rgba(251, 191, 36, 0.08);
}

.state-failed {
    background: rgba(248, 113, 113, 0.04);
    color: #f87171;
    border: 1px solid rgba(248, 113, 113, 0.20);
    box-shadow: 0 0 10px rgba(248, 113, 113, 0.08);
}

[data-theme="light"] .state-completed {
    background: rgba(5, 150, 105, 0.06);
    color: #059669;
    border: 1px solid rgba(5, 150, 105, 0.22);
    box-shadow: none;
}

[data-theme="light"] .state-processing {
    background: rgba(180, 83, 9, 0.06);
    color: #b45309;
    border: 1px solid rgba(180, 83, 9, 0.22);
    box-shadow: none;
}

[data-theme="light"] .state-failed {
    background: rgba(220, 38, 38, 0.06);
    color: #dc2626;
    border: 1px solid rgba(220, 38, 38, 0.22);
    box-shadow: none;
}

/* The only one of the four state badges with no [data-theme="light"] twin, and it needs none: its
   colour is var(--text-muted), which is already defined per theme, and slate at 6% reads as a faint
   grey on either background. The other three hard-code a green, an amber and a red, which is exactly
   why they each need one. Said here because "the fourth badge has no light rule" looks like the
   .revenue-period defect and is not: a counter-review raised it as one. */
.state-unknown {
    background: rgba(148, 163, 184, 0.06);
    color: var(--text-muted);
    border: 1px solid rgba(148, 163, 184, 0.20);
}

/* Pagination */

.dt-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
    margin-top: 18px;
    flex-wrap: wrap;
}

.dt-info {
    color: var(--text-muted);
    font-size: 13px;
}

.dt-pagination {
    display: flex;
    gap: 8px;
}

.dt-page-btn {
    padding: 7px 18px;
    border: 1px solid var(--border-orange);
    border-radius: 999px;
    background: var(--bg-input);
    color: var(--text-nav);
    font-size: 13px;
    font-family: inherit;
    cursor: pointer;
    transition: background-color 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}

.dt-page-btn:hover:not(:disabled) {
    background: rgba(244, 123, 32, 0.14);
    border-color: var(--orange);
    color: #ffffff;
}

/* ── WHITE ON A HOVER THAT IS ONLY PALE IN ONE THEME ──
   The hover above replaces the control's background with orange at 14%, which over a dark surface is
   dark enough to carry white text and over a light one is a pale peach. The label then went white on
   near-white and the button read as empty for as long as the pointer was on it: the state that says
   "this is the thing you are about to click" was the one state you could not read.

   Four controls do this and none of them had a twin: the two paging controls here, .menu-subitem and
   .btn-secondary. The remedy is the file's own: [data-theme="light"] .login-button and
   .login-dropdown a:hover already swap the same white for --text-main, which is the only reason the
   account menu did not have the defect too. Not the buttons whose hover background is the orange
   gradient or solid --orange: those are orange in both themes and white is right on them. */
[data-theme="light"] .dt-page-btn:hover:not(:disabled) { color: var(--text-main); }

.dt-page-btn:disabled {
    opacity: 0.35;
    cursor: not-allowed;
}

.dt-page-numbers {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-wrap: wrap;
}

.dt-page-num {
    min-width: 34px;
    height: 34px;
    padding: 0 6px;
    border: 1px solid var(--border-orange);
    border-radius: 6px;
    background: var(--bg-input);
    color: var(--text-nav);
    font-size: 13px;
    font-family: inherit;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background-color 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}

.dt-page-num:hover:not(.active) {
    background: rgba(244, 123, 32, 0.14);
    border-color: var(--orange);
    color: #ffffff;
}

/* .dt-page-num.active below keeps its white: its background is solid --orange, not a 14% wash. */
[data-theme="light"] .dt-page-num:hover:not(.active) { color: var(--text-main); }

.dt-page-num.active {
    background: var(--orange);
    border-color: var(--orange);
    color: #ffffff;
    font-weight: 600;
    cursor: default;
    pointer-events: none;
}

.dt-page-ellipsis {
    display: inline-flex;
    align-items: center;
    height: 34px;
    padding: 0 4px;
    color: var(--text-muted);
    font-size: 13px;
    user-select: none;
}

@media (prefers-reduced-motion: reduce) {
    .dt-page-num { transition: none; }
}


/* ── ZENZEFI BACK TIME ──────────────────────────────────────────────────────── */

.breadcrumb {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
}

.breadcrumb-sep {
    color: var(--text-dim);
    user-select: none;
}

.breadcrumb-item {
    color: var(--text-muted);
}

.zenzefi-card {
    max-width: 960px;
    width: 100%;
    margin: 0 auto;
    padding: 36px 40px;
}

.zenzefi-meta {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-bottom: 28px;
    font-size: 14px;
    color: var(--text-muted);
}

.zenzefi-meta-value {
    color: var(--text-main);
    font-size: 13px;
}

.zenzefi-divider {
    border: none;
    border-top: 1px solid var(--border-subtle);
    margin: 28px 0;
}

.zenzefi-instructions {
    font-size: 14px;
    color: var(--text-muted);
    line-height: 1.7;
    margin-bottom: 28px;
}

.zenzefi-instructions code {
    display: inline-block;
    font-family: "Courier New", Courier, monospace;
    font-size: 13px;
    background: rgba(244, 123, 32, 0.08);
    border: 1px solid rgba(244, 123, 32, 0.18);
    padding: 2px 8px;
    border-radius: 5px;
    color: var(--text-main);
    letter-spacing: 0.02em;
    margin: 2px 0;
}

[data-theme="light"] .zenzefi-instructions code {
    background: rgba(244, 123, 32, 0.06);
    border-color: rgba(244, 123, 32, 0.22);
}

.zenzefi-label {
    display: block;
    font-size: 11px;
    font-weight: 700;
    color: var(--text-muted);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    margin-bottom: 8px;
}

.zenzefi-textarea {
    width: 100%;
    min-height: 160px;
    padding: 14px 16px;
    background: var(--bg-input);
    border: 1px solid var(--border-orange);
    border-radius: var(--radius-small);
    color: var(--text-main);
    font-size: 13px;
    font-family: "Courier New", Courier, monospace;
    line-height: 1.6;
    resize: vertical;
    transition: border-color 0.18s ease, box-shadow 0.18s ease;
    display: block;
    margin-bottom: 10px;
}

.zenzefi-textarea:focus {
    outline: none;
    border-color: var(--orange);
    box-shadow: 0 0 0 3px rgba(244, 123, 32, 0.14);
}

.zenzefi-textarea::placeholder {
    color: var(--text-dim);
    font-family: "Courier New", Courier, monospace;
}

.zenzefi-field-error {
    display: block;
    font-size: 12px;
    color: var(--error);
    min-height: 18px;
    margin-bottom: 20px;
}

.btn-action-full {
    width: 100%;
    justify-content: center;
}


/* ────────────────────────────────────────────────
   TOKENS SIDEBAR DROPDOWN
   ──────────────────────────────────────────────── */

.menu-item-parent {
    width: 100%;
    text-align: left;
    background: transparent;
    border: none;
    font-size: 15px;
    font-family: inherit;
    cursor: pointer;
}

.menu-dropdown-arrow {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    margin-left: auto;
    color: var(--text-muted);
    transition: transform 0.2s ease;
}

.menu-item-parent[aria-expanded="true"] .menu-dropdown-arrow {
    transform: rotate(180deg);
}

.menu-submenu {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding-left: 10px;
    overflow: hidden;
    max-height: 120px;
    opacity: 1;
    transition: max-height 0.28s ease, opacity 0.20s ease;
}

.menu-submenu--closed {
    max-height: 0;
    opacity: 0;
    pointer-events: none;
}

.menu-subitem {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 36px;
    padding: 0 12px 0 10px;
    color: var(--text-nav);
    border-radius: var(--radius-small);
    font-size: 13px;
    text-decoration: none;
    transition: color 0.18s ease, background-color 0.18s ease;
}

.menu-subitem:hover {
    color: #ffffff;
    background: rgba(244, 123, 32, 0.14);
}

/* Over --bg-sidebar, which is near-white in this theme. See the note at .dt-page-btn:hover.
   :not(.active) is load-bearing: .menu-subitem.active below ties with .menu-subitem:hover on
   specificity and wins on order, so hovering the item you are already on leaves it orange. Without the
   exclusion this rule would out-specify that and change a state it has no business changing. */
[data-theme="light"] .menu-subitem:hover:not(.active) { color: var(--text-main); }

.menu-subitem.active {
    color: var(--orange);
    background: rgba(244, 123, 32, 0.10);
    font-weight: 500;
}

.menu-icon-sm {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    opacity: 0.75;
}

.menu-subitem.active .menu-icon-sm,
.menu-subitem:hover .menu-icon-sm {
    opacity: 1;
}

/* The other half of the icon rail (see the note at the responsive block). A 76px rail has nowhere to
   put a submenu, so it stays shut there and the chevron that would promise otherwise is hidden.
   <strong>Only there.</strong> Without the 561px floor this also held the drawer's submenus shut on a
   phone, and the `!important` is what made that unfixable from the JavaScript: the class was being
   toggled correctly and the declaration outranked it. Scoping to `.sidebar` now out-specifies
   `.menu-submenu--closed` on its own, so nothing here has to shout. */

@media (min-width: 561px) and (max-width: 900px) {
    .sidebar .menu-submenu        { max-height: 0; opacity: 0; pointer-events: none; }
    .sidebar .menu-dropdown-arrow { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    .menu-dropdown-arrow { transition: none; }
    .menu-submenu        { transition: none; }
}


/* ────────────────────────────────────────────────
   BUY TOKENS PAGE
   ──────────────────────────────────────────────── */

.token-purchase-grid {
    display: grid;
    grid-template-columns: 1fr;
    max-width: 520px;
    gap: 20px;
    margin-bottom: 24px;
}

.token-card-heading {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
}

.token-card-heading h3 {
    margin: 0;
    font-size: 16px;
    font-weight: 600;
    color: var(--text-main);
}

.token-card-desc {
    font-size: 13px;
    color: var(--text-muted);
    margin-bottom: 20px;
    line-height: 1.55;
}

.token-form-group {
    margin-bottom: 14px;
}

.token-qty-input {
    width: 100%;
    background: var(--bg-input);
    border: 1px solid var(--border-orange);
    border-radius: var(--radius-small);
    color: var(--text-main);
    font-size: 15px;
    font-family: inherit;
    padding: 9px 12px;
    transition: border-color 0.18s ease, box-shadow 0.18s ease;
    box-sizing: border-box;
}

.token-qty-input:focus {
    outline: none;
    border-color: var(--orange);
    box-shadow: 0 0 0 2px rgba(244, 123, 32, 0.12);
}

.token-qty-input::placeholder { color: var(--text-dim); }

.token-qty-input::-webkit-inner-spin-button,
.token-qty-input::-webkit-outer-spin-button {
    -webkit-appearance: none;
    opacity: 1;
    width: 24px;
    background-color: rgba(244, 123, 32, 0.12);
    background-image:
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath fill='%23f47b20' d='M5 0l5 6H0z'/%3E%3C/svg%3E"),
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath fill='%23f47b20' d='M5 6l5-6H0z'/%3E%3C/svg%3E");
    background-repeat: no-repeat, no-repeat;
    background-position: center 28%, center 72%;
    background-size: 8px 5px, 8px 5px;
    border-left: 1px solid var(--border-orange);
    border-radius: 0 var(--radius-small) var(--radius-small) 0;
    cursor: pointer;
    transition: background-color 0.18s ease;
}

.token-qty-input::-webkit-inner-spin-button:hover,
.token-qty-input::-webkit-outer-spin-button:hover {
    background-color: rgba(244, 123, 32, 0.28);
}

.token-price-display {
    margin-bottom: 18px;
    padding: 12px 16px;
    background: rgba(244, 123, 32, 0.05);
    border: 1px solid rgba(244, 123, 32, 0.14);
    border-radius: var(--radius-small);
}

.token-price-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    font-size: 13px;
    color: var(--text-muted);
}

.token-total-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 13px;
    color: var(--text-muted);
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid rgba(244, 123, 32, 0.10);
}

.token-total-amount {
    font-size: 22px;
    font-weight: 700;
    color: var(--orange);
}

.token-btn-group {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.btn-secondary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 9px 22px;
    border: 1px solid var(--border-orange);
    border-radius: 999px;
    background: transparent;
    color: var(--text-nav);
    font-size: 13px;
    font-weight: 600;
    letter-spacing: 0.04em;
    cursor: pointer;
    font-family: inherit;
    transition: background-color 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}

.btn-secondary:hover {
    background: rgba(244, 123, 32, 0.10);
    border-color: var(--orange);
    color: #ffffff;
}

/* The worst of the four: .btn-secondary is transparent, so its 10% wash sits straight on the page, and
   this is the guest bar's "Sign in". See the note at .dt-page-btn:hover. */
[data-theme="light"] .btn-secondary:hover { color: var(--text-main); }

.purchase-feedback {
    display: block;
    margin-top: 12px;
    padding: 10px 14px;
    border-radius: var(--radius-small);
    font-size: 13px;
    line-height: 1.45;
}

.purchase-feedback.error {
    background: rgba(224, 84, 84, 0.09);
    color: var(--error);
    border: 1px solid rgba(224, 84, 84, 0.22);
}

.purchase-feedback.info {
    background: rgba(244, 123, 32, 0.07);
    color: var(--orange);
    border: 1px solid rgba(244, 123, 32, 0.18);
}


/* ────────────────────────────────────────────────
   FAQ PAGE
   ──────────────────────────────────────────────── */

.faq-list {
    display: flex;
    flex-direction: column;
    gap: 16px;
    margin-bottom: 24px;
}

.faq-card {
    background: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-card);
    overflow: hidden;
}

.faq-card-header {
    background: rgba(244, 123, 32, 0.10);
    border-bottom: 1px solid rgba(244, 123, 32, 0.15);
    padding: 14px 20px;
}

:root[data-theme="light"] .faq-card-header {
    background: rgba(217, 107, 20, 0.08);
    border-bottom-color: rgba(217, 107, 20, 0.14);
}

.faq-card-title {
    font-size: 14px;
    font-weight: 600;
    color: var(--orange);
    margin: 0;
    line-height: 1.4;
}

.faq-card-body {
    padding: 16px 20px;
}

.faq-card-answer {
    font-size: 13px;
    color: var(--text-muted);
    line-height: 1.7;
    margin: 0;
    /* An answer is written in an admin textarea and its line breaks are meant. The old backend put
       them back with Twig's |nl2br; this does the same without a template that unescapes anything. */
    white-space: pre-line;
}

.faq-empty-state {
    padding: 52px 24px;
    text-align: center;
    color: var(--text-muted);
    font-size: 14px;
}


/* ────────────────────────────────────────────────
   DOWNLOADS PAGE
   ──────────────────────────────────────────────── */

.dl-heading {
    font-size: 18px;
    font-weight: 600;
    color: var(--text-main);
    margin: 0 0 10px 0;
}

.dl-intro {
    font-size: 14px;
    color: var(--text-muted);
    line-height: 1.6;
    margin: 0 0 26px 0;
}

.dl-section-label {
    display: block;
    font-size: 11px;
    font-weight: 700;
    color: var(--text-muted);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    margin-bottom: 14px;
}

.dl-steps {
    margin: 0 0 28px 0;
    padding-left: 20px;
    list-style: decimal;
}

.dl-steps li {
    font-size: 14px;
    color: var(--text-item);
    line-height: 1.65;
    padding: 5px 0 5px 6px;
}

.dl-steps li + li {
    border-top: 1px solid var(--border-subtle);
}

.dl-notice {
    background: rgba(244, 123, 32, 0.06);
    border: 1px solid rgba(244, 123, 32, 0.18);
    border-radius: var(--radius-small);
    padding: 16px 18px;
    margin-bottom: 30px;
}

.dl-notice-title {
    font-size: 11px;
    font-weight: 700;
    color: var(--orange);
    letter-spacing: 0.10em;
    text-transform: uppercase;
    margin: 0 0 6px 0;
}

.dl-notice-text {
    font-size: 13px;
    color: var(--text-muted);
    line-height: 1.55;
    margin: 0;
    /* Three sentences the old backend wrote as three lines, and meant as three. */
    white-space: pre-line;
}

.dl-btn {
    gap: 8px;
    /* .content is a flex column, so a direct child is stretched to the full width whatever its own
       display says, and .btn-action does not centre its contents, so the button spanned the page
       with its label against the left edge. In the design it sits inside a card and never becomes a
       flex item of the page. This makes it shrink to its label again. */
    align-self: flex-start;
}


/* ────────────────────────────────────────────────
   CUSTOM SCROLLBAR
   ──────────────────────────────────────────────── */

::-webkit-scrollbar {
    width: 6px;
    height: 6px;
}

::-webkit-scrollbar-track {
    background: rgba(255, 255, 255, 0.03);
    border-radius: 999px;
}

::-webkit-scrollbar-thumb {
    background: rgba(244, 123, 32, 0.28);
    border-radius: 999px;
}

::-webkit-scrollbar-thumb:hover {
    background: rgba(244, 123, 32, 0.55);
}

::-webkit-scrollbar-corner {
    background: transparent;
}

/* Firefox */
* {
    scrollbar-width: thin;
    scrollbar-color: rgba(244, 123, 32, 0.28) rgba(255, 255, 255, 0.03);
}

/* Light theme adjustments */
:root[data-theme="light"] ::-webkit-scrollbar-track {
    background: rgba(0, 0, 0, 0.05);
}

:root[data-theme="light"] ::-webkit-scrollbar-thumb {
    background: rgba(200, 90, 10, 0.28);
}

/* And the same for Firefox, which had been left out. `scrollbar-color` above names the track as white at
   3%, the exact .revenue-period defect, on the one engine none of the light-theme work was looked at in:
   on a light background that track is invisible, so the scrollbar loses its channel and the thumb floats.
   WebKit got its twin on the rule above; this is the same pair of colours through the property Firefox
   reads. Found on 8 August 2026 by scanning for the shape rather than by looking at a page. */
:root[data-theme="light"] * {
    scrollbar-color: rgba(200, 90, 10, 0.28) rgba(0, 0, 0, 0.05);
}

:root[data-theme="light"] ::-webkit-scrollbar-thumb:hover {
    background: rgba(200, 90, 10, 0.52);
}

:root[data-theme="light"] * {
    scrollbar-color: rgba(200, 90, 10, 0.28) rgba(0, 0, 0, 0.05);
}


/* ────────────────────────────────────────────────
   PROFILE PAGE
   ──────────────────────────────────────────────── */

/* Two-column section grid */
.prof-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 20px;
    margin-bottom: 20px;
}

.prof-grid > .history-card {
    margin-bottom: 0;
}

@media (max-width: 860px) {
    .prof-grid { grid-template-columns: 1fr; }
}

/* Account info rows */
.prof-info-list {
    display: flex;
    flex-direction: column;
    margin-bottom: 18px;
}

.prof-info-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 0;
    border-bottom: 1px solid var(--border-subtle);
    font-size: 13px;
}

.prof-info-row:first-child {
    border-top: 1px solid var(--border-subtle);
}

.prof-info-label {
    color: var(--text-muted);
    flex-shrink: 0;
}

.prof-info-value {
    color: var(--text-main);
    text-align: right;
    font-weight: 500;
    word-break: break-all;
}

/* Token counts in account info */
.prof-token-info {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 12px 16px;
    background: rgba(244, 123, 32, 0.05);
    border: 1px solid rgba(244, 123, 32, 0.12);
    border-radius: var(--radius-small);
}

.prof-token-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    font-size: 13px;
}

.prof-token-label {
    color: var(--text-muted);
    display: flex;
    align-items: center;
    gap: 5px;
}

.prof-token-icon {
    width: 13px;
    height: 13px;
    flex-shrink: 0;
}

.prof-token-count {
    font-size: 22px;
    font-weight: 700;
    color: var(--orange);
    line-height: 1;
}

.prof-token-unit {
    font-size: 12px;
    color: var(--text-dim);
    margin-left: 4px;
}

/* One-time access token */
/* The three levels sit on one row and wrap on a narrow screen rather than overflowing it. */
.prof-otp-levels {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

/* The two administrator-only levels, in the colours the team names them by. Both hand out more than a
   customer is ever given (logs kept in, or the SDK in the clear), so they are meant to look unlike the
   ordinary button rather than sit quietly beside it. */
.btn-action.btn-keep-logs {
    background: linear-gradient(180deg, #ffd166, #d9a412 52%, #8a6a06);
    color: #3a2c00;
}

.btn-action.btn-no-obfuscation {
    background: linear-gradient(180deg, #ff7a7a, #c82121 52%, #7a0d0d);
}

.prof-otp-levels-note {
    margin-top: 10px;
    font-size: 12px;
    opacity: 0.75;
}

.prof-otp-code {
    flex: 1;
    font-family: "Courier New", Courier, monospace;
    font-size: 14px;
    font-weight: 700;
    color: var(--orange);
    letter-spacing: 0.06em;
    word-break: break-all;
    min-width: 0;
}

/* Profile form inputs */
.prof-form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: 14px;
    row-gap: 14px;
    margin-bottom: 14px;
}

@media (max-width: 600px) {
    .prof-form-grid { grid-template-columns: 1fr; }
}

.prof-form-group {
    display: flex;
    flex-direction: column;
    gap: 5px;
    margin-bottom: 14px;
    min-width: 0;
}

.prof-form-group--inline {
    margin-bottom: 0;
}

.prof-input {
    width: 100%;
    background: var(--bg-input);
    border: 1px solid var(--border-orange);
    border-radius: var(--radius-small);
    color: var(--text-main);
    font-size: 14px;
    font-family: inherit;
    padding: 9px 12px;
    transition: border-color 0.18s ease, box-shadow 0.18s ease;
}

.prof-input:focus {
    outline: none;
    border-color: var(--orange);
    box-shadow: 0 0 0 2px rgba(244, 123, 32, 0.12);
}

.prof-input::placeholder { color: var(--text-dim); }

.prof-input.invalid {
    border-color: var(--error);
    box-shadow: 0 0 0 2px rgba(224, 84, 84, 0.10);
}

/* Password field with visibility toggle */
.prof-pw-wrapper {
    position: relative;
}

.prof-pw-wrapper .prof-input {
    padding-right: 42px;
}

.prof-pw-toggle {
    position: absolute;
    top: 50%;
    right: 10px;
    transform: translateY(-50%);
    width: 28px;
    height: 28px;
    background: none;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 4px;
    transition: color 0.15s ease;
    padding: 0;
}

.prof-pw-toggle:hover { color: var(--text-main); }
.prof-pw-toggle svg   { width: 15px; height: 15px; display: block; }

/* Field error */
.prof-field-error {
    font-size: 12px;
    color: var(--error);
    min-height: 16px;
    line-height: 1.3;
}

/* The billing type segmented switch that stood here (.billing-type-switch and .billing-type-option,
   eight rules and a light-theme hover) has been removed along with the profile script's handler for
   it. There is no individual/company switch on the profile page and there was never meant to be one:
   nothing in the schema stores which of the two an account is, so the design's control was left out
   deliberately (app/profile.html says so in its own header). Styling a control the product has decided
   not to have is how the handler in pages/profile.js kept looking plausible. */

/* QR code placeholder */
.prof-qr-wrap {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 20px;
    background: rgba(244, 123, 32, 0.04);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-small);
    margin-bottom: 18px;
    text-align: center;
}

.prof-qr-note {
    font-size: 12px;
    color: var(--text-muted);
    line-height: 1.5;
    margin: 0;
}

/* 2FA status indicator */
.prof-2fa-status {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    background: rgba(244, 123, 32, 0.04);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-small);
    margin-bottom: 18px;
    font-size: 13px;
    color: var(--text-muted);
}

.prof-2fa-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
    background: var(--text-dim);
    transition: background-color 0.25s ease, box-shadow 0.25s ease;
}

.prof-2fa-status.enabled {
    border-color: rgba(110, 231, 183, 0.22);
    background: rgba(110, 231, 183, 0.04);
}

.prof-2fa-status.enabled .prof-2fa-dot {
    background: #6ee7b7;
    box-shadow: 0 0 6px rgba(110, 231, 183, 0.45);
}

[data-theme="light"] .prof-2fa-status.enabled {
    background: rgba(5, 150, 105, 0.05);
    border-color: rgba(5, 150, 105, 0.20);
}

[data-theme="light"] .prof-2fa-status.enabled .prof-2fa-dot {
    background: #059669;
    box-shadow: none;
}

/* Billing form submit button: extra top clearance from the last field row */
#billingForm > .btn-action-full {
    margin-top: 20px;
}

@media (prefers-reduced-motion: reduce) {
    .prof-pw-toggle { transition: none; }
    .prof-2fa-dot   { transition: none; }
}


/* ────────────────────────────────────────────────
   MODEL DETAIL / AVAILABLE CODINGS PAGE
   ──────────────────────────────────────────────── */

/* Vehicle hero card */
.model-hero {
    display: flex;
    align-items: center;
    gap: 20px;
    padding: 20px;
    background: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-card);
    margin-bottom: 24px;
    flex-wrap: wrap;
}

.model-hero__image {
    width: 160px;
    height: 100px;
    flex-shrink: 0;
    border-radius: var(--radius-small);
    overflow: hidden;
    background: var(--bg-input);
}

.model-hero__image img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.model-hero__info {
    flex: 1;
    min-width: 160px;
}

.model-hero__series {
    display: inline-block;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-badge);
    background: rgba(244, 123, 32, 0.12);
    border: 1px solid rgba(244, 123, 32, 0.22);
    border-radius: 99px;
    padding: 2px 8px;
    margin-bottom: 6px;
}

.model-hero__name {
    margin: 0 0 4px 0;
    font-size: 22px;
    font-weight: 700;
    color: var(--text-main);
    line-height: 1.2;
}

.model-hero__meta {
    margin: 0;
    font-size: 13px;
    color: var(--text-muted);
}

.model-hero__back {
    /* Looks come from .btn-secondary; this only places it and keeps its label on one line.
       It used to be a 13px link in --text-muted, which reads as a caption on a page whose every
       other affordance is a pill, so the one control that leaves the page was the one nobody saw. */
    margin-left: auto;
    white-space: nowrap;
}

.model-hero__back [data-lucide] {
    width: 15px;
    height: 15px;
    flex-shrink: 0;
}

@media (max-width: 600px) {
    .model-hero { gap: 14px; }
    .model-hero__image { width: 100%; height: 140px; }
    .model-hero__back { margin-left: 0; }
}


/* Category block */
.ac-category {
    background: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-card);
    overflow: hidden;
}

.ac-category__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 14px 20px;
    border-bottom: 1px solid var(--border-subtle);
}

.ac-category__title {
    margin: 0;
    font-size: 11px;
    font-weight: 700;
    color: var(--text-main);
    letter-spacing: 0.12em;
    text-transform: uppercase;
}

.ac-category__count {
    flex-shrink: 0;
    font-size: 11px;
    font-weight: 700;
    color: var(--text-badge);
    background: rgba(244, 123, 32, 0.12);
    border: 1px solid rgba(244, 123, 32, 0.22);
    border-radius: 99px;
    padding: 1px 8px;
}

/* Variants grid: gap-pixel between cells via background trick */
.ac-variants {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1px;
    background: var(--border-subtle);
}

/* ── A WORD IN THE CORNER ──
   The editors save as they are used, so there is no reload to carry a banner and no button whose press
   was the acknowledgement. This is what is left: one line, bottom right, gone in a couple of seconds.
   It never takes a click (`pointer-events: none`), because it sits over the page somebody is working
   in, and a message that can swallow a drag is worse than none. */
/* TOP RIGHT, AND NOT BOTTOM RIGHT. This back office is used on wide screens, where the bottom right
   corner is the far end of a window nobody is looking at: a message there is a message nobody reads,
   which for the only acknowledgement an editor gives is the same as no acknowledgement at all. The eye
   is at the top of the page, where the editors are.

   It sits just under the 56px topbar rather than over it: the toggle and the account menu live in that
   corner, and covering them for three seconds to say "saved" trades one thing for another. Under it is
   still the corner, and still the first place the eye lands. */
.toast {
    position: fixed;
    top: 68px;
    right: 18px;
    z-index: 60;
    display: flex;
    align-items: center;
    gap: 11px;
    max-width: min(92vw, 46ch);
    padding: 15px 22px;
    border: 1px solid var(--border-light);
    border-left: 5px solid #6ee7b7;
    border-radius: 8px;
    background: var(--bg-dropdown);
    color: #6ee7b7;
    font-size: 15px;
    font-weight: 600;
    line-height: 1.35;
    box-shadow: 0 16px 44px rgba(0, 0, 0, 0.5);
    pointer-events: none;
    opacity: 0;
    transform: translateY(-12px);
    transition: opacity 170ms ease-out, transform 170ms ease-out;
}

/* A spot of the colour, and empty on purpose: this is a live region, and generated content that carried
   a word would be a word the screen reader announced before the sentence. */
.toast::before {
    content: "";
    flex: none;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: currentColor;
}

.toast--in {
    opacity: 1;
    transform: translateY(0);
}

/* A refusal is the one an operator must not walk past, so it takes the semantic red and stays more than
   twice as long: the duration is the script's; this is only what it looks like. */
.toast--failed {
    border-left-color: var(--error);
    color: var(--error);
}

/* The green is the one .form-message.success and .state-completed already use, so "it worked" is one
   colour across the application. Red needs saying again only to outrank the rule above it. */
[data-theme="light"] .toast {
    border-left-color: #059669;
    color: #059669;
}

[data-theme="light"] .toast--failed {
    border-left-color: var(--error);
    color: var(--error);
}

@media (prefers-reduced-motion: reduce) {
    .toast { transition: none; transform: none; }
    .toast--in { transform: none; }
}

/* ── THE CARS, GROUPED BY IGNITION SWITCH ──
   The page is read far more often than it is edited, so every control that is not needed to read it is
   folded away. It used to draw, per car, a label, a select of every EZS and a button; a tree of sixty
   cars was sixty selects with the list of cars somewhere inside them.

   Only variables here, no bare rgba: every neutral in this block already carries its light twin in
   base.css, which is what LightThemeTwinTest asks about. */

/* One sentence under the heading saying why the page is grouped at all. Filing a car under the wrong
   EZS offers the customer the wrong codings and nothing else in the application says so. */
.page-lead {
    margin: -6px 0 22px;
    max-width: 78ch;
    font-size: 13px;
    line-height: 1.6;
    color: var(--text-muted);
}

/* Add a car: a <details> whose <summary> is the button. `display: block` is required: a summary is
   `display: list-item` by default and the marker would sit inside the button. */
.add-car {
    padding-top: 14px;
    padding-bottom: 14px;
}

.add-car[open] {
    padding-bottom: 22px;
}

.add-car__toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    width: auto;
    padding: 9px 18px;
    border: 1px solid var(--border-orange);
    border-radius: 999px;
    list-style: none;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-main);
    cursor: pointer;
}

.add-car__toggle::-webkit-details-marker { display: none; }
.add-car__toggle::marker { content: ""; }

/* A "+" that becomes a "−" once the form is open, so the control says which way it goes. */
.add-car__toggle::before {
    content: "+";
    font-size: 15px;
    line-height: 1;
}

.add-car[open] > .add-car__toggle::before {
    content: "−";
}

.add-car[open] > .add-car__toggle {
    margin-bottom: 18px;
}

/* Three fields on one line where there is room: a model code, a name and an EZS are one thought. */
.add-car__fields {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: 0 18px;
}

/* One car per line: what it is, what it is called, and the way to move it. */
.car-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.car-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px 12px;
    padding: 9px 12px;
    border-radius: var(--radius-small);
    border: 1px solid transparent;
}

.car-row:hover {
    background: var(--bg-item);
    border-color: var(--border-light);
}

.car-row__name {
    flex: 1;
    min-width: 0;
    font-size: 14px;
    color: var(--text-main);
}

/* The model code is what the desktop client sends, so it is set as code and not as prose. */
.car-row__code {
    padding: 2px 8px;
    border-radius: 6px;
    background: var(--field-bg);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 12px;
    color: var(--text-subtle);
}

/* A car is dragged from one EZS to another, the same gesture as a back-time step one page over. The
   grip says so before the pointer is on the row. */
.car-row__grip {
    color: var(--text-subtle);
    font-size: 14px;
    line-height: 1;
    cursor: grab;
}

.car-row[draggable="true"] {
    cursor: grab;
}

.car-row--dragging {
    opacity: 0.5;
    cursor: grabbing;
}

/* The list is always rendered, so a group that starts empty still has somewhere to drop onto. */
.car-list--target {
    outline: 1px solid var(--border-orange);
    outline-offset: 4px;
    border-radius: var(--radius-small);
}

/* The count and the save status share the right of the group's header. */
.car-group__meta {
    display: flex;
    align-items: baseline;
    gap: 12px;
}

/* An editor with a save in flight: out of reach, and visibly so. `inert` is what takes it out of the
   tab order and out of hit testing; this is only what says that it is. */
.is-saving-region {
    opacity: 0.55;
    transition: opacity 120ms ease-out;
}

@media (prefers-reduced-motion: reduce) {
    .is-saving-region { transition: none; }
}

/* One dot beside the thing that changed, in place of a button nobody has to press.

   A DOT AND NOT THE WORD. Every save already raises a toast, so the word stood twice on the screen a
   hand's width apart, and was announced twice as well: the toast is a live region and so was this.
   What the toast cannot say is *which*: these editors save one route, one group, one row at a time.
   So this element keeps the position and gives up the sentence. It says where, the toast says what.

   Which makes grey the state it carries alone: nothing is toasted while a save is in flight, so a grey
   dot is the only thing that says this row is the one waiting.

   ITS BOX IS ALWAYS DRAWN. Collapsing it moved everything beside it every time a save landed: on the
   firmware page it shunted the checkbox out from under the pointer that had just ticked it. `display`
   here and `visibility` below are one rule in two halves: the first is what stops `hidden` removing the
   box, and the second is the only reason it is still invisible. Neither works alone. */
.car-group__status,
.ecu-status,
.fw-revisions__status,
.fw-sequence__status {
    display: inline-block;
    flex: none;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--text-subtle);
    transition: background-color 0.15s ease;
}

.car-group__status[hidden],
.ecu-status[hidden],
.fw-revisions__status[hidden],
.fw-sequence__status[hidden] {
    display: inline-block;
    visibility: hidden;
}

.car-group__status.is-saved,
.ecu-status.is-saved,
.fw-revisions__status.is-saved,
.fw-sequence__status.is-saved {
    background: var(--accent-warm);
}

.car-group__status.is-failed,
.ecu-status.is-failed,
.fw-revisions__status.is-failed,
.fw-sequence__status.is-failed {
    background: var(--error);
}

@media (prefers-reduced-motion: reduce) {
    .car-group__status,
    .ecu-status,
    .fw-revisions__status,
    .fw-sequence__status { transition: none; }
}

/* The heading of one sequence and the word that says whether its last edit landed. */
.fw-sequence__head {
    display: flex;
    align-items: baseline;
    gap: 12px;
}

/* Reserving a route is a setting on the route, so it sits in its heading rather than under its steps,
   pushed right and ahead of the status word: it is read once, and the status is read after every edit.
   The control is .form-check and the application's own .check-input: nothing here redraws it, because
   an eighteenth checkbox that looks like no other is a worse answer than the seventeen. Only the two
   things the heading needs are set: where it sits, and the padding a form row does not want inside a
   line of one. */
.fw-sequence__reserved {
    margin-left: auto;
    /* The heading aligns on the baseline; this is a box and has none worth aligning to. */
    align-self: center;
    padding: 0;
}

/* Shut, the move control is one quiet word at the end of the row; open, it is a form on its own line. */
.car-move {
    margin-left: auto;
}

.car-move__toggle {
    list-style: none;
    padding: 4px 10px;
    border-radius: 999px;
    border: 1px solid transparent;
    font-size: 12px;
    color: var(--text-subtle);
    cursor: pointer;
}

.car-move__toggle::-webkit-details-marker { display: none; }
.car-move__toggle::marker { content: ""; }

.car-move__toggle:hover,
.car-move[open] > .car-move__toggle {
    border-color: var(--border-orange);
    color: var(--text-main);
}

/* Full width when open, so the row it belongs to does not reflow around it. */
.car-move[open] {
    flex-basis: 100%;
    margin-left: 0;
    margin-top: 8px;
}

.car-move__form {
    display: flex;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 10px;
    padding: 12px;
    border-radius: var(--radius-small);
    background: var(--field-bg);
}

.car-move__form .form-label {
    margin: 0 0 4px;
}

.car-move__form .form-input {
    width: auto;
    min-width: 180px;
}

@media (max-width: 560px) {
    .car-move__form,
    .car-move__form .form-input {
        width: 100%;
    }
}

@media (max-width: 760px) {
    .ac-variants { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
    .model-hero__back { transition: none; }
}

/* ── TABS ──────────────────────────────────────────────────────────────────────
   Not from the mockup: it groups codings under category headings, and a coding can
   be in more than one category, which a heading cannot express. Built out of the
   vocabulary already here: the .ac-category__count pill, the orange accent, the
   .menu-item.active treatment, so it reads as part of the same page.           */

.tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 18px;
}

.tab {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    border: 1px solid var(--border-subtle);
    border-radius: 99px;
    background: transparent;
    color: var(--text-muted);
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}

.tab:hover {
    color: var(--text-main);
    border-color: var(--border-orange);
}

.tab.active {
    color: var(--text-main);
    border-color: var(--border-orange);
    background: rgba(244, 123, 32, 0.12);
}

/* The same pill the category header uses for its count. */
.tab__count {
    flex-shrink: 0;
    font-size: 11px;
    font-weight: 700;
    color: var(--text-badge);
    background: rgba(244, 123, 32, 0.12);
    border: 1px solid rgba(244, 123, 32, 0.22);
    border-radius: 99px;
    padding: 1px 8px;
}

.tab.active .tab__count {
    background: rgba(244, 123, 32, 0.22);
}

.ac-category[hidden] {
    display: none;
}

/* ── THE CATALOGUE'S CODING LIST ──
   A coding, then its variants one per line. Three things this is deliberately not:
   it is not two columns of cards (a variant is a name and a price, which is a row, not a card);
   it does not repeat the price in tokens (the customer buys in euros, and the token count told
   them nothing they could act on); and the coding's own name is bigger than its variants' rather
   than smaller, so the thing you scan for is the thing that stands out. */

.ac-categories {
    display: flex;
    flex-direction: column;
    /* There was no rule here at all, so every coding sat flush against the next one. */
    gap: 14px;
}

/* The catalogue's own header: the coding name leads, its variant count follows. */
.ac-category--catalogue .ac-category__title {
    font-size: 15px;
    font-weight: 700;
    letter-spacing: 0;
    text-transform: none;
}

/* One variant per line, hairline-separated, instead of a two-column grid of cards. */
.ac-variants--rows {
    grid-template-columns: 1fr;
}

.ac-row {
    display: flex;
    align-items: baseline;
    gap: 12px;
    padding: 9px 20px;
    background: var(--bg-card);
    transition: background-color 0.14s ease;
    /* Anchors the stretched link below. Harmless where a row has no link. */
    position: relative;
}

/* The whole line is the click target, not just the name.
   The link keeps its markup (one anchor, whose accessible name is still the coding) and grows a
   pseudo-element over the row instead. Hitting a 14px name to open a coding is the kind of aim a
   back office asks for all day; the row already lights up on hover, and now that means something.
   Anything interactive added to a row later needs `position: relative` to sit above this. */
.ac-row__name a::after {
    content: "";
    position: absolute;
    inset: 0;
}

.ac-row:hover { background: rgba(244, 123, 32, 0.03); }

[data-theme="light"] .ac-row:hover { background: rgba(244, 123, 32, 0.04); }

.ac-row__name {
    margin: 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-main);
    line-height: 1.4;
}

/* Takes the room that is left and gives it back: a long one is cut rather than wrapping the row
   onto a second line, which is what made the old list so tall. */
.ac-row__desc {
    margin: 0;
    flex: 1 1 auto;
    min-width: 0;
    font-size: 12px;
    color: var(--text-muted);
    line-height: 1.4;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Pushed to the right edge whether or not there is a description, and lined up with the prices
   above and below it: tabular figures so 9 € and 10 € do not shuffle the column. */
.ac-row__price {
    margin-left: auto;
    flex-shrink: 0;
    font-size: 14px;
    font-weight: 700;
    color: var(--orange);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Narrow screens: the description drops to its own line rather than being cut to nothing, and the
   price stays on the first one beside the name. */
@media (max-width: 600px) {
    .ac-row { flex-wrap: wrap; }

    .ac-row__desc {
        flex-basis: 100%;
        white-space: normal;
        overflow: visible;
    }
}

.ac-gallery {
    padding: 14px 20px 18px;
}

.ac-gallery__images {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 8px;
}

.ac-gallery__images img {
    max-width: 140px;
    height: auto;
    border-radius: 8px;
    border: 1px solid var(--border-subtle);
}

/* ── THE PUBLIC PAGES ──────────────────────────────────────────────────────────
   Not from the mockup: it covers the signed-in app, and the guest site has to
   exist too. Composed from the same pieces: the topbar, the cards, the buttons,
   with the sidebar's job (the nav) moved into the bar, since there is no sidebar
   when nobody is signed in.                                                     */

.guest-menu {
    display: flex;
    flex-direction: row;
    gap: 4px;
    margin: 0 auto 0 24px;
}

.guest-menu .menu-item {
    padding: 8px 14px;
    border-radius: 9px;
    font-size: 14px;
}

/* The public pages have no sidebar, so nothing offsets their content. */
.guest-main {
    max-width: 1180px;
    margin: 0 auto;
    padding: 22px;
}

.topbar-end .btn-secondary,
.topbar-end .btn-action {
    padding: 7px 14px;
    font-size: 13px;
    white-space: nowrap;
}

@media (max-width: 720px) {
    .guest-menu {
        margin-left: 12px;
    }
    .guest-menu .menu-item span {
        font-size: 13px;
    }
    /* On a narrow screen the two buttons are what get squeezed; the shorter one goes. */
    .topbar-end .btn-secondary {
        display: none;
    }
}


/* ── THE CONFIRMATION ──
   A native <dialog>: the browser gives the backdrop, Escape, the focus trap and the inertness of
   everything behind it, none of which is worth hand-building. Only the looks are ours. */

.confirm-dialog {
    max-width: 460px;
    padding: 0;
    border: 1px solid var(--border-orange);
    border-radius: var(--radius-card);
    background: var(--bg-card);
    color: var(--text-main);
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.55);
}

.confirm-dialog::backdrop {
    background: rgba(0, 0, 0, 0.55);
    backdrop-filter: blur(2px);
}

.confirm-dialog__body {
    padding: 26px 28px 22px;
}

.confirm-dialog__title {
    margin: 0 0 10px;
    font-size: 17px;
    font-weight: 700;
}

.confirm-dialog__message {
    margin: 0 0 22px;
    font-size: 14px;
    line-height: 1.6;
    color: var(--text-muted);
}

/* The safe one first in the source, so it is what the keyboard reaches first. */
.confirm-dialog__actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
}


/* ── ZENZEFI CERTIFICATES ──
   One card per container, showing what the certificate says about itself rather than its file size.
   The page could only show a name and a size until the library became a dependency. */

.zz-certs {
    display: flex;
    flex-direction: column;
    gap: 14px;
    margin-top: 16px;
}

.zz-cert {
    padding: 16px 18px;
    border: 1px solid var(--border-light);
    border-radius: var(--radius-small);
    background: var(--field-bg);
}

/* Expired, or unreadable: still listed; it is in the directory either way, and a row nobody can see
   is a file nobody can delete. */
.zz-cert--expired {
    border-color: rgba(248, 113, 113, 0.35);
}

.zz-cert__top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}

.zz-cert__name {
    margin: 0;
    font-size: 15px;
    font-weight: 600;
    color: var(--text-main);
}

.zz-cert__details {
    /* Tighter than a profile page: this is a reference table, not a form. */
    gap: 6px 20px;
    font-size: 13px;
}

.zz-cert__footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--border-light);
}

.zz-cert__file {
    font-size: 12px;
    color: var(--text-muted);
    overflow-wrap: anywhere;
}

/* --- Firmware admin -------------------------------------------------------------------------- */

.fw-group {
    margin-top: 16px;
}

.fw-group__ecu {
    margin: 0 0 8px;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-main);
}

.fw-rows {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.fw-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    padding: 10px 14px;
    border: 1px solid var(--border-light);
    border-radius: var(--radius-small);
    background: var(--field-bg);
}

.fw-row__id {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-main);
}

.fw-row__meta {
    font-size: 12px;
    color: var(--text-muted);
}

/* The eligible-list editor: two lists, drag between them and reorder within the first. */
/* ── ONE BACK-TIME SEQUENCE ──
   A row per firmware the gateway may already be running: which firmwares to flash from there, in
   order. The flat list's two-column drag editor is gone: a row holds only its own sequence, so a
   select adds a step and the x removes one, and two columns per row would have been twice as many
   lists as there are firmwares on one page. */
.fw-sequence {
    margin-top: 14px;
    padding-top: 14px;
    border-top: 1px solid var(--border-light);
}

/* ── THE EZS223 HARDWARE REVISIONS ──
   Three places a revision can be, and a chip is in exactly one of them. The two lists read as boxes so
   that a drop has somewhere to land; the pool underneath is lighter, the way the sequence pool one card
   up is, because a chip there is an offer rather than a setting. */
.fw-revisions__label {
    margin: 14px 0 6px;
    color: var(--text-muted);
    font-size: 12px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.fw-revisions__list {
    display: flex;
    flex-flow: row wrap;
    align-items: center;
    gap: 8px;
    list-style: none;
    min-height: 42px;
    padding: 8px 10px;
    border: 1px dashed var(--border-light);
    border-radius: 8px;
}

.fw-revisions__pool {
    display: flex;
    flex-flow: row wrap;
    align-items: center;
    gap: 8px;
    min-height: 34px;
}

.fw-revision {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    border: 1px solid var(--border-orange);
    border-radius: 999px;
    background: var(--field-bg);
    color: var(--accent-warm);
    font-size: 12px;
    white-space: nowrap;
    cursor: grab;
    user-select: none;
}

.fw-revision:active { cursor: grabbing; }

/* An offer rather than a setting, so it is drawn as one: no fill and a neutral outline. */
.fw-revision--pool {
    border-color: var(--border-light);
    background: transparent;
    color: var(--text-muted);
}

.fw-revision__remove {
    padding: 0 2px;
    border: none;
    background: none;
    color: inherit;
    font: inherit;
    line-height: 1;
    cursor: pointer;
}

.fw-revision__remove:hover,
.fw-revision__remove:focus-visible { color: var(--error); }

.fw-revisions__empty {
    color: var(--text-subtle);
    font-size: 12px;
}

.fw-sequence__from {
    margin: 0 0 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-muted);
}

/* ── THE POOL ──
   The firmwares not yet a step in this sequence, one clickable chip each, under the list it feeds.
   Deliberately lighter than a step: a step is solid and draggable, a chip is only an offer. It
   replaced a <select>, which cost two clicks per step and went on offering firmwares already in the
   sequence. */
.fw-pool {
    margin-top: 10px;
    display: flex;
    flex-flow: row wrap;
    align-items: center;
    gap: 6px;
}

.fw-pool__chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 5px 9px;
    border: 1px dashed var(--border-light);
    border-radius: var(--radius-small);
    background: none;
    font-family: inherit;
    font-size: 12px;
    white-space: nowrap;
    color: var(--text-muted);
    cursor: pointer;
}

.fw-pool__chip:hover,
.fw-pool__chip:focus-visible {
    border-style: solid;
    border-color: var(--accent-warm);
    color: var(--text-main);
}

.fw-pool__plus {
    font-size: 14px;
    line-height: 1;
    color: var(--accent-warm);
}

/* THE LIST ITSELF: a wrapping row, not a column. A firmware id is narrow and a sequence is short, so
   a step per line spent the width of the card on nothing. Steps sit side by side and wrap onto a
   second line when the row is full, which is why pages/firmware.js reads the drop position in two
   dimensions rather than one.

   IT ABSORBED .fw-droplist, which by then had exactly one user. That class was the shared half of the
   deleted two-column editor, and leaving it would have put `flex-direction` in two rules of equal
   specificity, where only the source order decided which won.

   THE NUMBERS are here rather than in the script on purpose: reordering renumbers because the DOM
   order changed, so there is nothing to call and nothing to forget to call. */
.fw-sequence__list {
    counter-reset: fw-step;
    list-style: none;
    margin: 0;
    padding: 8px;
    min-height: 46px;
    display: flex;
    flex-flow: row wrap;
    align-items: center;
    gap: 6px;
    border: 1px dashed var(--border-light);
    border-radius: var(--radius-small);
    background: var(--field-bg);
}

/* No animation on purpose: this has to stay light on software-rendered Windows PCs. */
.fw-sequence__list.fw-drop-target {
    border-color: var(--accent-warm);
    border-style: solid;
}

.fw-sequence__list .fw-item {
    counter-increment: fw-step;
}

.fw-item__rank {
    min-width: 1.4rem;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    color: var(--text-muted);
}

.fw-item__rank::before {
    content: counter(fw-step) ".";
}

/* The x sits at the end of its own step. `margin-left: auto` pushed it to the far right back when a
   step was a full-width row; a step is now content-sized, so there is no free space to push it across
   and the rule would have done nothing. */
.fw-item__remove {
    border: 0;
    background: none;
    padding: 0 2px;
    font-size: 16px;
    line-height: 1;
    color: var(--text-muted);
    cursor: pointer;
}

.fw-item__remove:hover {
    color: var(--error);
}

.fw-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    white-space: nowrap;
    border: 1px solid var(--border-light);
    border-radius: var(--radius-small);
    background: var(--bg-card);
    font-size: 13px;
    color: var(--text-main);
    cursor: grab;
}

.fw-item__grip {
    color: var(--text-muted);
    font-size: 14px;
    line-height: 1;
}

.fw-item.fw-dragging {
    opacity: 0.5;
}

/* An id still on the list whose firmware is no longer on disk: it cannot be saved, so it is marked. */
.fw-item.fw-stale {
    border-color: rgba(248, 113, 113, 0.5);
}

.fw-empty {
    font-size: 12px;
    color: var(--text-muted);
    padding: 6px 4px;
}

/* ── THE MESSAGE BOX ──
   The component itself is padding, a radius and a transparent border: the colour is entirely on the
   state class, so a box drawn without one is an invisible rectangle. That is what FormMessageStateTest
   was written to refuse, and it reads the states out of the stylesheets rather than holding a list.

   Both states have to be here, and that is the part this file got wrong. The test walks all three
   stylesheets at once, so `success` counted as defined the moment auth.css defined it, but auth.css
   and this file are never loaded together (auth.css is the six sign-in pages; this is every other
   page), so a `form-message success` on an application page resolved to nothing at all. Three of them
   did: the one-time access code and the password-changed confirmation on the profile, and the panel
   the back-time service writes its PDU into, which is a result the customer has just paid ten tokens
   for. Each rendered as the invisible rectangle the test exists to prevent, on the far side of the gap
   the test cannot see.

   The green is .state-completed's, above, which already carries its light twin, not auth.css's, whose
   --success tokens are declared in auth.css and would resolve to nothing here. */
.form-message {
    padding: 11px 14px;
    border-radius: 6px;
    font-size: 13px;
    line-height: 1.45;
    border: 1px solid transparent;
    margin-bottom: 18px;
}

.form-message.error {
    background: var(--error-bg);
    border-color: var(--error);
    color: var(--error);
}

.form-message.success {
    background: rgba(110, 231, 183, 0.10);
    border-color: rgba(110, 231, 183, 0.28);
    color: #6ee7b7;
}

/* The operator's announcement to customers, from bonjour.webapp_announce, headed the way the old
   backend's band was headed. Green, as that one was, and not a semantic colour chosen here: an
   announcement is neither a failure nor a success, and painting "closed on Monday" in red would be a
   false signal, but green is the colour the people reading these learned it in.

   NO LIGHT-THEME TWIN HERE, deliberately: both colours are tokens, and --green-bg and --green-border
   each carry their own light value in base.css, which is where --error and --error-bg are written too.

   The heading is uppercased by this file and not by the bundle, so the three languages hold ordinary
   words and only one place decides to shout. It is --text-main rather than the green itself: #6ee7b7
   is what .form-message.success gives its text, and against the near-white card of the light theme it
   is barely there.

   The body is --text-main for a related reason. This is the one message box that holds sentences rather
   than a phrase, and three lines of coloured text on a tinted band is a headache to read.

   pre-line is ON THE BODY, and that is not tidiness. The administration's field is a textarea, so an
   operator writes an announcement in lines; without it they arrive as one paragraph, and the server
   cannot fix that for them because the value reaches the page through th:text, which is what keeps it
   text. On the band it would also preserve the newlines of the template's own indentation and print a
   blank line between the heading and the announcement. */
.form-message.announce {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    background: var(--green-bg);
    border-color: var(--green-border);
    color: var(--text-main);
}

/* min-width: 0 so a long unbroken word wraps instead of pushing the close button off the band. */
.announce-body {
    flex: 1;
    min-width: 0;
}

.announce-title {
    margin: 0 0 4px 0;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-main);
}

.announce-text {
    margin: 0;
    white-space: pre-line;
}

/* The close button. Removed from the flow of the text rather than floated, so a long announcement wraps
   against it instead of under it. announce.js stores the fingerprint; announce-bootstrap.js hides the
   band on the next page before it paints. */
.announce-dismiss {
    flex: 0 0 auto;
    margin-left: auto;
    padding: 0;
    border: 0;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
    line-height: 1;
}

.announce-dismiss:hover,
.announce-dismiss:focus-visible {
    color: var(--text-main);
}

.announce-dismiss i {
    width: 16px;
    height: 16px;
}

/* Closed on this browser, decided before the first paint. The class is on <html>, so the band is never
   drawn rather than drawn and taken away, which is the difference between a page that is right and a
   page that jumps. */
.announce-dismissed .form-message.announce {
    display: none;
}

/* A certificate in use about to expire: the one message on the administration that must not be walked
   past, so it is the semantic red as a solid band rather than the tinted box of a form message. The red
   is each theme's own --error, and white reads on both. */
.certificate-alert {
    display: flex;
    align-items: center;
    gap: 14px;
    margin: 22px 22px 0;
    padding: 12px 18px;
    border-radius: 8px;
    background: var(--error);
    color: #fff;
    font-size: 14px;
    font-weight: 600;
    line-height: 1.45;
}

.certificate-alert svg {
    flex: none;
    width: 22px;
    height: 22px;
}

.certificate-alert__text {
    flex: 1;
}

.certificate-alert__text p {
    margin: 0;
}

.certificate-alert__action {
    flex: none;
    padding: 6px 14px;
    border: 1px solid #fff;
    border-radius: 6px;
    color: #fff;
    text-decoration: none;
    white-space: nowrap;
}

.certificate-alert__action:hover,
.certificate-alert__action:focus-visible {
    background: #fff;
    color: var(--error);
}

/* A message that belongs to the page rather than to a form inside it.
   It is drawn by the topbar fragment, between the bar and `.content`, so it has to bring the side
   padding `.content` would have given it: 22px, the same number, so the two edges line up. The
   shorthand also drops .form-message's own bottom margin: `.content` opens with 28px of padding and
   two gaps stacked read as a mistake. No colour of its own, deliberately: it is .form-message.error,
   which already carries its light-theme twin.

   COMPOUND, `.form-message.page-message`, and not a bare `.page-message`. FormMessageStateTest reads
   every class on a message box and requires the stylesheets to define it as one of the component's,
   because a class on a box that no rule matches is how `form-message-success` came to render an
   invisible rectangle while looking styled in the markup. This is a placement modifier rather than a
   state, and it is written the way the component's states are written for exactly that reason. */
.form-message.page-message {
    margin: 22px 22px 0;
}

[data-theme="light"] .form-message.success {
    background: rgba(5, 150, 105, 0.08);
    border-color: rgba(5, 150, 105, 0.28);
    color: #059669;
}

/* --- The revenue block on the admin home -----------------------------------------------------------
   It was a six-column table, and every figure in it had the same weight: the number an accountant reads
   first (net, for the month that has closed) was the sixth cell of the second row, in the same size
   and colour as the payment count. A run of period blocks instead, each headlined by its own net. How
   many periods is WebHomeController.REVENUE_MONTHS; how many fit on a line is this file's, below.

   Money is right-aligned and set in tabular figures so the columns of digits line up; a proportional
   font puts 1 and 8 at different widths and two totals stop being comparable at a glance. */
/* FIVE PERIODS ON ONE LINE, and the numbers are why these are the numbers. The grid gets the window
   less the sidebar (210), the content padding (2 x 22) and the card's own (2 x 24): 302 pixels gone
   before a single figure is drawn. Five columns of 196 with four 14-pixel gaps need 1036, so the run
   stays on one line from a 1338-pixel window: every laptop this is opened on, and a wrap below that
   rather than an overflow, because a wrapped run reads as two runs and not as a broken page. */
.revenue-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(196px, 1fr));
    gap: 14px;
    margin-top: 6px;
}

.revenue-period {
    background: rgba(255, 255, 255, 0.02);
    border: 1px solid var(--border-light);
    border-radius: 14px;
    padding: 16px 14px 14px;
}

/* White at 2% is what lifts the panel off the card in the dark theme, and on a light background it is
   simply invisible: the panel keeps its border and loses its relief, with nothing saying so. Black at
   2%, which is the same gesture the other way up.
   The claim this comment used to make (that every other hard-coded colour in the file already carried
   its twin) was not true when it was written, and it was written three times, on three elements, by
   three people who each found the third. LightThemeTwinTest counts them instead. */
[data-theme="light"] .revenue-period {
    background: rgba(0, 0, 0, 0.02);
}

/* The first block is the month in progress; the orange edge says which without a word to translate. */
.revenue-period:first-child {
    border-color: var(--border-orange);
}

.revenue-month {
    margin: 0;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-subtle);
}

.revenue-net-label {
    margin: 12px 0 0;
    font-size: 11px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-muted);
}

/* 26 rather than 30: it is still by far the largest thing in the block, which is the whole of what the
   size is for, and it keeps a five-digit month ("12345.67 EUR") on one line inside a 196-pixel column.
   Past that it wraps at the space before EUR, which is a second line and not an overflow. */
.revenue-net {
    margin: 2px 0 0;
    font-size: 26px;
    line-height: 1.15;
    font-weight: 700;
    color: var(--orange);
    font-variant-numeric: tabular-nums;
}

.revenue-lines {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 6px 10px;
    margin: 16px 0 0;
    padding-top: 14px;
    border-top: 1px solid var(--border-light);
    font-size: 13px;
}

.revenue-lines dt {
    color: var(--text-muted);
}

.revenue-lines dd {
    margin: 0;
    text-align: right;
    color: var(--text-main);
    font-variant-numeric: tabular-nums;
}

/* A deduction is written with its sign and coloured like one: a column headed "Fees" holding 45.44
   reads as money arriving until you stop and think, which is exactly what a summary must not require. */
.revenue-lines dd.revenue-deduction {
    color: var(--error);
}

/* A count, not an amount: small and muted, so it is never read as a sixth money figure. */
.revenue-count {
    margin: 14px 0 0;
    font-size: 12px;
    color: var(--text-subtle);
}

/* ── A value a customer has to hand to another program ──────────────────────────────────────────────
   Two of those: the back-time PDU, which goes into a diagnostic tool, and the one-time code that signs
   the desktop client in. Both are long, both were transcribed by hand, and both now have a button.

   `overflow-wrap: anywhere` rather than `break-all`: these have no spaces at all, so there is no word
   for the browser to keep whole, and without it the PDU ran off the right of the page instead of
   wrapping, which is what a customer reported on 7 August 2026. */
.copyable {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin-top: 6px;
}

.copyable-value {
    flex: 1;
    min-width: 0;
    overflow-wrap: anywhere;
    word-break: break-word;
    font-family: "Courier New", Courier, monospace;
}

.copyable-button {
    flex: 0 0 auto;
    padding: 4px 10px;
    font-size: 12px;
    font-weight: 600;
    line-height: 1.4;
    color: inherit;
    background: transparent;
    border: 1px solid currentColor;
    border-radius: 6px;
    opacity: 0.75;
    cursor: pointer;
}

.copyable-button:hover,
.copyable-button:focus-visible {
    opacity: 1;
}

.copyable-button.is-copied {
    opacity: 1;
    font-weight: 700;
}

/* The same copying as an icon, for a value in a line or a heading where a labelled button would crowd it:
   admin/fragments/layout.html copyIcon. Both icons are drawn and one is shown: lucide replaces each <i>
   with an <svg> once, at load, so swapping them is the stylesheet's job, keyed on the is-copied that
   core/copy.js toggles. Colour is inherited, so it needs no light-theme twin. */
.copy-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 4px;
    margin-left: 6px;
    vertical-align: middle;
    color: inherit;
    background: transparent;
    border: 0;
    border-radius: 6px;
    opacity: 0.6;
    cursor: pointer;
}

.copy-icon:hover,
.copy-icon:focus-visible,
.copy-icon.is-copied {
    opacity: 1;
}

.copy-icon svg {
    width: 16px;
    height: 16px;
}

.copy-icon .copy-icon__done,
.copy-icon.is-copied .copy-icon__idle {
    display: none;
}

.copy-icon.is-copied .copy-icon__done {
    display: inline;
}


/* ── THE CODING EDITOR ──
   One page for a coding and everything under it, saved as it is used (see pages/admin-coding.js). What
   the layout has to do for that: keep the save state and the way around the page in view, lay the three
   languages side by side rather than in a column three screens tall, and show one variant at a time
   beside the list of them, so nothing has to be unfolded to be found.

   Only variables for the neutrals, no bare rgba: every one of them carries its light twin in base.css or
   at the top of this file. The green is the page's own variable because it is the one colour here the
   themes disagree on. */

.ce {
    --ce-ok: #6ee7b7;
}

[data-theme="light"] .ce {
    --ce-ok: #059669;
}

/* The anchors land under the two sticky bars rather than beneath them. The bar's height is measured by
   the script, since its title can wrap; the fallback is what it measures on a wide screen. */
html:has(.ce) {
    scroll-behavior: smooth;
}

/* THE BAR. Sticky under the topbar and drawn like it: the name, whether it is offered, whether
   everything is saved, and the way to every section. It takes the content's padding back so it spans the
   width, the way the topbar does. */
.ce-bar {
    position: sticky;
    top: 56px;
    z-index: 8;
    margin: -28px -22px 22px;
    padding: 14px 22px 0;
    background: var(--bg-topbar);
    border-bottom: 1px solid var(--border-light);
    backdrop-filter: blur(18px);
}

/* The save state sits on the left, beside the way back, and never on the right: the toast appears in
   the top right corner, exactly where a line on the right would be, and would cover it for as long as
   it says the same thing. */
.ce-bar__head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px 18px;
}

.ce-bar__back {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--accent-warm-dim);
    font-size: 12px;
    font-weight: 800;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    transition: color 0.15s ease;
}

.ce-bar__back:hover { color: var(--orange); }

.ce-bar__back svg { width: 14px; height: 14px; }

.ce-bar__title-row {
    display: flex;
    align-items: center;
    gap: 14px;
    min-width: 0;
    margin: 8px 0 10px;
}

.ce-bar__title {
    min-width: 0;
    margin: 0;
    overflow: hidden;
    font-family: Georgia, "Times New Roman", serif;
    font-size: clamp(22px, 2.3vw, 30px);
    font-weight: 600;
    letter-spacing: 0.06em;
    line-height: 1.15;
    text-overflow: ellipsis;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--accent-warm);
    text-shadow: 0 0 28px rgba(244, 123, 32, 0.2);
}

.ce-bar__state { flex: none; }

.ce-save {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
}

.ce-save__dot {
    flex: none;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--ce-ok);
}

.ce-save.is-editing .ce-save__dot { background: var(--text-subtle); }

.ce-save.is-saving .ce-save__dot {
    background: var(--orange);
    animation: ce-pulse 1s ease-in-out infinite;
}

.ce-save.is-failed { color: var(--error); }

.ce-save.is-failed .ce-save__dot { background: var(--error); }

.ce-save__retry {
    padding: 0;
    border: 0;
    background: none;
    color: var(--orange);
    font: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;
}

@keyframes ce-pulse {
    50% { opacity: 0.35; }
}

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

.ce-tabs {
    display: flex;
    gap: 2px;
    margin: 0 -10px;
    overflow-x: auto;
    scrollbar-width: none;
}

.ce-tab {
    display: inline-flex;
    flex: none;
    align-items: center;
    gap: 7px;
    padding: 9px 10px 10px;
    border-bottom: 2px solid transparent;
    color: var(--text-muted);
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
    transition: color 0.15s ease, border-color 0.15s ease;
}

.ce-tab svg { width: 15px; height: 15px; }

.ce-tab:hover { color: var(--text-main); }

.ce-tab[aria-current="true"] {
    border-bottom-color: var(--orange);
    color: var(--text-main);
}

.ce-tab--danger:hover,
.ce-tab--danger[aria-current="true"] {
    border-bottom-color: var(--error);
    color: var(--error);
}

.ce-tab__count,
.ce-count {
    padding: 0 7px;
    border: 1px solid rgba(244, 123, 32, 0.22);
    border-radius: 99px;
    background: rgba(244, 123, 32, 0.12);
    color: var(--text-badge);
    font-size: 11px;
    font-weight: 700;
    line-height: 1.6;
}

.ce-count { font-size: 12px; }

.ce-tab__alert {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--error);
}

/* THE SECTIONS. The history cards every admin page is made of, with a head that names them. */
.ce-section {
    scroll-margin-top: calc(56px + var(--ce-bar-height, 150px) + 14px);
}

.ce-section__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 10px 20px;
    margin-bottom: 18px;
}

.ce-section__title {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0;
    font-size: 18px;
    font-weight: 700;
    color: var(--text-main);
}

.ce-section__title svg { width: 18px; height: 18px; }

.ce-section__lead {
    max-width: 80ch;
    margin: 0;
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-muted);
}

.ce-section__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.ce-section__actions svg,
.ce-vpanel__foot svg { width: 15px; height: 15px; }

/* The Save buttons the forms carry for a browser without the script; with it, there is nothing to press. */
.ce-fallback { margin: 4px 0 22px; }

.ce--live .ce-fallback { display: none; }

/* THE THREE LANGUAGES, side by side, each box badged with its language. */
.ce-langs {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 22px;
}

.ce-legend {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--text-subtle);
}

.ce-langs__grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px;
}

.ce-lang { position: relative; }

.ce-lang .form-input { padding-left: 50px; }

.ce-langs--text .ce-lang .form-input {
    min-height: 118px;
    padding-top: 36px;
    padding-left: 14px;
}

/* Inside the box, on its left edge; for a description, above the first line. A click on it lands in the
   box beneath, since it is that box's label. */
.ce-lang__code {
    position: absolute;
    top: 12px;
    left: 11px;
    z-index: 1;
    padding: 2px 6px;
    border: 1px solid rgba(244, 123, 32, 0.22);
    border-radius: 5px;
    background: rgba(244, 123, 32, 0.12);
    color: var(--text-badge);
    font-size: 10px;
    font-weight: 800;
    letter-spacing: 0.08em;
    cursor: text;
}

.ce-langs--text .ce-lang__code { top: 10px; }

.ce .form-input[aria-invalid="true"] {
    border-color: var(--error);
    box-shadow: 0 0 0 3px var(--error-bg);
}

.ce-hint {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 0;
    font-size: 12px;
    line-height: 1.5;
    color: var(--text-muted);
}

.ce-hint svg {
    flex: none;
    width: 14px;
    height: 14px;
    margin-top: 1px;
    color: var(--accent-warm-dim);
}

/* THE PRICE AND THE TWO SWITCHES, on one row where there is room for it. */
.ce-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 18px 40px;
}

.ce-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.ce-field--price { width: 210px; }

.ce-field__hint {
    font-size: 12px;
    line-height: 1.4;
    color: var(--text-muted);
}

.ce-affix { position: relative; }

.ce-affix .form-input { padding-right: 72px; }

.ce-affix__unit {
    position: absolute;
    top: 50%;
    right: 14px;
    transform: translateY(-50%);
    font-size: 12px;
    color: var(--text-muted);
    pointer-events: none;
}

.ce-switches {
    display: flex;
    flex-wrap: wrap;
    gap: 16px 32px;
    padding-top: 24px;
}

/* A checkbox drawn as a switch: still the input underneath, transparent and on top, so a click, a tap,
   Space and the label all work as they do on any checkbox. The track is reached with `~` and not `+`,
   here and on the chips below: th:field writes a hidden `_enabled` input straight after the checkbox, so
   the track is never its adjacent sibling, and `+` drew every ticked box as unticked. */
.ce-switch {
    position: relative;
    display: inline-flex;
    align-items: flex-start;
    gap: 12px;
    cursor: pointer;
}

.ce-switch__input {
    position: absolute;
    top: 0;
    left: 0;
    width: 40px;
    height: 22px;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}

.ce-switch__track {
    position: relative;
    flex: none;
    width: 40px;
    height: 22px;
    border: 1px solid var(--border-light);
    border-radius: 999px;
    background: var(--field-bg);
    transition: background-color 0.15s ease, border-color 0.15s ease;
}

.ce-switch__track::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--text-muted);
    transition: transform 0.15s ease, background-color 0.15s ease;
}

.ce-switch__input:checked ~ .ce-switch__track {
    border-color: var(--orange);
    background: var(--orange);
}

.ce-switch__input:checked ~ .ce-switch__track::after {
    transform: translateX(18px);
    background: #fff;
}

.ce-switch__input:focus-visible ~ .ce-switch__track {
    outline: 2px solid var(--orange);
    outline-offset: 3px;
}

.ce-switch__text {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.ce-switch__label {
    font-size: 14px;
    font-weight: 600;
    line-height: 22px;
    color: var(--text-main);
}

.ce-switch__hint {
    font-size: 12px;
    color: var(--text-muted);
}

.ce-switch--compact { align-items: center; }

.ce-switch--compact .ce-switch__label { font-size: 13px; }

/* THE CHIPS: the control units and the categories, each a checkbox that looks like what it is. */
.ce-picker + .ce-picker { margin-top: 24px; }

.ce-picker__head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px 14px;
    margin-bottom: 12px;
}

.ce-picker__count {
    font-size: 12px;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
}

.ce-picker__checked {
    font-weight: 700;
    color: var(--text-main);
}

.ce-filter {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 34px;
    margin-left: auto;
    padding: 0 12px;
    border: 1px solid var(--border-light);
    border-radius: 999px;
    background: var(--field-bg);
}

.ce-filter:focus-within { border-color: var(--border-orange); }

.ce-filter svg {
    width: 14px;
    height: 14px;
    color: var(--text-muted);
}

.ce-filter__input {
    width: 170px;
    border: 0;
    outline: none;
    background: transparent;
    color: var(--text-main);
    font: inherit;
    font-size: 13px;
}

.ce-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.ce-chip {
    position: relative;
    display: inline-flex;
}

.ce-chip__input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}

.ce-chip__text {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 7px 14px;
    border: 1px solid var(--border-light);
    border-radius: 999px;
    background: var(--field-bg);
    color: var(--text-muted);
    font-size: 13px;
    font-weight: 600;
    transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.ce-chip__input:hover ~ .ce-chip__text {
    border-color: var(--border-orange);
    color: var(--text-main);
}

.ce-chip__input:checked ~ .ce-chip__text {
    border-color: var(--orange);
    background: rgba(244, 123, 32, 0.14);
    color: var(--text-main);
}

/* The tick base.css draws in a checkbox, drawn here in front of the name. */
.ce-chip__input:checked ~ .ce-chip__text::before {
    content: "";
    width: 10px;
    height: 10px;
    background: var(--orange);
    clip-path: polygon(14% 44%, 0 65%, 40% 100%, 100% 16%, 82% 0, 37% 66%);
}

.ce-chip__input:focus-visible ~ .ce-chip__text {
    outline: 2px solid var(--orange);
    outline-offset: 2px;
}

.ce-warning {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 12px;
    padding: 9px 12px;
    border: 1px solid var(--error);
    border-radius: 8px;
    background: var(--error-bg);
    color: var(--error);
    font-size: 13px;
}

.ce-warning svg {
    flex: none;
    width: 15px;
    height: 15px;
}

/* THE VARIANTS: the list on the left, one editor on the right. */
.ce-empty {
    margin: 0 0 16px;
    font-size: 13px;
    color: var(--text-muted);
}

.ce-variants {
    display: grid;
    grid-template-columns: minmax(220px, 280px) minmax(0, 1fr);
    align-items: start;
    gap: 22px;
}

.ce-variants--empty { grid-template-columns: minmax(0, 1fr); }

.ce-vlist {
    position: sticky;
    top: calc(56px + var(--ce-bar-height, 150px) + 14px);
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.ce-vlist__item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    border: 1px solid transparent;
    border-radius: 10px;
    color: var(--text-main);
    transition: background-color 0.15s ease, border-color 0.15s ease;
}

.ce-vlist__item:hover { background: var(--field-bg); }

.ce-vlist__item[aria-current="true"] {
    border-color: var(--border-orange);
    background: var(--field-bg-focus);
    box-shadow: inset 3px 0 0 var(--orange);
}

.ce-vlist__text {
    display: flex;
    flex: 1;
    flex-direction: column;
    min-width: 0;
}

.ce-vlist__name {
    overflow: hidden;
    font-size: 13px;
    font-weight: 600;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ce-vlist__price {
    font-size: 12px;
    color: var(--text-muted);
}

.ce-dot {
    flex: none;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--text-subtle);
}

.ce-dot.is-on { background: var(--ce-ok); }

.ce-vpanel {
    display: flex;
    flex-direction: column;
    gap: 18px;
    padding: 20px;
    border: 1px solid var(--border-light);
    border-radius: 14px;
    background: var(--field-bg);
    scroll-margin-top: calc(56px + var(--ce-bar-height, 150px) + 14px);
}

.ce-vpanel + .ce-vpanel { margin-top: 18px; }

.ce--live .ce-vpanel + .ce-vpanel { margin-top: 0; }

.ce-vpanel--draft {
    border-style: dashed;
    border-color: var(--border-orange);
}

.ce-vform {
    display: flex;
    flex-direction: column;
    gap: 18px;
}

.ce-vform .ce-langs,
.ce-vpanel--draft .ce-langs { margin-bottom: 0; }

.ce-vpanel__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
}

.ce-vpanel__title {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    font-size: 16px;
    font-weight: 700;
    color: var(--text-main);
}

.ce-vpanel__title svg { width: 17px; height: 17px; }

.ce-vpanel__foot {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.ce-vpanel__delete {
    padding-top: 14px;
    border-top: 1px solid var(--border-subtle);
}

/* THE CODING FILE: what it is, what it requires of a car, and the place to drop its replacement. */
.ce-file {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 14px;
    border: 1px solid var(--border-light);
    border-radius: 12px;
    background: var(--bg-card);
}

.ce-file__current {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px 10px;
}

.ce-file__current > svg {
    width: 18px;
    height: 18px;
    color: var(--accent-warm);
}

.ce-file__name {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 13px;
    color: var(--text-main);
}

.ce-file__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    margin-left: auto;
}

.ce-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 0;
    border: 0;
    background: none;
    color: var(--accent-warm-link);
    font: inherit;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: color 0.15s ease;
}

.ce-link:hover { color: var(--orange); }

.ce-link:disabled {
    opacity: 0.6;
    cursor: progress;
}

.ce-link svg { width: 14px; height: 14px; }

.ce-link--danger,
.ce-link--danger:hover { color: var(--error); }

.ce-link--danger:hover { text-decoration: underline; }

.ce-verdict {
    padding: 1px 8px;
    border: 1px solid currentColor;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    white-space: nowrap;
}

.ce-verdict.is-ok { color: var(--ce-ok); }

.ce-verdict.is-failed { color: var(--error); }

.ce-verdict.is-running { color: var(--text-muted); }

.ce-ecus {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
}

.ce-ecus__label {
    margin-right: 4px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--text-subtle);
}

.ce-ecu {
    padding: 2px 9px;
    border: 1px solid var(--border-subtle);
    border-radius: 6px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 12px;
    color: var(--text-muted);
}

.ce-ecu--required {
    border-color: var(--border-orange);
    background: rgba(244, 123, 32, 0.10);
    color: var(--text-main);
}

.ce-ecu--one-of {
    border-style: dashed;
    border-color: var(--border-light);
    color: var(--text-main);
}

.ce-ecus__none {
    font-size: 12px;
    color: var(--text-muted);
}

/* The place a file is dropped. The file input covers it, transparent: a click opens the chooser and a
   file dropped on it lands in the input, both natively. Without the script the input is shown as it is,
   so the file chosen is visible before Save. */
.ce-drop {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 16px;
    border: 1.5px dashed var(--border-light);
    border-radius: 12px;
    color: var(--text-muted);
    font-size: 13px;
    line-height: 1.45;
    text-align: center;
    transition: border-color 0.15s ease, background-color 0.15s ease, color 0.15s ease;
}

.ce-drop > svg {
    flex: none;
    width: 18px;
    height: 18px;
}

.ce--live .ce-drop__input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    padding: 0;
    opacity: 0;
    cursor: pointer;
}

.ce--live .ce-drop:hover,
.ce-drop.is-over {
    border-color: var(--orange);
    background: rgba(244, 123, 32, 0.06);
    color: var(--text-main);
}

.ce--live .ce-drop:focus-within {
    outline: 2px solid var(--orange);
    outline-offset: 2px;
}

.ce-drop.is-chosen {
    border-style: solid;
    border-color: var(--border-orange);
    color: var(--text-main);
}

.ce-drop[aria-invalid="true"],
.ce-drop.is-refused {
    border-color: var(--error);
    color: var(--error);
}

.ce-drop.is-refused { border-style: solid; }

.ce-drop.is-busy {
    border-style: solid;
    border-color: var(--orange);
    color: var(--text-main);
    cursor: progress;
}

.ce-drop.is-busy > svg { display: none; }

.ce-drop.is-busy::before {
    content: "";
    flex: none;
    width: 16px;
    height: 16px;
    border: 2px solid var(--border-orange);
    border-top-color: var(--orange);
    border-radius: 50%;
    animation: ce-spin 0.8s linear infinite;
}

.ce-output.is-failed { border-color: var(--error); }

/* THE GALLERY: the pictures as a customer will see them, and a tile to drop more on. */
.ce-gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 14px;
}

.ce-upload { display: contents; }

.ce-thumb {
    position: relative;
    margin: 0;
    aspect-ratio: 4 / 3;
    overflow: hidden;
    border: 1px solid var(--border-light);
    border-radius: 12px;
    background: var(--field-bg);
}

.ce-thumb img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.ce-thumb.is-uploading img { visibility: hidden; }

.ce-thumb.is-uploading::after {
    content: "";
    position: absolute;
    top: calc(50% - 12px);
    left: calc(50% - 12px);
    width: 24px;
    height: 24px;
    border: 2px solid var(--border-orange);
    border-top-color: var(--orange);
    border-radius: 50%;
    animation: ce-spin 0.8s linear infinite;
}

.ce-thumb.is-uploading .ce-thumb__delete { display: none; }

.ce-thumb__delete {
    position: absolute;
    top: 8px;
    right: 8px;
}

.ce-thumb__remove {
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 1px solid var(--border-light);
    border-radius: 50%;
    background: var(--bg-dropdown);
    color: var(--text-main);
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.ce-thumb:hover .ce-thumb__remove,
.ce-thumb__remove:focus-visible { opacity: 1; }

.ce-thumb__remove:hover {
    border-color: var(--error);
    color: var(--error);
}

.ce-thumb__remove svg { width: 15px; height: 15px; }

.ce-drop--tile {
    flex-direction: column;
    aspect-ratio: 4 / 3;
    padding: 14px;
}

.ce-drop--tile > svg {
    width: 26px;
    height: 26px;
}

/* THE DANGER ZONE: last, apart, and red, because it is the one thing on the page that is not undone by
   editing something back. */
.ce-danger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 16px 28px;
    margin-bottom: 22px;
    padding: 20px 24px;
    border: 1px solid var(--error);
    border-radius: 18px;
    background: var(--error-bg);
}

.ce-danger__text p {
    max-width: 72ch;
    margin: 6px 0 0;
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-muted);
}

.ce-danger__button {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 9px 20px;
    border: 1px solid var(--error);
    border-radius: 999px;
    background: transparent;
    color: var(--error);
    font: inherit;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.ce-danger__button:hover {
    background: var(--error);
    color: #fff;
}

.ce-danger__button svg { width: 15px; height: 15px; }


/* ── RESPONSIVE, AND WHY IT IS THE LAST THING IN THIS FILE ──
   A media query adds no specificity. `@media (max-width: 560px) { .dt-controls { align-items:
   flex-start } }` and a plain `.dt-controls { align-items: center }` are both one class, so the one
   written *later* wins: at every width, media query or not.

   This section used to sit in the middle of the file, above the components it overrides, and **nine of
   its declarations were dead**: the phone never got a column of left-aligned table controls, a
   full-width search box or the tighter cell padding, the ZenZefi card never got either of its narrower
   paddings, and `prefers-reduced-motion` never reached `.tabulator-row` or `.dt-page-btn`, so two
   elements went on animating for a reader who had asked them not to. Every one of them looked
   authoritative in the source and did nothing in the browser.

   Being last is what makes them true. Anything added below this line is unconditional and will beat
   it, so add components above, and add their responsive rules here. */

/* ── THE ICON RAIL, AND THE TWO PLACES IT LEAKED OUT OF ──
   Between 561px and 900px the sidebar is a 76px rail: labels do not fit, so only the icons show.
   Both bounds are load-bearing, and both were missing.

   The floor. Below 561px the sidebar stops being a rail and becomes a 210px off-canvas drawer
   (further down), but a media query is additive, so `max-width: 900px` is still true at 400px and the
   drawer inherited the whole rail treatment: 210px of centred icons, no labels, no section heading, no
   chevrons, and (through the sibling block at the submenu rules) submenus pinned shut with
   `!important` against the JavaScript that was correctly toggling them. That is the bug reported from a
   phone on 10 August 2026: "no text and the submenus do not open". The JavaScript was never at fault.

   The scope. `.menu-item` is not the sidebar's alone: the public pages have no sidebar and put the same
   class in their topbar, where an item is a bare <span> with no icon beside it. So an unscoped
   `.menu-item span { display: none }` emptied the guest navigation from 900px down: two links rendered
   as empty boxes on every tablet and phone. Found by reading this rule, not by looking at the page.

   Everything here therefore says `.sidebar` and carries a floor. */

@media (min-width: 561px) and (max-width: 900px) {
    :root { --sidebar-width: 76px; }

    .sidebar { padding-inline: 9px; }

    .sidebar .menu-item span:not(.menu-icon),
    .sidebar .menu-title { display: none; }

    .sidebar .brand {
        height: 70px;
        margin-bottom: 16px;
        justify-content: center;
        padding: 6px 4px 14px;
    }

    .sidebar .brand-logo {
        width: 44px;
        max-width: 44px;
        max-height: 44px;
    }

    .sidebar .menu-item {
        justify-content: center;
        padding: 0;
    }
}

/* Not part of the rail: it is the page's own padding and belongs to every narrow width. Kept in its
   own block so that the range above stays readable as "this is what a rail looks like". */
@media (max-width: 900px) {
    .zenzefi-card { padding: 28px 30px; }
}

@media (max-width: 560px) {
    :root { --sidebar-width: 0; }

    .sidebar {
        display: block;
        width: 210px;
        transform: translateX(-100%);
        transition: transform 0.25s ease;
    }

    .sidebar.sidebar-open { transform: translateX(0); }

    .main { margin-left: 0; }

    .topbar { padding-inline: 14px; }

    .content { padding: 22px 14px 40px; }

    .page-heading,
    .section-header,
    .footer {
        align-items: flex-start;
        flex-direction: column;
    }

    .cards-grid { grid-template-columns: 1fr; }

    .login-dropdown { width: min(190px, calc(100vw - 28px)); }

    .dt-controls,
    .dt-footer      { flex-direction: column; align-items: flex-start; }
    .dt-search-input { width: 100%; }

    .tabulator .tabulator-header .tabulator-col .tabulator-col-content,
    .tabulator-row .tabulator-cell { padding: 10px 12px; }

    .zenzefi-card { padding: 22px 18px; }
}

@media (prefers-reduced-motion: reduce) {
    .menu-item, .car-card,
    .login-dropdown,
    .tabulator-row, .dt-page-btn { transition: none; }
}


/* The coding editor, narrowing. Below 1100px the variants stack, their list turned into a row that
   scrolls sideways; below 900px the three languages do; on a phone the bar gives back the phone's own
   padding and the price takes the width. A touch screen has no hover, so the cross on an image is
   always shown there. */
@media (max-width: 1100px) {
    .ce-variants { grid-template-columns: minmax(0, 1fr); }

    .ce-vlist {
        position: static;
        flex-direction: row;
        padding-bottom: 4px;
        overflow-x: auto;
    }

    .ce-vlist__item {
        flex: none;
        max-width: 260px;
    }
}

@media (max-width: 900px) {
    .ce-langs__grid { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 560px) {
    .ce-bar {
        margin: -22px -14px 18px;
        padding: 12px 14px 0;
    }

    .ce-field--price { width: 100%; }

    .ce-switches { padding-top: 0; }

    .ce-filter {
        width: 100%;
        margin-left: 0;
    }

    .ce-filter__input { width: 100%; }

    .ce-gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (hover: none) {
    .ce-thumb__remove { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    html:has(.ce) { scroll-behavior: auto; }

    .ce-bar__back, .ce-tab, .ce-switch__track, .ce-switch__track::after, .ce-chip__text,
    .ce-vlist__item, .ce-link, .ce-drop, .ce-thumb__remove, .ce-danger__button { transition: none; }

    .ce-save.is-saving .ce-save__dot,
    .ce-drop.is-busy::before,
    .ce-thumb.is-uploading::after { animation: none; }
}


/* ── THE SCAN SHORTLISTS ──
   By calculator, with the ignition switches dropped onto it. Sixteen hundred rows are rendered at once,
   so a row is one line and costs as little as a line can; the palette above never scrolls, which is the
   whole reason the drag goes this way round.

   Only variables here, no bare rgba: every neutral carries its light twin in base.css. */

/* It follows the page. Arming the palette and then picking calculators is one gesture that spans the
   whole list, so the half that gets armed cannot be the half that scrolls away. Under the 56px topbar,
   and below the toast, which is the only thing that may cover it. */
.ezs-palette {
    position: sticky;
    top: 56px;
    z-index: 5;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 14px;
    padding: 12px 14px;
    border: 1px solid var(--border-light);
    border-radius: 8px;
    background: var(--bg-dropdown);
}

.ezs-palette__armed {
    margin-left: auto;
    color: var(--text-muted);
    font-size: 12px;
}

.ezs-palette__label {
    margin-right: 6px;
    color: var(--text-muted);
    font-size: 12px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.ezs-chip {
    display: inline-flex;
    align-items: center;
    padding: 4px 11px;
    border: 1px solid var(--border-orange);
    border-radius: 999px;
    background: transparent;
    color: var(--accent-warm);
    font: inherit;
    font-size: 12px;
    line-height: 1.6;
    white-space: nowrap;
}

/* Pressed to arm, dragged for a single pair: both, and it says so before either. */
.ezs-chip--source {
    cursor: pointer;
    user-select: none;
}

.ezs-chip--source:active { cursor: grabbing; }

/* Armed reads as filled rather than as outlined, because "which of these am I about to apply" has to be
   answerable from across the page. */
.ezs-chip--armed {
    border-color: var(--orange);
    background: var(--orange);
    color: var(--menu-bg);
    font-weight: 600;
}

/* On a row, a chip is the control that takes it off, so it reads as pressable rather than as a label. */
.ezs-chip--on {
    cursor: pointer;
    transition: border-color 0.15s ease-out, color 0.15s ease-out;
}

.ezs-chip--on:hover,
.ezs-chip--on:focus-visible {
    border-color: var(--error);
    color: var(--error);
}

.ecu-toolbar {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}

.ecu-toolbar .ecu-filter { max-width: 280px; }
.ecu-count { margin-left: auto; }

/* Sized to the toolbar rather than to a page's main action: they sit beside the filter, and the number
   each names changes with every keystroke. Disabled is the state they ship in and the state they spend
   most of their life in, so it is drawn rather than left looking pressable. */
.ecu-apply-all,
.ecu-remove-all {
    flex: none;
    padding: 7px 16px;
    font-size: 12px;
    letter-spacing: 0.02em;
}

.ecu-apply-all:disabled,
.ecu-remove-all:disabled {
    border-color: var(--border-light);
    color: var(--text-subtle);
    cursor: default;
}

.ecu-apply-all:disabled:hover,
.ecu-remove-all:disabled:hover {
    background: transparent;
    border-color: var(--border-light);
    color: var(--text-subtle);
}

/* The only control on this page that destroys, so it is the only one that reddens under the pointer:
   the same signal a chip gives when hovering it means taking that switch off. */
.ecu-remove-all { border-color: var(--border-light); }

.ecu-remove-all:not(:disabled):hover,
.ecu-remove-all:not(:disabled):focus-visible {
    border-color: var(--error);
    background: transparent;
    color: var(--error);
}

.ecu-list {
    list-style: none;
    border: 1px solid var(--border-light);
    border-radius: 8px;
    overflow: hidden;
}

/* One line per calculator. The name has a fixed column so sixteen hundred chip runs start on one edge
   and the eye can go down it. */
.ecu-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 6px 14px;
    border-bottom: 1px solid var(--border-light);
}

.ecu-row:last-child { border-bottom: none; }
.ecu-row:hover { background: var(--field-bg); }

/* The calculator's name is the control that picks it, so the whole column is the target rather than a
   separate button beside it. Disabled until something is armed, because picking would then do nothing. */
.ecu-row__pick {
    flex: none;
    width: 24ch;
    padding: 2px 6px;
    border: 1px solid transparent;
    border-radius: 5px;
    background: transparent;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.ecu-row__pick:disabled { cursor: default; }

.ecu-row__pick:not(:disabled):hover,
.ecu-row__pick:not(:disabled):focus-visible {
    border-color: var(--border-orange);
    background: var(--field-bg-focus);
}

.ecu-row__name {
    color: var(--text-main);
    font-size: 13px;
}

.ecu-row__chips {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
}

.ecu-chip-form { display: contents; }

.ecu-row__none {
    color: var(--text-subtle);
    font-size: 12px;
}

.ecu-no-match {
    padding: 18px 14px;
    color: var(--text-muted);
    font-size: 13px;
}
