:root {
    --brand-gold: #ab8c52;
    --brand-gold-dark: #806430;
    /* Gold dark enough to carry white text at 5.55:1. Buttons only —
       decoration keeps --brand-gold. */
    --brand-gold-on-white-text: #806430;
    --brand-gold-light: #cbb895;
    --brand-gold-pale: #e8d4ae;
    --brand-charcoal: #212121;
    --brand-charcoal-soft: #2e2e2e;
    --brand-cream: #f5f2ec;
    --brand-cream-soft: #fcfbf9;
    --brand-text: #282c2e;
    --brand-muted: #636262;
    --brand-rush: #a8462f;

    /* One colour per solution, so each part of the business is recognisable
       before its name is read — and so a page inside a solution can carry its
       colour. Chosen to sit with the gold rather than shout over it: all four
       are muted and warm, and every one clears 4.5:1 on cream for text. */
    --sol-pipeline: #8a6a2f;
    --sol-pipeline-soft: #f2e8d4;
    --sol-operations: #2c6560;
    --sol-operations-soft: #dcebe9;
    --sol-sourcing: #9a4a33;
    --sol-sourcing-soft: #f6e3dd;
    --sol-inventory: #55642f;
    --sol-inventory-soft: #e7ecd9;

    --font-display: "Bricolage Grotesque", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    --font-body: "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    --font-secondary: "Karla", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

    --radius-lg: 16px;
    --radius-md: 12px;
    --radius-sm: 8px;
    --radius-pill: 999px;
    --shadow-sm: 0 1px 2px rgba(33, 33, 33, 0.06), 0 1px 3px rgba(33, 33, 33, 0.04);
    --shadow-md: 0 4px 12px rgba(33, 33, 33, 0.08), 0 2px 4px rgba(33, 33, 33, 0.05);
    --shadow-lg: 0 12px 28px rgba(33, 33, 33, 0.12), 0 4px 8px rgba(33, 33, 33, 0.06);
    --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
}

a, .btn, .card, .stage-card, .nav-link, .form-control, .form-select, .badge {
    transition: background-color var(--transition-fast), border-color var(--transition-fast),
        box-shadow var(--transition-fast), transform var(--transition-fast), color var(--transition-fast);
}

body {
    background-color: var(--brand-cream);
    color: var(--brand-text);
    font-family: var(--font-body);
    -webkit-font-smoothing: antialiased;
}

@media (min-width: 1400px) {
    .container {
        max-width: 1600px;
        padding-left: 2rem;
        padding-right: 2rem;
    }
}

@media (min-width: 1800px) {
    .container {
        max-width: 1880px;
        padding-left: 3rem;
        padding-right: 3rem;
    }
}

/* ------------------------------------------------------------------
   Page shell — every page wears the dashboard's frame.
   ------------------------------------------------------------------
   Pages used to sit in a 960px centred column while the dashboard
   spanned the full container, so the title jumped ~170px sideways on
   every navigation and tables floated on bare cream with no surface
   under them. `.page-shell` is the dashboard's width. Forms and prose
   still need a reading measure, but they get it from `.panel-measure`
   *inside* the frame rather than by re-centring the whole page.
   ------------------------------------------------------------------ */

.page-shell {
    width: 100%;
}

/* Kept so anything still marked narrow lines up on the same left edge
   as the dashboard instead of floating in the middle of the viewport. */
.content-narrow {
    max-width: 1040px;
    margin-right: auto;
}

/* The order pages take the whole page width. The detail view puts two columns
   of dense specification beside a status panel, and the edit form is rows of
   paired fields — at 1040px both left most of a desktop screen empty to their
   right, which is a lot of nothing to look past while reading a spec. */
.content-wide {
    max-width: none;
}

.panel-measure {
    max-width: 820px;
}

/* The content surface: the same glass the dashboard's chart card uses. */
.panel {
    border-radius: var(--radius-lg);
    margin-bottom: 1.5rem;
}

/* Only panels whose content runs edge to edge clip their overflow, and only
   so a hovered last table row can't paint a square corner over the panel's
   rounded one. It must NOT go on `.panel` itself: the order form's typeahead
   dropdowns are absolutely positioned inside their panel, and a clipping
   ancestor cuts them off at the panel's edge. */
.panel-flush {
    overflow: hidden;
}

.panel-body {
    padding: 1.5rem;
}

/* A toolbar (search, filters) sits at the top of the panel, divided from
   the content below rather than floating loose above the whole page. */
.panel-toolbar {
    padding: 1rem 1.25rem;
}

/* A toolbar built on Bootstrap's `.row` carries negative side margins, which
   push 4px past a panel that clips its overflow. Zero them and let each
   column's own half-gutter padding supply the inset instead. */
.panel-toolbar.row {
    margin-left: 0;
    margin-right: 0;
    padding-left: calc(1.25rem - var(--bs-gutter-x, 1.5rem) * 0.5);
    padding-right: calc(1.25rem - var(--bs-gutter-x, 1.5rem) * 0.5);
}

/* The divider belongs to whatever follows the toolbar, not to the toolbar
   itself: on phones the table is `d-none`, so a border on the toolbar would
   draw a stray line hanging just above the panel's own bottom edge. */
.panel-toolbar + .table-responsive,
.panel-toolbar + .panel-body,
.panel-toolbar + .panel-empty {
    border-top: 1px solid #ece6d8;
}

/* Table cell padding already supplies the inset, so the panel must not
   add a second one — and the final row's rule would otherwise draw a
   line just inside the panel's own bottom edge. */
.panel > .table-responsive .table,
.panel > .table {
    margin-bottom: 0;
}

.panel > .table-responsive .table > tbody > tr:last-child > td,
.panel > .table .table > tbody > tr:last-child > td {
    border-bottom: 0;
}

/* A status filter holds one short word; the search box should take the room. */
@media (min-width: 768px) {
    .panel-toolbar-select {
        flex: 0 0 220px;
    }
}

.panel-empty {
    padding: 3rem 1.5rem;
    text-align: center;
    color: var(--brand-muted);
}

/* A row count that belongs to the toolbar it labels, rather than a
   lone stat tile floating above a one-number page. */
.panel-count {
    display: inline-block;
    padding: 0.35rem 0.85rem;
    border-radius: var(--radius-pill);
    background-color: var(--brand-gold-pale);
    /* --brand-gold-dark on this pale gold is only 3.82:1; this is 5.05:1. */
    color: #6b5228;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 0.95rem;
    line-height: 1.4;
}

@media (max-width: 991.98px) {
    .panel {
        border-radius: var(--radius-md);
    }

    .panel-body {
        padding: 1.1rem;
    }

    .panel-toolbar {
        padding: 0.85rem 1rem;
    }
}

/* ------------------------------------------------------------------
   Split view: a list on the left, the order it points at on the right
   ------------------------------------------------------------------
   Used by the Scan Station (scanner left, scanned order right) and by
   Chats (conversations left, the order and its thread right). Both are
   places you work through a run of orders, so the order opens beside
   the list instead of costing a page load and a trip back. Below tablet
   width the two stack, list first.
   ------------------------------------------------------------------ */

.split-view {
    display: grid;
    gap: 1.5rem;
}

@media (min-width: 992px) {
    .split-view {
        grid-template-columns: minmax(320px, 400px) minmax(0, 1fr);
        align-items: start;
    }
}

.split-side {
    min-width: 0;
}

.split-detail {
    min-width: 0;
    border: 1px solid rgba(171, 140, 82, 0.28);
    border-radius: var(--radius-lg);
    background-color: rgba(255, 255, 255, 0.55);
    overflow: hidden;
}

.split-detail-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.75rem 1rem;
    border-bottom: 1px solid rgba(171, 140, 82, 0.22);
}

.split-detail-title {
    font-family: var(--font-secondary);
    font-weight: 700;
    font-size: 0.85rem;
    letter-spacing: 0.02em;
    color: var(--brand-muted);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* On a phone the header had the order number ellipsised down to nothing while
   "Open full page" wrapped onto two lines beside it. Give the title the row and
   let the button sit under it at its natural width. */
@media (max-width: 575.98px) {
    .split-detail-head {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.5rem;
    }

    .split-detail-title {
        white-space: normal;
        font-size: 0.9rem;
        color: var(--brand-text);
    }
}

.split-detail-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
    min-height: 320px;
    padding: 2rem 1rem;
    color: var(--brand-muted);
    text-align: center;
}

/* When the panel asked for an order and got a page that refuses to be
   framed. Says what happened, and offers the way round it. */
.split-detail-failed {
    max-width: 26rem;
    text-align: center;
}

.split-detail-empty svg {
    width: 40px;
    height: 40px;
    opacity: 0.5;
}

.split-detail-frame {
    display: block;
    width: 100%;
    /* Tall enough to show the order without a second scrollbar race against
       the page; the frame scrolls internally past that. */
    height: min(78vh, 900px);
    border: 0;
}

@media (max-width: 991.98px) {
    .split-detail-frame {
        height: 70vh;
    }

    /* Where a tap opens the order full page instead of filling the panel, the
       panel is only ever an empty box under the list. Chats does that; the
       Scan Station does not, because scanning has nowhere else to put the
       order. */
    .split-detail-wide-only {
        display: none;
    }
}

/* Inside the frame there is no sidebar to clear and no tab bar to sit above. */
body.is-embedded .main-wrapper {
    margin-left: 0;
    padding-top: 1rem;
    padding-bottom: 1rem;
}

/* The panel is already a narrow column; a page gutter inside it is a second
   one. Bootstrap was holding the order to 720px in an 874px frame, and the
   154px it kept back is what squeezed the status column beside the order down
   to 156px. Give the order the whole panel. */
body.is-embedded .container {
    max-width: none;
}

body.is-embedded .content-narrow,
body.is-embedded .panel-measure {
    max-width: none;
}

/* Stat tiles above a panel, sized like the dashboard's bento tiles. */
.stat-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 1.5rem;
    margin-bottom: 1.5rem;
}

.stat-row > .card {
    height: 100%;
    margin-bottom: 0;
}

.btn.page-header-spacer {
    display: none;
}

.page-header {
    margin-bottom: 2rem !important;
}

h1, .navbar-brand, .display-font {
    font-family: var(--font-display);
}

/* The wordmark keeps its own tighter setting. It is a logotype rather
   than a heading, and it is drawn at the same size on every page. */
.navbar-brand {
    letter-spacing: 0.01em;
}

h2, h3, h4, h5 {
    font-weight: 700;
}

/* Tracking, set once for the whole app: headlines at 0.03rem, body copy at
   0.04rem. In rem rather than em, so the gap between letters is the same
   width at every size — a headline and a caption are spaced the same
   absolute amount rather than the headline's growing with its type.

   Element selectors, so anything with a class of its own — the uppercase
   section labels, the chips, the day pills — keeps whatever it was given.
   Buttons, table cells and form controls are left alone: this is about
   reading, and a wider button is only a wider button. */
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6,
.display-font {
    letter-spacing: 0.03rem;
}

p {
    letter-spacing: 0.04rem;
}

a {
    color: var(--brand-gold-dark);
}

a:hover {
    color: var(--brand-charcoal);
}

/* Navbar */

.navbar.bg-dark {
    background-color: var(--brand-charcoal) !important;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
}

.navbar-brand {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-size: 1.15rem;
}

.navbar-brand img {
    height: 28px;
    width: auto;
}

.navbar-dark .navbar-nav .nav-link {
    color: rgba(255, 255, 255, 0.75);
    border-radius: var(--radius-sm);
    padding-left: 0.75rem !important;
    padding-right: 0.75rem !important;
}

.navbar-dark .navbar-nav .nav-link:hover {
    color: #fff;
    background-color: rgba(255, 255, 255, 0.08);
}

.navbar-dark .navbar-nav .nav-link.active {
    color: var(--brand-charcoal);
    background-color: var(--brand-gold-light);
    font-weight: 600;
}

/* Sidebar (desktop) */

.sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: 76px;
    background-color: var(--brand-charcoal);
    flex-direction: column;
    align-items: center;
    padding: 1.25rem 0;
    gap: 0.5rem;
    z-index: 1030;
}

.sidebar-logo {
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 0.75rem;
}

.sidebar-logo img {
    width: 26px;
    height: auto;
}

.sidebar-icon {
    width: 44px;
    height: 44px;
    border-radius: var(--radius-md);
    display: flex;
    align-items: center;
    justify-content: center;
    color: rgba(255, 255, 255, 0.7);
    text-decoration: none;
    border: none;
    background: transparent;
}

.sidebar-icon svg {
    width: 20px;
    height: 20px;
}

.sidebar-icon:hover {
    background-color: rgba(255, 255, 255, 0.08);
    color: #fff;
}

.sidebar-icon.active {
    background-color: var(--brand-gold-light);
    color: var(--brand-charcoal);
}

.sidebar-icon-accent {
    background-color: var(--brand-gold);
    color: #fff;
    margin-bottom: 0.5rem;
}

.sidebar-icon-accent:hover {
    background-color: var(--brand-gold-dark);
    color: #fff;
}

.sidebar-spacer {
    flex: 1;
}

.sidebar-avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background-color: var(--brand-gold-light);
    color: var(--brand-charcoal);
    font-weight: 700;
    font-size: 0.9rem;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Mobile top bar + bottom tab bar */

.mobile-topbar {
    padding: 0.6rem 0;
}

.mobile-avatar-btn {
    border: none;
    background: transparent;
    padding: 0;
    line-height: 0;
}

.mobile-avatar-btn .sidebar-avatar {
    width: 34px;
    height: 34px;
    font-size: 0.8rem;
}

.mobile-tabbar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1030;
    display: flex;
    align-items: stretch;
    background-color: var(--brand-charcoal);
    box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.15);
    padding-bottom: env(safe-area-inset-bottom, 0);
}

.mobile-tab {
    flex: 1;
    min-width: 0; /* six tabs have to share a 375px phone */
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.2rem;
    padding: 0.5rem 0.15rem 0.6rem;
    color: rgba(255, 255, 255, 0.65);
    text-decoration: none;
    font-family: var(--font-secondary);
    font-size: 0.62rem;
    font-weight: 600;
}

.mobile-tab > span:last-child {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Wraps the icon so the unread badge can sit on its corner. */
.mobile-tab-icon {
    position: relative;
    display: inline-flex;
    line-height: 0;
}

.mobile-tab-icon .nav-badge {
    top: -6px;
    right: -9px;
}

/* Six tabs on a 320px phone: shrink the labels rather than truncate them. */
@media (max-width: 359.98px) {
    .mobile-tab {
        font-size: 0.55rem;
        padding-left: 0.1rem;
        padding-right: 0.1rem;
    }
}

.mobile-tab svg {
    width: 20px;
    height: 20px;
}

.mobile-tab:hover {
    color: #fff;
}

.mobile-tab.active {
    color: var(--brand-gold-light);
}

.mobile-tab-accent {
    color: rgba(255, 255, 255, 0.85);
}

.mobile-tab-accent-icon {
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background-color: var(--brand-gold);
    color: #fff;
    margin-top: -1.4rem;
    box-shadow: var(--shadow-md);
}

.mobile-tab-accent-icon svg {
    width: 20px;
    height: 20px;
}

.mobile-tab-accent:hover .mobile-tab-accent-icon {
    background-color: var(--brand-gold-dark);
}

.mobile-tab-accent.active {
    color: var(--brand-gold-light);
}

@media (max-width: 991.98px) {
    /* The phone bar stays put, the way the wide one already did. It was the
       only bar in the app that scrolled away, which meant the crown, the
       account and the "viewing as" switch were somewhere up the page rather
       than somewhere on the screen.

       Sticky, not fixed: sticky keeps the bar in the flow, so it holds its
       own height open and the page below it starts where it always did.
       Fixed would have lifted it out and dropped the first inch of every
       page underneath it. */
    .mobile-topbar {
        position: sticky;
        top: 0;
        z-index: 1035;
        /* The gap under the bar has to scroll with the page, not travel
           with the bar: a transparent margin pinned to the top is a strip
           of nothing for the page to slide through. It moves to the page's
           own padding below. */
        margin-bottom: 0 !important;
    }

    .main-wrapper {
        padding-top: 1.5rem;
        padding-bottom: 6.5rem;
    }

    .container {
        padding-left: 1.25rem;
        padding-right: 1.25rem;
    }
}

[data-tooltip] {
    position: relative;
}

[data-tooltip]::after {
    content: attr(data-tooltip);
    position: absolute;
    left: calc(100% + 10px);
    top: 50%;
    transform: translateY(-50%);
    background-color: var(--brand-charcoal);
    color: #fff;
    padding: 0.35rem 0.7rem;
    border-radius: var(--radius-sm);
    font-size: 0.75rem;
    font-weight: 600;
    white-space: nowrap;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity var(--transition-fast);
    box-shadow: var(--shadow-md);
}

[data-tooltip]:hover::after {
    opacity: 1;
    visibility: visible;
}

@media (min-width: 992px) {
    .main-wrapper {
        margin-left: 76px;
        padding-top: 3rem;
        padding-bottom: 4rem;
    }
}

/* Buttons */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    border-radius: var(--radius-sm);
    font-weight: 600;
    padding: 0.55rem 1.1rem;
    box-shadow: var(--shadow-md);
}

.btn:hover {
    box-shadow: var(--shadow-lg);
    transform: translateY(-1px);
}

.btn-sm {
    padding: 0.35rem 0.8rem;
}

.btn-link,
.btn-close {
    box-shadow: none;
}

.btn-link:hover,
.btn-close:hover {
    box-shadow: none;
    transform: none;
}

.btn-primary {
    /* Disabled had no brand value, so Bootstrap's blue came through — the
       one state nobody had put on screen until a page shipped with a button
       that starts unavailable. Pale gold with the 5.05:1 text on it reads as
       "exists, not available" and stays in the palette. */
    --bs-btn-disabled-bg: var(--brand-gold-pale);
    --bs-btn-disabled-border-color: var(--brand-gold-pale);
    --bs-btn-disabled-color: #6b5228;
    --bs-btn-bg: var(--brand-gold-on-white-text);
    --bs-btn-border-color: var(--brand-gold-on-white-text);
    --bs-btn-hover-bg: #6f5626;
    --bs-btn-hover-border-color: #6f5626;
    --bs-btn-active-bg: #6f5626;
    --bs-btn-active-border-color: #6f5626;
    --bs-btn-color: #fff;
    --bs-btn-hover-color: #fff;
    --bs-btn-active-color: #fff;
}

.btn-outline-primary {
    --bs-btn-color: var(--brand-gold-dark);
    --bs-btn-border-color: var(--brand-gold);
    --bs-btn-hover-bg: var(--brand-gold);
    --bs-btn-hover-border-color: var(--brand-gold);
    --bs-btn-hover-color: #fff;
    --bs-btn-active-bg: var(--brand-gold-dark);
    --bs-btn-active-border-color: var(--brand-gold-dark);
}

.btn-outline-secondary {
    --bs-btn-color: var(--brand-charcoal-soft);
    --bs-btn-bg: var(--brand-cream-soft);
    --bs-btn-border-color: var(--brand-gold-light);
    --bs-btn-hover-color: var(--brand-charcoal);
    --bs-btn-hover-bg: var(--brand-gold-pale);
    --bs-btn-hover-border-color: var(--brand-gold);
    --bs-btn-active-color: var(--brand-charcoal);
    --bs-btn-active-bg: var(--brand-gold-pale);
    --bs-btn-active-border-color: var(--brand-gold);
}

.btn-success {
    --bs-btn-bg: #4d7a5f;
    --bs-btn-border-color: #4d7a5f;
    --bs-btn-hover-bg: #3d6249;
    --bs-btn-hover-border-color: #3d6249;
}

/* Cards, tables */

.card {
    border: none;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
}

.card-body {
    padding: 1.5rem;
}

.card-header {
    background-color: transparent;
    font-family: var(--font-secondary);
    font-weight: 700;
    border-bottom: 1px solid #ece6d8;
    padding: 1.1rem 1.5rem;
    color: var(--brand-text);
}

a.card-header:hover {
    background-color: var(--brand-cream-soft);
    color: var(--brand-text);
}

.table {
    --bs-table-bg: transparent;
}

.table td, .table th {
    padding: 1rem 1.1rem;
}

.table thead th {
    font-family: var(--font-secondary);
    font-weight: 700;
    font-size: 0.78rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--brand-muted);
    border-bottom-width: 1px;
    border-color: #ece6d8;
}

.table tbody tr {
    border-color: #f0ece2;
}

.table-hover tbody tr:hover {
    background-color: var(--brand-cream-soft);
}

/* Whole-row click target (see the row-link script in base.html) */
.table tbody tr.row-link {
    cursor: pointer;
}

.table tbody tr.row-link:focus-visible {
    outline: 2px solid var(--brand-gold);
    outline-offset: -2px;
}

/* Form controls */

.form-control, .form-select {
    border-radius: var(--radius-md);
    border-color: #ddd6c7;
    padding: 0.6rem 0.9rem;
}

.form-control:focus, .form-select:focus {
    border-color: var(--brand-gold);
    box-shadow: 0 0 0 0.2rem rgba(171, 140, 82, 0.18);
}

/* Alerts */

.alert {
    border: none;
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
}

/* Badges */

.badge {
    border-radius: var(--radius-pill);
    font-weight: 600;
    padding: 0.4em 0.9em;
}

.badge.bg-info.text-dark {
    background-color: var(--brand-gold-pale) !important;
    color: var(--brand-charcoal) !important;
}

/* Mini stats strip (list pages) */

.mini-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 1.25rem;
    margin-bottom: 2.25rem;
}

.mini-stats .card {
    border: 1px solid var(--brand-gold-light);
}

/* Tappable list cards (mobile list views) */
.list-card {
    color: var(--brand-text);
    border: 1px solid var(--brand-gold-light);
}

.list-card:hover {
    box-shadow: var(--shadow-md);
    border-color: var(--brand-gold);
}

.list-card .fw-semibold {
    color: var(--brand-charcoal);
}

/* The order currently shown in the panel beside the list. Same "you are here"
   marker the Chats rows use, so the two lists read the same way. */
.list-card.is-open {
    border-color: var(--brand-gold);
    box-shadow: inset 3px 0 0 var(--brand-gold);
}

/* The list scrolls inside the page rather than running it to a great length,
   so the order beside it stays put while you work down the orders. */
@media (min-width: 992px) {
    .order-cards {
        max-height: calc(100vh - 260px);
        overflow-y: auto;
        overscroll-behavior: contain;
        padding-right: 0.35rem;
    }
}

.mini-stats .card-body {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 0.6rem;
    padding: 1.1rem 1.3rem;
}

.mini-stats .stat-icon {
    width: 44px;
    height: 44px;
}

.mini-stats .stat-icon svg {
    width: 20px;
    height: 20px;
}

.mini-stats .stat-value {
    font-size: 1.6rem;
}

.mini-stats .stat-label {
    font-size: 0.72rem;
}

/* Dashboard bento grid */

.bento-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-template-areas: "hero due rush overdue";
    gap: 1.5rem;
    margin-bottom: 2rem !important;
}

/* The header used to say only "Welcome back". This is the line under it that
   names what needs attention, so the greeting carries something. */
.dash-focus {
    margin-top: 0.35rem;
    font-size: 0.95rem;
    color: var(--brand-muted);
}

.dash-focus a {
    color: inherit;
    text-underline-offset: 2px;
}

.dash-focus-alert,
.dash-focus a.dash-focus-alert {
    color: var(--brand-rush);
    font-weight: 600;
}

/* A tile reading 0 is still information — no overdue orders is good news — but
   it should not compete with the numbers that need acting on. */
.stat-tile .stat-value.is-zero {
    color: #9a9793;
}

/* The 14-day chart now sits below the board. */
.dash-chart {
    margin-bottom: 1.5rem;
}

.bento-grid .card {
    height: 100%;
    margin-bottom: 0;
}

.bento-hero { grid-area: hero; }
.bento-due { grid-area: due; }
.bento-rush { grid-area: rush; }
.bento-overdue { grid-area: overdue; }

.bento-hero,
.bento-due,
.bento-rush,
.bento-overdue {
    cursor: pointer;
    /* Touch devices otherwise paint a translucent box over the tile on tap. */
    -webkit-tap-highlight-color: transparent;
}

.bento-hero {
    background: linear-gradient(135deg, var(--brand-charcoal) 0%, #34302a 100%);
    color: #fff;
}

@media (hover: hover) and (pointer: fine) {
    .bento-hero:hover,
    .bento-due:hover,
    .bento-rush:hover,
    .bento-overdue:hover {
        box-shadow: var(--shadow-lg);
        transform: translateY(-2px);
    }
}

.bento-hero .card-body {
    height: 100%;
    justify-content: center;
}

.bento-hero .stat-value {
    color: #fff;
    font-size: 3rem;
}

.bento-hero .stat-label {
    color: rgba(255, 255, 255, 0.65);
}

.bento-hero .stat-icon {
    width: 60px;
    height: 60px;
    background-color: rgba(255, 255, 255, 0.12);
    color: var(--brand-gold-light);
}

.bento-hero .stat-icon svg {
    width: 26px;
    height: 26px;
}

.bento-hero .stat-progress {
    background-color: rgba(255, 255, 255, 0.15);
}

.bento-hero .stat-progress-fill {
    background-color: var(--brand-gold-light);
}

.bento-hero .stat-progress-caption {
    color: rgba(255, 255, 255, 0.55);
}

.bento-due .card-body,
.bento-rush .card-body,
.bento-overdue .card-body {
    justify-content: center;
    height: 100%;
}

@media (max-width: 991px) {
    .bento-grid {
        grid-template-columns: repeat(2, 1fr);
        grid-template-areas:
            "hero due"
            "rush overdue";
    }
}

/* Four full-width tiles stacked down a phone screen pushed the board to
   y=896 — past the fold on a 844px viewport, so the dashboard opened on
   statistics and you had to scroll to reach the work. Active Orders keeps the
   full width; the three supporting numbers share one row. */
@media (max-width: 575px) {
    .bento-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
        grid-template-areas:
            "hero hero hero"
            "due  rush overdue";
        gap: 0.75rem;
    }

    .bento-due .card-body,
    .bento-rush .card-body,
    .bento-overdue .card-body {
        padding: 0.85rem 0.7rem;
        /* These bodies are centred at desktop sizes. Side by side that pushes
           the one-line "Overdue" 14px below the two-line labels beside it. */
        justify-content: flex-start;
    }

    /* Icon above the number rather than beside it — there is no room for a
       row at a third of a phone's width. */
    .bento-due .card-body > .d-flex,
    .bento-rush .card-body > .d-flex,
    .bento-overdue .card-body > .d-flex {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.45rem !important;
    }

    .bento-due .stat-icon,
    .bento-rush .stat-icon,
    .bento-overdue .stat-icon {
        width: 32px;
        height: 32px;
        border-radius: var(--radius-sm);
    }

    .bento-due .stat-icon svg,
    .bento-rush .stat-icon svg,
    .bento-overdue .stat-icon svg {
        width: 16px;
        height: 16px;
    }

    .bento-due .stat-value,
    .bento-rush .stat-value,
    .bento-overdue .stat-value {
        font-size: 1.7rem;
    }

    .bento-due .stat-label,
    .bento-rush .stat-label,
    .bento-overdue .stat-label {
        font-size: 0.63rem;
        letter-spacing: 0.02em;
    }
}

/* On the narrowest phones a third of the width is ~70px of text, where the
   tracking alone pushed "Due This Week" onto a third line. */
@media (max-width: 399px) {
    .bento-due .stat-label,
    .bento-rush .stat-label,
    .bento-overdue .stat-label {
        font-size: 0.6rem;
        letter-spacing: 0;
    }
}

/* Dashboard / kanban */

.stat-tile {
    border: 1px solid var(--brand-gold-light);
}

.stat-tile .card-body {
    padding: 1.4rem 1.6rem;
    display: flex;
    flex-direction: column;
}

.stat-icon {
    flex: 0 0 auto;
    width: 52px;
    height: 52px;
    border-radius: var(--radius-md);
    background-color: var(--brand-gold-pale);
    color: var(--brand-gold-dark);
    display: flex;
    align-items: center;
    justify-content: center;
}

.stat-icon svg {
    width: 24px;
    height: 24px;
}

.stat-tile-alert .stat-icon {
    background-color: #f6dcd4;
    color: var(--brand-rush);
}

.stat-value {
    font-family: var(--font-display);
    font-size: 2.3rem;
    font-weight: 700;
    line-height: 1.1;
    color: var(--brand-charcoal);
}

.stat-label {
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--brand-muted);
    margin-top: 0.15rem;
}

.stat-tile-alert .stat-value {
    color: var(--brand-rush);
}

.stat-progress {
    height: 6px;
    border-radius: var(--radius-pill);
    background-color: #ece6d8;
    overflow: hidden;
}

.stat-progress-fill {
    height: 100%;
    border-radius: var(--radius-pill);
    background-color: var(--brand-gold);
}

.stat-progress-fill-alert {
    background-color: var(--brand-rush);
}

.stat-progress-caption {
    font-size: 0.72rem;
    color: var(--brand-muted);
    margin-top: 0.4rem;
}

/* Statistics chart */

.chart-wrap {
    display: flex;
    align-items: flex-end;
    gap: 0.5rem;
    padding-top: 0.5rem;
}

.chart-bar-col {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: 0;
}

.chart-bar-track {
    height: 160px;
    width: 100%;
    display: flex;
    align-items: flex-end;
    justify-content: center;
}

.chart-bar {
    width: 14px;
    background-color: var(--brand-gold-light);
    border-radius: var(--radius-pill);
    min-height: 6px;
}

.chart-bar-track:hover .chart-bar {
    background-color: var(--brand-gold);
}

.chart-bar-label {
    font-size: 0.65rem;
    color: var(--brand-muted);
    margin-top: 0.5rem;
    white-space: nowrap;
}

.due-pill {
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    padding: 0.15em 0.6em;
    border-radius: var(--radius-pill);
    white-space: nowrap;
}

.due-pill-overdue {
    background-color: #f6dcd4;
    color: var(--brand-rush);
}

.due-pill-soon {
    background-color: var(--brand-gold-pale);
    color: var(--brand-charcoal);
}

/* An empty lane is a drop target, not a message. It used to read "No orders",
   which meant a quiet board shouted the same two words five times. */
.stage-empty {
    height: 72px;
    border-radius: var(--radius-md);
    border: 1px dashed rgba(171, 140, 82, 0.30);
    background-color: rgba(255, 255, 255, 0.35);
}

/* The Ready column's door to the rest of its history. */
.stage-more {
    display: block;
    margin-top: 0.5rem;
    padding: 0.45rem 0.6rem;
    border-radius: var(--radius-md);
    text-align: center;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--brand-muted);
    background: color-mix(in srgb, var(--brand-muted) 10%, transparent);
    text-decoration: none;
}
.stage-more:hover { color: var(--brand-gold); text-decoration: none; }

/* ------------------------------------------------------------------
   Board lanes
   ------------------------------------------------------------------
   Six fixed 270px columns needed 1700px and the viewport had 1300, so
   two stages sat off-screen with nothing to say so. On a wide screen
   the lanes now divide the width instead — a narrower Quality Check
   you can see beats a roomy one you cannot. Below that the row still
   scrolls, which is the right behaviour on a phone.
   ------------------------------------------------------------------ */

.board-lanes {
    display: flex;
    gap: 0.75rem;
}

/* ------------------------------------------------------------------
   Stage picker (narrow screens)
   ------------------------------------------------------------------
   Nine lanes side by side is 2,176px of sideways swiping on a phone,
   and the last stages are a long way from the thumb. Below the width
   where the lanes all fit, the board shows one stage at a time and
   these chips choose it. They wrap instead of scrolling, so nothing
   is hidden off the edge — the whole pipeline is readable at a glance
   with its counts, which the lane strip never managed.
   ------------------------------------------------------------------ */



/* The facts on the blocked page. Big enough to read out over a workshop
   floor, because that is exactly how this address travels: somebody says
   it aloud to whoever keeps the list. */
.off-network-why {
    margin-top: 1.25rem;
    border-top: 1px solid var(--line);
    padding-top: 0.75rem;
}

.off-network-why > summary {
    cursor: pointer;
    font-size: 0.9rem;
    color: var(--brand-gold-on-white-text);
}

.off-network-why[open] > summary {
    margin-bottom: 0.6rem;
}

.off-network-facts {
    padding: 0.75rem 0.9rem;
    margin-bottom: 0.75rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background-color: var(--brand-cream);
}

/* The install panel. Docked at the foot of the screen on a phone, where a
   thumb is, and out of the way of the tab bar it sits above. */
/* The last icon on the rail, pushed to the foot: it leaves this solution
   rather than moving around inside it, and the gap says so. */
.sidebar-icon-foot {
    margin-top: auto;
}

.install-app {
    position: fixed;
    left: 0.75rem;
    right: 0.75rem;
    bottom: calc(4.75rem + env(safe-area-inset-bottom, 0px));
    z-index: 1040;
    padding: 0.8rem 0.9rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-md, 14px);
    background-color: var(--surface, #fff);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
}

@media (min-width: 992px) {
    .install-app {
        left: auto;
        right: 1.25rem;
        bottom: 1.25rem;
        max-width: 24rem;
    }
}

.install-app-body {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.install-app-icon {
    border-radius: 10px;
    flex-shrink: 0;
}

.install-app-words {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
}

.install-app-why {
    font-size: 0.82rem;
    color: var(--brand-muted);
}

.install-app-actions {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-left: auto;
}

.install-app-steps {
    margin: 0.75rem 0 0;
    padding-left: 1.1rem;
    font-size: 0.85rem;
    color: var(--brand-muted);
}

.install-app-steps li + li {
    margin-top: 0.3rem;
}

.network-asked-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.network-asked-item {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 0.9rem;
    padding: 0.6rem 0;
    border-top: 1px solid var(--line);
}

.network-asked-item > div:first-child {
    min-width: 11rem;
}

.network-asked-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-left: auto;
}

.off-network-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
}

.off-network-asked {
    font-size: 0.9rem;
    color: var(--brand-muted);
}

/* The answer, when the address itself gives one away. Set apart from the
   plain facts above it: this is a finding, not another row of data, and
   the person reading it on a phone in a workroom needs the difference to
   be visible before they have read a word. */
.off-network-verdict {
    padding: 0.8rem 0.9rem;
    margin-bottom: 0.75rem;
    border: 1px solid var(--line);
    border-left: 3px solid var(--brand-gold);
    border-radius: var(--radius-sm);
    background-color: var(--brand-cream);
    font-size: 0.95rem;
}

.off-network-verdict p:last-child {
    color: var(--brand-muted);
}

.off-network-row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
}

.off-network-row + .off-network-row {
    margin-top: 0.35rem;
}

.off-network-label {
    font-family: var(--font-secondary);
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--brand-muted);
    min-width: 9.5rem;
}

.off-network-ip {
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--brand-text);
    background: none;
    padding: 0;
    /* Read aloud digit by digit, so it must never wrap mid-number. */
    white-space: nowrap;
}

/* Mine / my whole shop. One control with two halves rather than a button
   that toggles, so the state is readable without pressing anything —
   which half is filled in is the answer to "what am I looking at". */
.lens-switch {
    display: inline-flex;
    margin: 0;
    border: 1px solid var(--brand-gold);
    border-radius: var(--radius-pill);
    overflow: hidden;
}

.lens-btn {
    border: 0;
    background: transparent;
    color: var(--brand-gold-on-white-text);
    font-family: var(--font-secondary);
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    padding: 0.35rem 0.85rem;
    white-space: nowrap;
    cursor: pointer;
}

.lens-btn.is-on {
    background-color: var(--brand-gold-dark);
    color: #fff;
    cursor: default;
}

.lens-btn:not(.is-on):hover {
    background-color: rgba(171, 140, 82, 0.14);
}

/* On the dashboard it sits on its own line above the filters. */
.board-filters ~ .lens-switch,
.glass-stage + .lens-switch {
    margin-bottom: 0.75rem;
}

/* Headings inside the orders list, when it is grouped. Sticky, because a
   long group scrolls past its own name and then every card below looks
   like it belongs to whatever heading is still on screen. */
.list-group-head {
    position: sticky;
    top: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.4rem 0.6rem;
    margin: 0.75rem 0 0.5rem;
    border-radius: var(--radius-sm);
    background-color: var(--brand-cream);
    border-bottom: 1px solid var(--brand-gold-light);
}

.order-cards > .list-group-head:first-child {
    margin-top: 0;
}

.list-group-name {
    font-family: var(--font-secondary);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--brand-gold-on-white-text);
}

.list-group-count {
    font-size: 0.72rem;
    font-weight: 700;
    color: var(--brand-muted);
}

[data-theme="dark"] .list-group-head {
    background-color: var(--surface);
}

/* --- the front-fabric repair list ----------------------------------
   A list built to be worked down: the order, who it is for, and the
   button that opens the form with the empty box in it. Not a table —
   the useful row here is three facts and an action, and a table would
   spend half the width on column headings that repeat.
   ------------------------------------------------------------------ */

/* --- the file library ------------------------------------------------
   Every file the shop holds, the other way in from the order it is on. */
.files-filters {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    align-items: center;
}

.files-search { flex: 1 1 16rem; min-width: 12rem; }
.files-filters .form-select { flex: 0 1 11rem; }

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

.file-row {
    display: flex;
    align-items: flex-start;
    gap: .9rem;
    padding: .7rem 1rem;
    border-bottom: 1px solid var(--line);
}

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

/* The picture itself where there is one. A column of identical grey
   icons says nothing a person did not already know. */
.file-mark {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    border-radius: .5rem;
    overflow: hidden;
    background: color-mix(in srgb, var(--brand-gold) 14%, transparent);
}

.file-mark img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.file-ext {
    font-family: var(--font-secondary);
    font-size: .65rem;
    font-weight: 700;
    letter-spacing: .04em;
    color: var(--brand-muted);
}

/* The machine file is the one the floor goes looking for, so it does not
   look like everything else. */
.file-mark-machine { background: rgba(46, 125, 50, .14); }
.file-mark-machine .file-ext { color: #2e7d32; }
.file-mark-chat { background: rgba(63, 81, 181, .12); }

.file-main { min-width: 0; flex: 1 1 auto; }

.file-name {
    font-weight: 600;
    overflow-wrap: anywhere;
}

.file-name a { color: var(--brand-text); text-decoration: none; }
.file-name a:hover { color: var(--brand-gold-dark); text-decoration: underline; }

.file-note {
    font-size: .85rem;
    color: var(--brand-muted);
    overflow-wrap: anywhere;
}

.file-facts {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    margin-top: .2rem;
    font-size: .82rem;
    color: var(--brand-muted);
}

.file-facts a { color: var(--brand-gold-dark); text-decoration: none; font-weight: 600; }
.file-facts a:hover { text-decoration: underline; }

.file-kind {
    padding: .1rem .45rem;
    border-radius: .35rem;
    background: color-mix(in srgb, var(--brand-gold) 18%, transparent);
    font-size: .7rem;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--brand-text);
}

.file-kind-machine { background: rgba(46, 125, 50, .18); color: #2e7d32; }
.file-kind-chat { background: rgba(63, 81, 181, .16); color: #3f51b5; }

[data-theme="dark"] .file-kind-machine { color: #8fd694; }
[data-theme="dark"] .file-kind-chat { color: #9fa8da; }

@media (max-width: 575.98px) {
    .files-filters .form-select { flex: 1 1 100%; }
    .file-mark { width: 40px; height: 40px; }
}

.repair-note {
    padding: 0.9rem 1.1rem;
    margin-bottom: 1rem;
    border: 1px solid var(--line);
    border-left: 4px solid var(--brand-gold);
    border-radius: var(--radius-lg, 1rem);
    background-color: rgba(171, 140, 82, 0.07);
    max-width: 60rem;
}

.repair-count {
    font-family: var(--font-secondary);
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--brand-muted);
}

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

.repair-item {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 1rem;
    padding: 0.8rem 1rem;
    border-bottom: 1px solid var(--line);
}

.repair-item:last-child {
    border-bottom: 0;
}

.repair-item-main {
    display: flex;
    flex-direction: column;
    min-width: 11rem;
}

.repair-number {
    font-weight: 700;
    color: var(--brand-text);
    text-decoration: none;
}

.repair-number:hover {
    color: var(--brand-gold-dark);
    text-decoration: underline;
}

.repair-customer {
    font-size: 0.85rem;
    color: var(--brand-muted);
}

.repair-item-facts {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.85rem;
    color: var(--brand-muted);
    flex: 1 1 12rem;
}

/* Pinned right on a wide row, and dropped to its own line when the
   facts have already wrapped — a button that shifts around mid-list is
   a button people miss. */
.repair-fix {
    margin-left: auto;
    flex: none;
}

/* The doubled-rows list carries a tick box and the offending lines, so
   its row is a column beside the box rather than one flat strip. */
.repair-item-wide {
    align-items: flex-start;
    flex-wrap: nowrap;
}

.repair-tick {
    padding-top: 0.15rem;
    flex: none;
}

.repair-item-body {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    min-width: 0;
}

/* Stacked, so the facts row's flex-basis — written for a sideways row —
   would otherwise become 12rem of empty height. */
.repair-item-body > * {
    flex: none;
}

.doubled-lines {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    margin-top: 0.15rem;
}

/* Struck through, because the point of the line is that it is the copy
   going away — not another fabric on the order. */
.doubled-line {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
    font-size: 0.85rem;
}

.doubled-tag {
    font-family: var(--font-secondary);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--brand-muted);
}

.doubled-what {
    color: var(--brand-text);
    text-decoration: line-through;
    text-decoration-color: rgba(178, 45, 38, 0.7);
}

[data-theme="dark"] .repair-note {
    background-color: rgba(171, 140, 82, 0.14);
}

@media (max-width: 575.98px) {
    .repair-fix {
        margin-left: 0;
        width: 100%;
    }
}

/* --- something has gone wrong --------------------------------------
   The one control on the site that is allowed to shout. Red, wide, and
   sitting apart from the buttons that move an order along, because the
   thing it reports is not a stage — the job has stopped, and the person
   pressing it is standing at a machine waiting for somebody.
   ------------------------------------------------------------------ */

.alarm-card {
    border-color: rgba(178, 45, 38, 0.35);
}

.alarm-btn {
    background-color: #b22d26;
    border: 1px solid #97241e;
    color: #fff;
    font-weight: 700;
    letter-spacing: 0.01em;
}

.alarm-btn:hover,
.alarm-btn:focus {
    background-color: #97241e;
    color: #fff;
}

/* The rail this sits in is about 230px wide, where two buttons side by
   side wrap "Send the alarm" onto three lines. Stacked, with the way out
   as a quiet link under it — cancelling is not the thing being offered. */
.alarm-cancel {
    display: block;
    width: 100%;
    margin-top: 0.5rem;
    padding: 0.15rem;
    border: 0;
    background: none;
    font-size: 0.85rem;
    color: var(--brand-muted);
    text-decoration: underline;
}

.alarm-cancel:hover {
    color: var(--brand-text);
}

/* On the Scan Station the alarm sits in the scanner column, under the
   result of the last scan. Boxed so it does not read as one more scan
   control — it is the one button on this page that does not move work
   forward. */
.scan-alarm {
    padding: 0.85rem;
    border: 1px solid rgba(178, 45, 38, 0.35);
    border-radius: var(--radius-lg, 1rem);
    background-color: rgba(178, 45, 38, 0.05);
}

[data-theme="dark"] .scan-alarm {
    background-color: rgba(178, 45, 38, 0.14);
}

.alarm-hint {
    margin-top: 0.5rem;
    font-size: 0.8rem;
    color: var(--brand-muted);
    text-align: center;
}

/* The banner on an order that is stopped. Read before the order itself,
   which is why it sits above the grid rather than inside it. */
.alarm-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0.85rem;
    padding: 1rem 1.15rem;
    margin-bottom: 1rem;
    border: 1px solid rgba(178, 45, 38, 0.4);
    border-left: 5px solid #b22d26;
    border-radius: var(--radius-lg, 1rem);
    background-color: rgba(178, 45, 38, 0.07);
}

.alarm-banner-mark {
    flex: none;
    width: 2rem;
    height: 2rem;
    color: #b22d26;
}

.alarm-banner-mark svg {
    width: 100%;
    height: 100%;
}

.alarm-banner-body {
    flex: 1 1 16rem;
    min-width: 0;
}

.alarm-banner-title {
    margin: 0 0 0.15rem;
    font-size: 1.05rem;
    font-weight: 700;
    color: #8d211b;
}

.alarm-banner-note {
    margin: 0 0 0.25rem;
    /* A note typed at a machine arrives without line breaks and can run on. */
    overflow-wrap: anywhere;
}

.alarm-banner-meta {
    margin: 0;
    font-size: 0.8rem;
    color: var(--brand-muted);
}

.alarm-banner-clear {
    display: flex;
    gap: 0.4rem;
    flex: 1 1 15rem;
    align-items: flex-start;
}

.alarm-banner-waiting {
    flex: 1 1 15rem;
    font-size: 0.85rem;
    color: #8d211b;
}

/* How long the machine stood still. Set apart from the two names above
   it because it is the line somebody scans for. */
.settled-issue-stood {
    margin-top: 0.15rem;
    font-size: 0.82rem;
    font-weight: 700;
    color: var(--brand-gold-on-white-text, var(--brand-muted));
}

.settled-issue {
    padding: 0.55rem 0 0.55rem 0.7rem;
    border-left: 3px solid rgba(178, 45, 38, 0.35);
    margin-bottom: 0.6rem;
}

.settled-issue-head {
    font-weight: 700;
    font-size: 0.85rem;
}

/* A print and a download in the same column as the stage changes. Quieter
   than a stage — these are things that happened TO the order rather than
   steps it took — and marked so the two are told apart without reading. */
.history-avatar-printed,
.history-avatar-tidied,
.history-avatar-handed,
.history-avatar-emailed,
.history-avatar-downloaded {
    background-color: color-mix(in srgb, var(--brand-muted, #847d6c) 16%, transparent);
    color: var(--brand-muted, #847d6c);
}
.history-avatar-printed svg,
.history-avatar-tidied svg,
.history-avatar-handed svg,
.history-avatar-emailed svg,
.history-avatar-downloaded svg {
    width: 17px;
    height: 17px;
}

/* On the board: a card that is stopped says so from across the room. */
.stage-card.has-problem {
    border-color: rgba(178, 45, 38, 0.55);
    box-shadow: inset 3px 0 0 #b22d26;
}

/* The stage, when the column is not the stage. Quiet — it is context for
   the card, not the point of it, and it sits beside the Stopped flag
   which has to stay the loudest thing on a card. */
.card-stage-flag {
    display: inline-block;
    margin-top: 0.35rem;
    margin-right: 0.3rem;
    padding: 0.1rem 0.45rem;
    border-radius: var(--radius-pill);
    background-color: var(--brand-gold-pale);
    color: #3a2f16;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.03em;
}

/* Wider than the filters beside it: its options are sentences, and a box
   that clips "Columns by who took it" is a box nobody trusts. */
.board-filter-display {
    flex: 0 0 12.5rem;
}

.board-filter-display select {
    border-color: var(--brand-gold);
    font-weight: 700;
}

.card-problem-flag {
    display: inline-block;
    margin-top: 0.35rem;
    padding: 0.1rem 0.45rem;
    border-radius: var(--radius-pill);
    background-color: #b22d26;
    color: #fff;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

[data-theme="dark"] .alarm-banner {
    background-color: rgba(178, 45, 38, 0.16);
}

[data-theme="dark"] .alarm-banner-title {
    color: #f0a49e;
}

/* --- narrowing the board ------------------------------------------
   Five questions in a row: which stage, which store, whose, which
   designer, how urgent. They wrap on a narrow screen rather than
   scrolling sideways, because a filter you cannot see is a filter you
   forget is on — and a board quietly showing a seventh of the work is
   worse than one showing all of it.
   ------------------------------------------------------------------ */

.board-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.5rem 0.75rem;
    padding: 0.85rem 1rem;
    margin-bottom: 1rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-lg, 1rem);
    background-color: rgba(255, 255, 255, 0.55);
}

.board-filter {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    min-width: 8.5rem;
    flex: 1 1 8.5rem;
}

.board-filter-label {
    font-family: var(--font-secondary);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--brand-muted);
}

.board-filter select {
    font-size: 0.85rem;
}

.board-filter-actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    /* Sits on the same line as the boxes, not under their labels. */
    padding-bottom: 0.05rem;
}

.board-filter-clear {
    font-size: 0.85rem;
    color: var(--brand-muted);
    text-decoration: underline;
}

.board-filter-clear:hover {
    color: var(--brand-text);
}

/* With script, the boxes submit themselves and the button is redundant. */
.board-filters.is-live #board-filter-apply {
    display: none;
}

[data-theme="dark"] .board-filters {
    background-color: var(--surface);
}

@media (max-width: 575.98px) {
    .board-filter {
        /* Two to a row rather than five squeezed onto one. */
        flex: 1 1 calc(50% - 0.75rem);
        min-width: 0;
    }
}

.stage-picker {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-bottom: 1rem;
}

.stage-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.4rem 0.7rem;
    border-radius: var(--radius-pill);
    border: 1px solid rgba(171, 140, 82, 0.32);
    background-color: rgba(255, 255, 255, 0.7);
    font-family: var(--font-secondary);
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--brand-muted);
    -webkit-tap-highlight-color: transparent;
}

.stage-chip.is-active {
    background-color: var(--brand-charcoal);
    border-color: var(--brand-charcoal);
    color: #fff;
}

.stage-chip-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.25rem;
    height: 1.25rem;
    padding: 0 0.3rem;
    border-radius: var(--radius-pill);
    background-color: var(--brand-gold-pale);
    color: #6b5228;
    font-size: 0.7rem;
}

.stage-chip.is-active .stage-chip-count {
    background-color: rgba(255, 255, 255, 0.22);
    color: #fff;
}

@media (max-width: 1199.98px) {
    /* One stage fills the width; the rest wait behind their chip. */
    .board-scroll {
        overflow-x: visible;
        padding-bottom: 0;
    }

    .board-lanes {
        display: block;
    }

    .stage-column {
        display: none;
        min-width: 0;
    }

    .stage-column.is-selected {
        display: block;
    }

    /* The active chip already names the stage and carries its count. */
    .stage-head {
        display: none !important;
    }

    /* Dragging between lanes is meaningless when one shows at a time; the
       per-card stage dropdown is how a phone moves work along. */
    .board-hint {
        display: none;
    }

    .stage-empty {
        height: 96px;
    }
}

/* Wide lanes that scroll, rather than nine squeezed to fit. Squeezing put
   every lane on screen but broke order numbers across lines and wrapped the
   style onto two. Scrolling costs visibility of the far stages — which is
   exactly the defect this board had before — so the picker stays on screen at
   every width, listing all nine with their counts and jumping to one on click.
   Nothing is off screen and unannounced any more; it is just off screen. */
@media (min-width: 1200px) {
    .board-lanes {
        display: flex;
        gap: 0.75rem;
    }

    .board-scroll {
        overflow-x: auto;
        padding-bottom: 1rem;
    }
}

.stage-column {
    flex: 0 0 240px;
    min-width: 240px;
}

/* The count badge is taller than the label, so a lane that has one used to
   push its cards 6px lower than the empty lanes beside it. Reserve the badge's
   height whether or not one is showing. */
.stage-head {
    min-height: 22px;
}

/* Every lane starts its cards on the same line even where a label wraps. */
@media (min-width: 1200px) {
    .stage-head {
        min-height: 22px;
        align-items: flex-start !important;
    }
}

/* A tint per lane keeps the column boundaries explicit, so a lane's
   right-aligned count cannot read as the next lane's number. */
@media (min-width: 1200px) {
    .stage-column {
        background-color: rgba(255, 255, 255, 0.42);
        border-radius: var(--radius-md);
        padding: 0.6rem 0.5rem;
    }

    .stage-column .stage-card {
        margin-bottom: 0.5rem;
    }

    .stage-column .stage-card .card-body {
        padding: 0.65rem !important;
    }
}

.stage-column h2 {
    font-family: var(--font-secondary);
    font-weight: 700;
    font-size: 0.8rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--brand-muted);
}

.stage-card {
    margin-bottom: 0.85rem;
    border-radius: var(--radius-md);
    cursor: grab;
}

/* A card nobody may move should not offer a hand to grab it by — it still
   opens the order, so it offers the pointer that says so. */
.stage-card[draggable="false"] {
    cursor: pointer;
}

/* Reachable without a mouse, and visibly so: the card answers to Enter. */
.stage-card:focus-visible {
    outline: 2px solid var(--brand-gold);
    outline-offset: 2px;
}

.stage-card:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
}

.stage-card.dragging {
    opacity: 0.4;
}

.stage-dropzone {
    min-height: 64px;
    border-radius: var(--radius-md);
    transition: background-color var(--transition-fast);
}

/* Lanes are flex siblings, so one holding 26 cards stretched every lane to
   2,566px and the board with it. That put the board's own horizontal scrollbar
   about 2,100px below the fold — you had to scroll past the entire board to
   reach the control that moves it sideways, by which point no lane was on
   screen. The far stages were effectively unreachable.

   Each lane scrolls inside itself instead, so the board stays roughly a screen
   tall and its horizontal scrollbar stays within reach of the lanes it moves. */
@media (min-width: 1200px) {
    .stage-dropzone {
        max-height: 55vh;
        overflow-y: auto;
        /* Reaching the end of a lane must not start scrolling the page. */
        overscroll-behavior: contain;
        scrollbar-width: thin;
        scrollbar-color: var(--brand-gold-light) transparent;
    }

    .stage-dropzone::-webkit-scrollbar {
        width: 6px;
    }

    .stage-dropzone::-webkit-scrollbar-thumb {
        background-color: var(--brand-gold-light);
        border-radius: var(--radius-pill);
    }
}

.stage-dropzone.drag-over {
    background-color: rgba(171, 140, 82, 0.12);
    outline: 2px dashed var(--brand-gold);
    outline-offset: 4px;
}

.priority-rush {
    border-left: 4px solid var(--brand-rush);
}

.priority-normal {
    border-left: 4px solid var(--brand-gold);
}

.priority-low {
    border-left: 4px solid #b0a89a;
}

.priority-dot {
    display: inline-block;
    width: 0.6rem;
    height: 0.6rem;
    border-radius: 50%;
    margin-right: 0.4rem;
}

.priority-dot.priority-rush {
    background-color: var(--brand-rush);
    border-left: none;
}

.priority-dot.priority-normal {
    background-color: var(--brand-gold);
    border-left: none;
}

.priority-dot.priority-low {
    background-color: #b0a89a;
    border-left: none;
}

.board-scroll {
    overflow-x: auto;
    padding-bottom: 1rem;
    scroll-snap-type: x proximity;
    scrollbar-width: thin;
    scrollbar-color: var(--brand-gold-light) transparent;
}

.board-scroll .stage-column {
    scroll-snap-align: start;
}

.board-scroll::-webkit-scrollbar {
    height: 8px;
}

.board-scroll::-webkit-scrollbar-track {
    background: transparent;
}

.board-scroll::-webkit-scrollbar-thumb {
    background-color: var(--brand-gold-light);
    border-radius: var(--radius-pill);
}

.dashboard-empty {
    background-color: var(--brand-cream-soft);
    border-radius: var(--radius-lg);
    padding: 3rem 2rem;
    text-align: center;
    color: var(--brand-muted);
}

.dashboard-empty .stat-icon {
    margin: 0 auto 1rem;
}

/* Order sheet / print */

.order-sheet img {
    max-width: 320px;
}

.order-sheet .brand-header {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    margin-bottom: 1rem;
}

.order-sheet .brand-header img {
    height: 32px;
    width: auto;
}

/* Scan station */

#scan-input {
    font-size: 1rem;
    border-color: var(--brand-gold);
    border-radius: var(--radius-md);
}

#scan-input:focus {
    border-color: var(--brand-gold-dark);
    box-shadow: 0 0 0 0.25rem rgba(171, 140, 82, 0.25);
}

.scan-ready {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--brand-muted);
    cursor: pointer;
    user-select: none;
}

.scan-dot {
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: #c9c4b8;
    transition: background var(--transition-fast), box-shadow var(--transition-fast);
}

.scan-ready.is-ready {
    color: #2e7d32;
}

.scan-ready.is-ready .scan-dot {
    background: #2e7d32;
    box-shadow: 0 0 0 4px rgba(46, 125, 50, 0.18);
}

#reader {
    width: 100%;
    border-radius: var(--radius-md);
    overflow: hidden;
}

#reader video {
    border-radius: var(--radius-md);
    width: 100% !important;
    height: auto !important;
}

/* Login */

.login-card {
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
}

@media print {
    .no-print {
        display: none !important;
    }
    body {
        background: #fff;
    }
}

/* Order detail: photo gallery */

.gallery-grid {
    display: grid;
    grid-template-columns: 2fr 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    gap: 8px;
    height: 380px;
    border-radius: var(--radius-lg);
    overflow: hidden;
}

.gallery-grid a {
    display: block;
    position: relative;
    overflow: hidden;
    background-color: var(--brand-cream-soft);
}

.gallery-grid img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform var(--transition-fast);
}

.gallery-grid a:hover img {
    transform: scale(1.03);
}

.gallery-tile-1 {
    grid-row: 1 / span 2;
    grid-column: 1;
}

.gallery-tile-2 {
    grid-row: 1;
    grid-column: 2;
}

.gallery-tile-3 {
    grid-row: 1;
    grid-column: 3;
}

.gallery-tile-4 {
    grid-row: 2;
    grid-column: 2;
}

.gallery-tile-5 {
    grid-row: 2;
    grid-column: 3;
}

.gallery-file-tile {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.3rem;
    padding: 0.5rem;
    text-align: center;
    color: var(--brand-charcoal-soft);
}

.gallery-file-ext {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.1rem;
    color: var(--brand-gold-dark);
}

.gallery-more-overlay {
    position: absolute;
    inset: 0;
    background: rgba(33, 33, 33, 0.55);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
}

/* Order detail: facts row, sticky box, history */

.facts-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    font-size: 1rem;
}

.facts-dot {
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background-color: var(--brand-muted);
    display: inline-block;
}

/* Parks below a sticky page header where there is one; pages without one keep
   the plain offset. The variable is set from the header's measured height. */
.sticky-box {
    position: sticky;
    top: var(--sticky-box-top, 1.5rem);
}

/* Order form: customer typeahead */

.customer-combobox {
    position: relative;
}

.customer-results {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    z-index: 1050;
    background-color: #fff;
    border: 1px solid #ddd6c7;
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
    /* Deep enough that a thirty-colour catalog is a short scroll rather
       than a long one. */
    max-height: 340px;
    overflow-y: auto;
    padding: 0.35rem;
}

.customer-result-item {
    display: block;
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    padding: 0.5rem 0.7rem;
    border-radius: var(--radius-sm);
    font-size: 0.9rem;
    color: var(--brand-text);
}

.customer-result-item:hover,
.customer-result-item:focus {
    background-color: var(--brand-cream-soft);
}

.customer-result-create {
    color: var(--brand-gold-dark);
    font-weight: 600;
    border-top: 1px solid #ece6d8;
    margin-top: 0.25rem;
    padding-top: 0.6rem;
}

/* Said when the chosen material has nothing in the catalog under it, so an
   empty list reads as an answer rather than a broken search. */
.fabric-results-note {
    padding: 0.5rem 0.7rem;
    font-size: 0.85rem;
    line-height: 1.4;
    color: var(--brand-muted);
}

/* A voice note that would not play says why, rather than leaving a button
   that does nothing. Only ever rendered after a failure. */
.vn-error {
    margin-top: 0.4rem;
    font-size: 0.78rem;
    line-height: 1.35;
    color: var(--brand-rush);
}

/* Trim fabric: two alternatives from the paper order form. Radios wearing
   chips, so the browser enforces "one of" and the page still reads like the
   sheet it came from. */
.tf-choice {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    align-items: center;
}

.tf-opt {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    cursor: pointer;
    padding: 0.55rem 0.9rem;
    border: 1px solid #ded6c5;
    border-radius: var(--radius-md);
    background-color: #fff;
    font-size: 0.9rem;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

.tf-opt:hover {
    border-color: var(--brand-gold-light);
}

.tf-opt input {
    margin: 0;
}

.tf-opt.is-on {
    border-color: var(--brand-gold);
    background-color: var(--brand-cream-soft);
    font-weight: 600;
}

/* Only there once something is chosen: a radio cannot be un-picked by
   clicking it again, and an order may legitimately have neither. */
.tf-clear {
    font-size: 0.82rem;
    color: var(--brand-muted);
    background: none;
    border: 0;
    text-decoration: underline;
    padding: 0.25rem 0.4rem;
}

.tf-clear:hover {
    color: var(--brand-text);
}

[data-theme="dark"] .tf-opt {
    background-color: var(--surface);
    border-color: var(--line);
}

[data-theme="dark"] .tf-opt.is-on {
    background-color: var(--surface-raised);
    border-color: var(--brand-gold);
}

/* Embroidery queue: the machine's version of the print queue. Shares
   .print-row and its bar; these are the parts that differ. */
.file-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    padding: 0.15rem 0.45rem;
    border-radius: var(--radius-sm);
    background-color: var(--brand-gold-pale);
    color: var(--brand-gold-dark);
}

.eq-files {
    margin-top: 0.35rem;
}

/* An order that reached the machine with no file to run. Loud on purpose:
   it is the row somebody has to act on, and nothing else in the app says it. */
.print-row-missing {
    border-color: var(--brand-rush);
}

.eq-missing {
    margin-top: 0.35rem;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--brand-rush);
}

/* Workplace blocks: the Members roster and the Access editor both group
   people by shop, so the styling lives here rather than inline in one of
   them and copied into the other. */
/* One block per workplace. The header carries the name and the tally, and
   the panel below it holds that shop's people — so the two read as one
   thing rather than a caption floating above a card. */
.store-block { margin-bottom: 1.25rem; }

.store-head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.7rem 1rem;
    border: 1px solid #e4ddcd;
    border-bottom: none;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    background-color: var(--brand-cream-soft);
}

.store-name {
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    font-size: 0.78rem;
    color: var(--brand-gold-on-white-text);
}

.store-tally {
    margin-left: auto;
    font-size: 0.78rem;
    color: var(--brand-muted);
}

.store-block .panel {
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
    margin-bottom: 0;
}

.store-empty {
    padding: 0.9rem 1rem;
    font-size: 0.85rem;
    color: var(--brand-muted);
    background-color: #fff;
    border: 1px solid #e4ddcd;
    border-top: none;
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}

[data-theme="dark"] .store-head,
[data-theme="dark"] .store-empty {
    background-color: var(--surface);
    border-color: var(--line);
}

/* Order form: two panes, sections, and a pinned action bar.

   The form is twenty-odd fields long. Flat, they read as one undifferentiated
   wall; grouped into named sections they read as the six decisions an order
   actually is. The style diagram and the cutting specs are not decisions —
   they are what you look at while making them — so they leave the flow and
   become a rail that stays put. */

/* No `align-items: start` here, however much the rail looks like it wants it.
   That shrinks the rail's grid item to its own content — a few hundred pixels
   against the form's couple of thousand — and a sticky child cannot travel
   further than its containing block, so the rail would scroll away with
   everything else. Stretched, the item spans the row and the rail can ride
   the whole form. */
.order-form-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 22rem;
    gap: 1.25rem;
}

.order-form-main {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    min-width: 0;
}

/* One column on a phone, with the rail landing directly after the garment
   section — where the style has just been chosen and the diagram first means
   something. It cannot simply be reordered as a whole, because its siblings
   are nested inside the main pane; `display: contents` dissolves that pane so
   the sections become flex items alongside the rail and can be ordered past
   it. Everything from Embroidery down is marked late and goes after.

   This has to stay below `.order-form-main` above: same specificity, so the
   later rule wins, and above it the `display: flex` would put the pane back
   and quietly undo the whole reorder. */
@media (max-width: 991.98px) {
    .order-form-layout {
        display: flex;
        flex-direction: column;
    }

    .order-form-main {
        display: contents;
    }

    .form-section-late {
        order: 1;
    }
}

.form-section {
    background-color: var(--brand-cream-soft);
    border: 1px solid #e4ddcd;
    border-radius: var(--radius-lg);
    padding: 1.25rem;
}

/* Linked orders is a commitment to create more orders, not a field. Gold edge
   so it is impossible to fill the form in without noticing it. */
.form-section-accent {
    border-color: var(--brand-gold);
}

.form-section-title {
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--brand-gold-on-white-text);
    margin: 0 0 1rem;
}

/* A divider inside a section: front fabrics, back fabric, lettering. Quieter
   than a section title, because it is a part of one, not a new one. */
.form-subhead {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    font-weight: 600;
    font-size: 0.9rem;
    color: var(--brand-text);
    margin: 1.25rem 0 0.6rem;
    padding-top: 0.9rem;
    border-top: 1px solid #e8e1d2;
}

.front-fabric-row,
.fabric-picker,
.color-row {
    margin-bottom: 0.5rem;
}

/* The delete sits at the end of the row it deletes. Full width rows used to
   push it most of a screen away from anything it referred to. */
.row-remove-col {
    display: flex;
    align-items: flex-end;
    align-self: stretch;
    padding-bottom: 0.15rem;
}

.row-remove {
    width: 2rem;
    height: 2rem;
    line-height: 1;
    font-size: 1.15rem;
    border-radius: var(--radius-sm);
    border: 1px solid #ded6c5;
    background-color: #fff;
    color: var(--brand-muted);
    transition: color 0.15s ease, border-color 0.15s ease, background-color 0.15s ease;
}

.row-remove:hover,
.row-remove:focus-visible {
    color: var(--brand-rush);
    border-color: var(--brand-rush);
    background-color: #fdf3f1;
}

.btn-add-row {
    border: 1px dashed #cfc5ae;
    background-color: transparent;
    color: var(--brand-gold-on-white-text);
    font-size: 0.85rem;
    font-weight: 600;
    border-radius: var(--radius-sm);
    padding: 0.4rem 0.85rem;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

.btn-add-row:hover,
.btn-add-row:focus-visible {
    border-style: solid;
    border-color: var(--brand-gold);
    background-color: #fff;
}

/* --- The reference rail --- */

/* Clear of the sticky shell bar, which is 59px and only exists at this
   breakpoint — the same one that turns the rail on. */
.rail-sticky {
    position: sticky;
    top: calc(59px + 1rem);
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

@media (max-width: 991.98px) {
    .rail-sticky {
        position: static;
    }
}

.rail-card {
    background-color: var(--brand-cream-soft);
    border: 1px solid #e4ddcd;
    border-radius: var(--radius-lg);
    padding: 1rem;
}

.rail-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.6rem;
}

.rail-card-title {
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--brand-gold-on-white-text);
}

.rail-card-note {
    font-size: 0.78rem;
    color: var(--brand-muted);
    margin-top: 0.4rem;
}

/* --- The pinned action bar --- */

.order-form-bar {
    position: sticky;
    bottom: 0;
    z-index: 5;
    display: flex;
    justify-content: flex-end;
    gap: 0.6rem;
    margin-top: 1.25rem;
    padding: 0.85rem 1rem;
    border-radius: var(--radius-lg);
    border: 1px solid #e4ddcd;
    background-color: rgba(252, 251, 249, 0.92);
    backdrop-filter: blur(8px);
}

.order-form-bar .btn {
    min-width: 7rem;
}

/* The phone already has a fixed tab bar across the bottom, so the action bar
   has to stop above it rather than under it. `.main-wrapper` reserves 6.5rem
   at the foot of the page on this breakpoint; the bar takes the top of that
   and the tab bar keeps the rest. */
@media (max-width: 991.98px) {
    .order-form-bar {
        bottom: calc(3.5rem + env(safe-area-inset-bottom, 0px));
    }
}

@media (max-width: 575.98px) {
    .order-form-bar {
        border-radius: var(--radius-md);
    }

    .order-form-bar .btn {
        flex: 1 1 0;
        min-width: 0;
    }
}

/* Order form: style preview, cutting specs, color rows */

.style-image-preview {
    background-color: var(--brand-cream-soft);
    border-radius: var(--radius-md);
    min-height: 80px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0.5rem;
}

.style-image-preview img {
    max-height: 140px;
    max-width: 100%;
    border-radius: var(--radius-sm);
}

/* On the order form the diagram is the thing being read while the order is
   typed up — which panel is which, where the numbers sit — so it gets real
   room rather than a thumbnail's worth. */
.style-image-preview-lg {
    min-height: 260px;
}

.style-image-preview-lg img {
    max-height: 420px;
}

@media (max-width: 767.98px) {
    .style-image-preview-lg {
        min-height: 200px;
    }

    .style-image-preview-lg img {
        max-height: 300px;
    }
}

.cutting-spec-box {
    border: 1px solid #ddd6c7;
    border-radius: var(--radius-md);
    padding: 0.6rem 0.9rem;
    background-color: var(--brand-cream-soft);
    min-height: 44px;
    font-size: 0.9rem;
}

/* A repeatable row inside a section. The section behind it is already
   cream-soft, so these go white to read as separate rows on it rather than
   dissolving into it. */
.color-row,
.front-fabric-row,
.fabric-picker {
    padding: 0.5rem;
    border-radius: var(--radius-md);
    border: 1px solid #ece5d6;
    background-color: #fff;
}

/* Order detail: bento grid */

.bento-order-grid {
    display: grid;
    grid-template-columns: 2fr 2fr 1.2fr;
    grid-template-areas:
        "facts facts action"
        "linked linked action"
        "about about action"
        "embroidery embroidery action"
        "files files action"
        "digitized digitized action"
        "chat chat chat"
        "history history history";
    gap: 1rem;
}

.bento-facts { grid-area: facts; }
.bento-action { grid-area: action; }
.bento-linked { grid-area: linked; }
.bento-about { grid-area: about; }
.bento-embroidery { grid-area: embroidery; }
.bento-files { grid-area: files; }
.bento-digitized { grid-area: digitized; }
.bento-chat { grid-area: chat; }
.bento-history { grid-area: history; }

@media (max-width: 767px) {
    .bento-order-grid {
        grid-template-columns: 1fr;
        grid-template-areas:
            "facts"
            "action"
            "linked"
            "about"
            "embroidery"
            "files"
            "digitized"
            "chat"
            "history";
    }

    .bento-action .sticky-box {
        position: static;
    }
}

/* Linked orders: one job made in more than one size. The size leads each
   row, because that is the thing that differs between them. */
.linked-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.linked-item {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.5rem 0.6rem;
    border: 1px solid var(--line, #ece6d8);
    border-radius: var(--radius-sm);
    margin-bottom: 0.4rem;
}

.linked-item.is-current {
    background-color: var(--brand-cream-soft);
    border-color: var(--brand-gold);
}

.linked-size {
    flex-shrink: 0;
    min-width: 2.6rem;
    padding: 0.15rem 0.45rem;
    border-radius: var(--radius-sm);
    background-color: var(--brand-gold-light);
    color: var(--brand-charcoal);
    font-weight: 700;
    font-size: 0.85rem;
    text-align: center;
}

.linked-number {
    font-weight: 600;
    color: var(--brand-text);
}

a.linked-number:hover {
    color: var(--brand-gold-dark);
}

.linked-status {
    margin-left: auto;
}

.linked-here {
    flex-shrink: 0;
    font-size: 0.78rem;
    color: var(--brand-muted);
}

.linked-item.is-current .linked-status {
    margin-left: auto;
}

.linked-due {
    flex-shrink: 0;
    font-size: 0.8rem;
    color: var(--brand-muted);
}

/* Sits under the grid rather than inside it, so it spans the page and
   reads as the closing line of the order rather than one more card
   competing with the ones above. */
.linked-panel {
    margin-top: 1rem;
}

@media (max-width: 575.98px) {
    .linked-item {
        flex-wrap: wrap;
    }

    /* Pushed to its own line rather than squeezed against the number. */
    .linked-status {
        margin-left: 0;
    }
}

/* CheckboxSelectMultiple renders a wrapping div with one div per choice,
   not a list — so the row is laid out on that wrapper. */
/* The matching piece has been chosen, so the row is answered. Faded and
   unpressable rather than removed: a row that vanishes says nothing, and
   the person reading it wonders whether they are on the wrong screen. */
.link-sizes-done {
    opacity: 0.45;
    pointer-events: none;
}

.link-sizes > div {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
}

.link-sizes label {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-weight: 700;
    padding: 0.3rem 0.7rem;
    border: 1px solid var(--line, #ece6d8);
    border-radius: var(--radius-sm);
    background-color: var(--surface, #fff);
    cursor: pointer;
}

.link-sizes label:hover {
    border-color: var(--brand-gold);
}

/* The chip that marks a set wherever orders are listed. */
.set-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.05rem 0.4rem;
    border-radius: var(--radius-sm);
    background-color: var(--brand-gold-light);
    color: var(--brand-charcoal);
    font-size: 0.75rem;
    font-weight: 700;
}

.history-avatar {
    flex-shrink: 0;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background-color: var(--brand-gold-light);
    color: var(--brand-charcoal);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 0.85rem;
}

@media (max-width: 767px) {
    .navbar-brand {
        font-size: 1rem;
    }

    .btn {
        min-height: 44px;
    }

    .page-header {
        flex-direction: column !important;
        align-items: center !important;
        text-align: center;
        gap: 0.75rem;
    }

    .page-header > div {
        width: 100%;
    }

    .page-header.page-header-simple {
        flex-direction: row !important;
        align-items: center !important;
        text-align: left;
        gap: 0.5rem;
    }

    .page-header-simple h1.h3 {
        flex: 1;
        text-align: left;
        min-width: 0;
        font-size: 1.05rem;
        white-space: nowrap;
    }

    .page-header-simple .btn {
        flex: 0 0 auto;
        padding: 0.4rem 0.6rem;
        font-size: 0.72rem;
        white-space: nowrap;
    }

    /* Spacer only existed to center the title; not needed now titles are left-aligned. */
    .page-header-simple .page-header-spacer {
        display: none !important;
    }

    .page-title {
        text-align: center;
    }

    .form-actions {
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
        gap: 0.5rem;
    }

    #scan-input {
        font-size: 1rem;
    }

    h1.h3 {
        font-size: 1.4rem;
    }

    .card-body {
        padding: 1.1rem;
    }

    .gallery-grid {
        grid-template-columns: 1fr;
        grid-template-rows: 220px repeat(4, 120px);
        height: auto;
    }

    .gallery-tile-1,
    .gallery-tile-2,
    .gallery-tile-3,
    .gallery-tile-4,
    .gallery-tile-5 {
        grid-column: 1;
    }

    .gallery-tile-1 {
        grid-row: 1;
    }

    .gallery-tile-2 {
        grid-row: 2;
    }

    .gallery-tile-3 {
        grid-row: 3;
    }

    .gallery-tile-4 {
        grid-row: 4;
    }

    .gallery-tile-5 {
        grid-row: 5;
    }

    .sticky-box {
        position: static;
    }
}

/* ------------------------------------------------------------------
   Liquid glass (ported from the KokonutUI React component to plain CSS,
   tuned for the Mefoar cream/gold palette).
   Structure mirrors the original: rim highlights + inner glow + inner
   shadow, translucent blurred backdrop, and an SVG displacement filter
   for the "liquid" refraction where the browser supports it.
   ------------------------------------------------------------------ */

/* Ambient color behind the dashboard so the glass has something to refract. */
.glass-stage {
    position: relative;
}

/* (dashboard ambient wash removed) */

.glass-stage > * {
    position: relative;
    z-index: 1;
}

.glass-card {
    position: relative;
    overflow: hidden;
    border: 1px solid rgba(255, 255, 255, 0.55);
    border-radius: var(--radius-lg);
    background: rgba(255, 255, 255, 0.90);
    -webkit-backdrop-filter: blur(18px) saturate(150%);
    backdrop-filter: blur(18px) saturate(150%);
    box-shadow:
        0 0 6px rgba(0, 0, 0, 0.03),
        0 2px 10px rgba(0, 0, 0, 0.10),
        inset 2px 2px 0.5px -2px rgba(255, 255, 255, 0.95),
        inset -2px -2px 0.5px -2px rgba(120, 100, 60, 0.35),
        inset 1px 1px 1px -0.5px rgba(255, 255, 255, 0.85),
        inset -1px -1px 1px -0.5px rgba(120, 100, 60, 0.22),
        inset 0 0 8px 6px rgba(255, 255, 255, 0.22),
        inset 0 0 2px 2px rgba(255, 255, 255, 0.35),
        0 0 14px rgba(255, 255, 255, 0.30);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}

/* The refraction layer. Sits behind the content; only browsers that support
   an SVG url() backdrop-filter will show the displacement, everything else
   simply keeps the blur above. */
.glass-card > .glass-refraction {
    position: absolute;
    inset: 0;
    z-index: 0;
    border-radius: inherit;
    pointer-events: none;
    -webkit-backdrop-filter: url("#liquid-glass");
    backdrop-filter: url("#liquid-glass");
}

.glass-card > *:not(.glass-refraction) {
    position: relative;
    z-index: 1;
}

/* Hover: lift + the sweeping shine from the original component. */
.glass-card::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 2;
    border-radius: inherit;
    pointer-events: none;
    background: linear-gradient(100deg, transparent 20%, rgba(255, 255, 255, 0.55) 50%, transparent 80%);
    opacity: 0;
    transition: opacity 0.25s ease-out;
}

@media (hover: hover) and (pointer: fine) {
    a.glass-card:hover,
    .glass-card:hover {
        transform: translateY(-2px);
        box-shadow:
            0 0 6px rgba(0, 0, 0, 0.04),
            0 10px 26px rgba(0, 0, 0, 0.14),
            inset 2px 2px 0.5px -2px rgba(255, 255, 255, 1),
            inset -2px -2px 0.5px -2px rgba(120, 100, 60, 0.38),
            inset 1px 1px 1px -0.5px rgba(255, 255, 255, 0.9),
            inset 0 0 10px 6px rgba(255, 255, 255, 0.26),
            0 0 18px rgba(255, 255, 255, 0.36);
    }

    .glass-card:hover::after {
        opacity: 1;
    }
}

/* Text needs a touch more weight over a translucent surface. */
.glass-card .stat-value {
    color: var(--brand-charcoal);
    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
}

.glass-card .stat-icon {
    background-color: rgba(255, 255, 255, 0.55);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.7);
}

@media (prefers-reduced-motion: reduce) {
    .glass-card,
    .glass-card::after {
        transition: none;
    }
    a.glass-card:hover,
    .glass-card:hover {
        transform: none !important;
    }
}

/* Dark variant: the hero tile stays a high-contrast anchor, but glassy. */
.glass-card.bento-hero {
    background: linear-gradient(135deg, rgba(33, 33, 33, 0.82) 0%, rgba(52, 48, 42, 0.78) 100%);
    border-color: rgba(255, 255, 255, 0.18);
    box-shadow:
        0 0 6px rgba(0, 0, 0, 0.06),
        0 2px 10px rgba(0, 0, 0, 0.22),
        inset 2px 2px 0.5px -2px rgba(255, 255, 255, 0.55),
        inset -2px -2px 0.5px -2px rgba(0, 0, 0, 0.6),
        inset 1px 1px 1px -0.5px rgba(255, 255, 255, 0.35),
        inset 0 0 8px 6px rgba(255, 255, 255, 0.06),
        0 0 14px rgba(0, 0, 0, 0.18);
}

.glass-card.bento-hero .stat-value,
.glass-card.bento-hero .stat-label,
.glass-card.bento-hero .stat-progress-caption {
    color: #fff;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
}

.glass-card.bento-hero .stat-icon {
    background-color: rgba(255, 255, 255, 0.16);
    color: #fff;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22);
}

.glass-card.bento-hero::after {
    display: none;
}

.glass-card.bento-hero:hover {
    box-shadow:
        0 10px 26px rgba(0, 0, 0, 0.3),
        inset 2px 2px 0.5px -2px rgba(255, 255, 255, 0.65),
        inset 0 0 10px 6px rgba(255, 255, 255, 0.08);
}

/* ==================================================================
   App-wide liquid glass
   Every card picks up the glass treatment, with an ambient color field
   behind the whole app so there's something for the glass to refract.
   ================================================================== */

/* Ambient field behind everything (fixed so it doesn't scroll away). */
/* (ambient wash removed - it washed the page out) */

/* The wrapper is positioned so cards can layer their own refraction, but it
   must NOT carry a z-index: that would open a stacking context and trap every
   modal underneath Bootstrap's backdrop, leaving the dialog greyed out and its
   close button unclickable. (The ambient wash this once sat above is gone.) */
.main-wrapper {
    position: relative;
}

/* Base glass for every card in the app. */
.card {
    position: relative;
    border: 1px solid rgba(255, 255, 255, 0.55);
    background: rgba(255, 255, 255, 0.90);
    -webkit-backdrop-filter: blur(18px) saturate(150%);
    backdrop-filter: blur(18px) saturate(150%);
    box-shadow:
        0 0 6px rgba(0, 0, 0, 0.03),
        0 2px 10px rgba(0, 0, 0, 0.10),
        inset 2px 2px 0.5px -2px rgba(255, 255, 255, 0.95),
        inset -2px -2px 0.5px -2px rgba(120, 100, 60, 0.35),
        inset 1px 1px 1px -0.5px rgba(255, 255, 255, 0.85),
        inset 0 0 8px 6px rgba(255, 255, 255, 0.22),
        0 0 14px rgba(255, 255, 255, 0.30);
}

/* Panels that sit on top of a card shouldn't double-frost. */
.card .card,
.card .list-group,
.card .list-group-item {
    background: transparent;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    box-shadow: none;
}

.card .list-group-item {
    border-color: rgba(120, 100, 60, 0.14);
}

/* Modals and the offcanvas-style panels get the same treatment. */
.modal-content {
    border: 1px solid rgba(255, 255, 255, 0.6);
    background: rgba(255, 255, 255, 0.95);
    -webkit-backdrop-filter: blur(24px) saturate(150%);
    backdrop-filter: blur(24px) saturate(150%);
}

/* Form controls stay solid so they remain obviously editable/legible. */
.form-control,
.form-select,
textarea.form-control {
    background-color: rgba(255, 255, 255, 0.92);
}

.form-control:focus,
.form-select:focus {
    background-color: #fff;
}

/* Tables inside glass cards: kill the opaque default so the frost shows. */
.card .table {
    --bs-table-bg: transparent;
}

/* PERFORMANCE: grids that can render hundreds of cards (style catalog,
   cutting specs) keep the glass look but drop backdrop-filter, which is
   expensive to composite per-element while scrolling on a phone. */
.style-grid .card,
.style-catalog-card,
.spec-card,
.stage-card,
.list-card {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    background: rgba(255, 255, 255, 0.94);
}

/* Print: no frost, no ambient wash — plain white for the order sheet. */
@media print {
    body::before {
        display: none;
    }
    .card,
    .modal-content {
        background: #fff !important;
        -webkit-backdrop-filter: none !important;
        backdrop-filter: none !important;
        box-shadow: none !important;
        border-color: #ddd !important;
    }
}


/* ==================================================================
   Order chat
   ================================================================== */

.chat-thread {
    max-height: 26rem;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding-right: 0.25rem;
}

.chat-empty {
    color: var(--brand-muted);
    font-size: 0.9rem;
    padding: 1.25rem 0;
    text-align: center;
}

.chat-msg {
    display: flex;
    gap: 0.6rem;
    align-items: flex-start;
    max-width: 82%;
}

.chat-msg-mine {
    align-self: flex-end;
    flex-direction: row-reverse;
}

.chat-avatar {
    flex-shrink: 0;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background-color: var(--brand-gold-light);
    color: var(--brand-charcoal);
    font-weight: 700;
    font-size: 0.75rem;
    display: flex;
    align-items: center;
    justify-content: center;
}

.chat-bubble {
    background-color: #fff;
    border: 1px solid var(--brand-gold-light);
    border-radius: var(--radius-md);
    padding: 0.5rem 0.75rem;
    min-width: 0;
}

.chat-msg-mine .chat-bubble {
    background-color: var(--brand-gold-pale);
    border-color: var(--brand-gold);
}

.chat-meta {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    font-size: 0.7rem;
    color: var(--brand-muted);
    margin-bottom: 0.2rem;
}

.chat-meta-author {
    font-weight: 700;
    color: var(--brand-charcoal);
}

.chat-text {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    font-size: 0.92rem;
}

.chat-bubble img.chat-photo,
.chat-bubble video.chat-video {
    display: block;
    max-width: 100%;
    max-height: 18rem;
    border-radius: var(--radius-sm);
    margin-top: 0.15rem;
}

.chat-bubble img.chat-photo {
    cursor: zoom-in;
}

.chat-bubble audio.chat-audio {
    display: block;
    margin-top: 0.15rem;
    max-width: 100%;
    min-width: 13rem;
}

/* Chats: one row per order that has a conversation. */

.chat-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.chat-row {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    padding: 0.7rem 0.9rem;
    border-radius: var(--radius-md);
    background-color: var(--brand-cream-soft);
    border: 1px solid rgba(184, 158, 106, 0.22);
    text-decoration: none;
    color: inherit;
    -webkit-tap-highlight-color: transparent;
}

.chat-row-avatar {
    flex-shrink: 0;
    width: 48px;
    height: 48px;
    border-radius: 50%;
    overflow: hidden;
    background-color: var(--brand-gold-light);
    color: var(--brand-charcoal);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
}

.chat-row-avatar img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.chat-row-body {
    flex: 1;
    min-width: 0;
}

.chat-row-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
}

.chat-row-title {
    font-weight: 700;
    letter-spacing: -0.01em;
}

.chat-row-when {
    flex-shrink: 0;
    font-size: 0.72rem;
    color: var(--brand-muted);
    font-variant-numeric: tabular-nums;
}

.chat-row-sub {
    font-size: 0.82rem;
    color: var(--brand-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.chat-row-bottom {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-top: 0.15rem;
}

.chat-row-preview {
    flex: 1;
    min-width: 0;
    font-size: 0.85rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.chat-row-author {
    font-weight: 600;
    color: var(--brand-muted);
}

/* Gold pill = unread. Plain grey number = how long the thread is. */
.chat-row-count {
    flex-shrink: 0;
    min-width: 1.4rem;
    height: 1.4rem;
    padding: 0 0.4rem;
    border-radius: 999px;
    background-color: var(--brand-gold);
    color: #fff;
    font-size: 0.7rem;
    font-weight: 700;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.chat-row-total {
    flex-shrink: 0;
    font-size: 0.72rem;
    color: var(--brand-muted);
    font-variant-numeric: tabular-nums;
}

.chat-row.is-unread {
    background-color: #fff;
    border-color: var(--brand-gold);
}

.chat-row.is-unread .chat-row-title,
.chat-row.is-unread .chat-row-preview {
    font-weight: 700;
}

.chat-row.is-unread .chat-row-preview,
.chat-row.is-unread .chat-row-author {
    color: var(--brand-charcoal);
}

/* The conversation currently shown in the panel beside the list. Reads as
   "you are here" rather than as another unread — a filled left edge, so it
   never competes with the gold border unread already uses. */
.chat-row.is-open {
    background-color: #fff;
    border-color: var(--brand-gold);
    box-shadow: inset 3px 0 0 var(--brand-gold);
}

/* Unread count on the Chats nav entry. */
.nav-badge {
    position: absolute;
    top: 2px;
    right: 2px;
    min-width: 17px;
    height: 17px;
    padding: 0 4px;
    border-radius: 999px;
    background-color: var(--brand-rush);
    color: #fff;
    font-size: 0.6rem;
    font-weight: 700;
    line-height: 17px;
    text-align: center;
}

.sidebar-icon { position: relative; }

.dropdown-item .nav-badge {
    position: static;
    display: inline-block;
    margin-left: 0.4rem;
    vertical-align: middle;
}

@media (hover: hover) and (pointer: fine) {
    .chat-row:hover {
        border-color: var(--brand-gold);
        background-color: #fff;
    }
}

/* ------------------------------------------------------------------
   Mark-up editor
   ------------------------------------------------------------------
   Draw on the style diagram and save it back to the order as a design
   file. Full screen: the diagram is the work, and a shop marking up
   which fabric goes where needs the whole screen for it.
   ------------------------------------------------------------------ */

.markup-overlay {
    position: fixed;
    inset: 0;
    z-index: 3000;
    display: flex;
    flex-direction: column;
    background: #201d18;
}

.markup-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem 1.25rem;
    padding: 0.7rem 1rem;
    background: #fff;
    border-bottom: 1px solid #e3ddd1;
}

.markup-tools,
.markup-colors,
.markup-actions {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.markup-actions {
    margin-left: auto;
    flex-wrap: wrap;
}

.markup-tool {
    border: 1px solid #e3ddd1;
    background: #fff;
    border-radius: 999px;
    padding: 0.35rem 0.8rem;
    font-size: 0.85rem;
    font-weight: 600;
    color: #3a342c;
}

.markup-tool.is-active {
    border-color: var(--brand-gold);
    background: var(--brand-gold-pale);
    color: #6b5228;
}

.markup-tool:focus-visible,
.markup-swatch:focus-visible {
    outline: 2px solid var(--brand-gold);
    outline-offset: 2px;
}

.markup-swatch {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    border: 2px solid #fff;
    box-shadow: 0 0 0 1px #d9d3ca;
    background: var(--sw);
    padding: 0;
}

/* The chosen colour gets a ring rather than a tick, which would vanish
   against the darker swatches. */
.markup-swatch.is-active {
    box-shadow: 0 0 0 2px var(--sw);
}

.markup-size {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.8rem;
    color: var(--brand-muted);
}

.markup-size input {
    width: 90px;
}

.markup-stage {
    position: relative;
    flex: 1;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: auto;
}

#markup-canvas {
    background: #fff;
    border-radius: 6px;
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.45);
    touch-action: none;   /* drawing must not pan the page */
    cursor: crosshair;
}

.markup-text-input {
    position: absolute;
    z-index: 2;
    width: 220px;
    padding: 0.3rem 0.5rem;
    border: 2px solid var(--brand-gold);
    border-radius: 6px;
    font-size: 0.9rem;
}

.markup-foot {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.6rem 1rem;
    background: #fff;
    border-top: 1px solid #e3ddd1;
}

.markup-foot .form-control {
    max-width: 520px;
}

@media (max-width: 575.98px) {
    .markup-actions {
        margin-left: 0;
        width: 100%;
    }

    .markup-actions .btn {
        flex: 1;
    }

    .markup-foot {
        flex-direction: column;
        align-items: stretch;
    }

    .markup-foot .form-control {
        max-width: none;
    }
}

/* Order header that stays put while the page scrolls, so the order number,
   the prev/next arrows and the actions stay reachable down in the chat. */

#page-header-sentinel {
    height: 1px;
    margin-bottom: -1px;
}

.page-header-sticky {
    position: sticky;
    top: 0;
    /* Well under Bootstrap's modal backdrop (1050) so dialogs still cover it. */
    z-index: 20;
    background-color: var(--brand-cream);
    /* Bleed past the centred column so card shadows don't show at the edges. */
    margin-left: -1rem;
    margin-right: -1rem;
    padding: 0.75rem 1rem 0.5rem;
    transition: box-shadow 0.2s ease, padding 0.2s ease;
}

.page-header-sticky.is-stuck {
    padding-top: 0.5rem;
    box-shadow: 0 8px 20px -14px rgba(38, 32, 24, 0.55);
}

.page-header-sticky.is-stuck h1.h3 {
    font-size: 1.2rem;
}

/* On a phone the header is two stacked rows; drop the customer line once
   stuck so it doesn't eat the screen. The status is still on the card below. */
@media (max-width: 991.98px) {
    .page-header-sticky.is-stuck .order-head-title > .text-muted {
        display: none;
    }
}

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

/* Previous/next order arrows beside the order number. */

.order-head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.order-nav-btn {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    border: 1px solid var(--brand-gold-light);
    background-color: #fff;
    color: var(--brand-charcoal);
    display: flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
}

.order-nav-btn svg { width: 16px; height: 16px; }

.order-nav-btn.is-disabled {
    opacity: 0.3;
    cursor: default;
}

@media (hover: hover) and (pointer: fine) {
    .order-nav-btn:not(.is-disabled):hover {
        background-color: var(--brand-gold);
        border-color: var(--brand-gold);
        color: #fff;
    }
}

/* On a phone the header stacks and centres, so push the arrows to the
   edges with the order number between them. */
@media (max-width: 767.98px) {
    .order-head {
        justify-content: space-between;
        width: 100%;
    }

    .order-head-title {
        flex: 1;
        min-width: 0;
    }
}

/* Voice notes: waveform player, WhatsApp-style. */

.vn {
    --vn-played: var(--brand-gold);
    --vn-idle: #cfc6b4;
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: 0.2rem;
    min-width: 14rem;
    max-width: 17rem;
}

.chat-msg-mine .vn {
    --vn-idle: rgba(255, 255, 255, 0.45);
    --vn-played: #fff;
}

.vn-play {
    flex-shrink: 0;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    border: none;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--brand-gold);
    color: #fff;
    padding: 0;
    -webkit-tap-highlight-color: transparent;
}

.chat-msg-mine .vn-play {
    background-color: #fff;
    color: var(--brand-gold);
}

.vn-play svg { width: 15px; height: 15px; }
.vn-play .vn-icon-pause { display: none; }
.vn.is-playing .vn-play .vn-icon-play { display: none; }
.vn.is-playing .vn-play .vn-icon-pause { display: block; }

.vn-body {
    flex: 1;
    min-width: 0;
}

.vn-wave {
    height: 26px;
    cursor: pointer;
    touch-action: none;
}

.vn-wave-canvas {
    display: block;
    width: 100%;
    height: 100%;
}

.vn-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-top: 0.1rem;
    font-size: 0.7rem;
    color: var(--brand-muted);
    font-variant-numeric: tabular-nums;
}

.chat-msg-mine .vn-foot { color: rgba(255, 255, 255, 0.8); }

.vn-rate {
    border: none;
    background: transparent;
    color: inherit;
    font-size: 0.68rem;
    font-weight: 700;
    padding: 0 0.25rem;
    border-radius: 0.6rem;
    line-height: 1.5;
    cursor: pointer;
}

.vn-rate:hover { background-color: rgba(0, 0, 0, 0.06); }

/* Recording bar: replaces the composer while the mic is held. */

.chat-recording {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    margin-top: 0.5rem;
    padding: 0.45rem 0.7rem;
    border-radius: 999px;
    background-color: var(--brand-rush);
    color: #fff;
    transform: translateX(var(--vn-slide, 0px));
    transition: background-color 0.15s ease;
}

.chat-recording.is-cancelling { background-color: #7d2f1e; }

.vn-rec-dot {
    flex-shrink: 0;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background-color: #fff;
    animation: vn-blink 1.1s steps(1, end) infinite;
}

@keyframes vn-blink {
    0%, 49% { opacity: 1; }
    50%, 100% { opacity: 0.15; }
}

.vn-rec-time {
    flex-shrink: 0;
    font-size: 0.8rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    min-width: 2.4rem;
}

.vn-rec-wave {
    flex: 1 1 auto;
    height: 22px;
    min-width: 2rem;
    max-width: 7rem; /* leave room for "Slide to cancel" on a narrow phone */
}

.chat-recording.is-locked .vn-rec-wave { max-width: none; }

.vn-rec-wave-canvas { display: block; width: 100%; height: 100%; }

.vn-rec-hint {
    flex: 0 1 auto;
    font-size: 0.72rem;
    opacity: 0.9;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.chat-recording.is-locked .vn-rec-hint { display: none; }

/* Except to say nothing is coming through. A mouse click goes straight to
   the locked state, so hiding the hint there hid this warning on precisely
   the machines that have no microphone to begin with. It takes its space
   from the waveform — a flat line has nothing to show anyway. */
.chat-recording.is-locked.is-quiet .vn-rec-hint { display: block; }
.chat-recording.is-quiet .vn-rec-hint {
    flex: 0 0 auto;
    opacity: 1;
    font-weight: 600;
}
.chat-recording.is-quiet .vn-rec-wave { min-width: 0; }

.vn-rec-cancel,
.vn-rec-send {
    flex-shrink: 0;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    border: none;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: rgba(255, 255, 255, 0.18);
    color: #fff;
    padding: 0;
    -webkit-tap-highlight-color: transparent;
}

.vn-rec-cancel svg,
.vn-rec-send svg { width: 15px; height: 15px; }
.vn-rec-send { background-color: #fff; color: var(--brand-rush); }

/* Slide-up-to-lock pill. Sits above the recording bar, which itself sits
   above the composer this is anchored to. */
.vn-lock {
    position: absolute;
    bottom: 5.9rem;
    right: 0.15rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.15rem;
    padding: 0.35rem 0.3rem;
    border-radius: 999px;
    background-color: var(--brand-cream-soft);
    border: 1px solid var(--brand-gold-light);
    color: var(--brand-muted);
    font-size: 0.6rem;
    animation: vn-lock-hint 1.4s ease-in-out infinite;
    pointer-events: none;
}

.vn-lock svg { width: 13px; height: 13px; }

@keyframes vn-lock-hint {
    0%, 100% { transform: translateY(0); opacity: 0.75; }
    50% { transform: translateY(-4px); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .vn-rec-dot, .vn-lock { animation: none; }
}

.chat-file-link {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.85rem;
    font-weight: 600;
    margin-top: 0.15rem;
}

.chat-delete {
    border: none;
    background: transparent;
    color: var(--brand-muted);
    font-size: 0.7rem;
    padding: 0;
    line-height: 1;
    cursor: pointer;
}

.chat-delete:hover {
    color: var(--brand-rush);
}

/* One of several rows that are the same fabric. The choice is which to
   keep, so the rows are the choice rather than a list beside one. */
.fabric-twin {
    display: flex;
    align-items: baseline;
    gap: 0.55rem;
    padding: 0.4rem 0.5rem;
    border-radius: 0.45rem;
    cursor: pointer;
}

.fabric-twin:hover { background-color: rgba(171, 140, 82, 0.08); }

.fabric-twin input {
    width: 1.05rem;
    height: 1.05rem;
    accent-color: var(--brand-gold);
    flex: none;
}

.fabric-twin-label { font-weight: 600; overflow-wrap: anywhere; }

.fabric-twin-meta {
    margin-left: auto;
    font-size: 0.82rem;
    color: var(--brand-muted);
    white-space: nowrap;
}

/* --- sending a proof for approval -------------------------------------- */
.approval-shell { max-width: 44rem; }

/* Who it comes from is not a choice, so it is stated rather than offered.
   Set as facts on a panel, not as disabled boxes: a greyed-out field reads
   as something broken rather than something decided. */
.approval-facts {
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--line);
    border-radius: 0.6rem;
    background-color: rgba(171, 140, 82, 0.06);
    font-size: 0.88rem;
}

.approval-facts div + div { margin-top: 0.25rem; }

.approval-facts span {
    display: inline-block;
    min-width: 7.5rem;
    font-family: var(--font-secondary);
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--brand-muted);
}

.approval-file {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    padding: 0.35rem 0;
    cursor: pointer;
}

.approval-file input {
    width: 1.05rem;
    height: 1.05rem;
    accent-color: var(--brand-gold);
    flex: none;
}

.approval-file-name { font-weight: 600; overflow-wrap: anywhere; }

.approval-file-meta {
    margin-left: auto;
    font-size: 0.8rem;
    color: var(--brand-muted);
    white-space: nowrap;
}

/* A file's row on the order page: what it is on the left, what you can do
   with it on the right. The two acts are buttons rather than a link and a
   word — one is a save and one cannot be undone, and neither should be
   something a thumb finds by accident. */
.file-row-line {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.3rem 0;
    border-bottom: 1px solid var(--line);
}

.file-row-line:last-of-type { border-bottom: 0; }

.file-acts {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-left: auto;
    flex-shrink: 0;
}

.file-act {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.2rem 0.6rem;
    font-size: 0.78rem;
    white-space: nowrap;
}

.file-act svg {
    width: 0.85rem;
    height: 0.85rem;
}

@media (max-width: 575.98px) {
    /* On a phone the name gets the line and the two buttons get the next
       one — a name squeezed to three letters is a name that identifies
       nothing. */
    .file-row-line {
        flex-wrap: wrap;
        padding: 0.5rem 0;
    }
    .file-acts { margin-left: 0; }
}

/* Save. Quiet in the meta line until wanted, and a real tap target on a
   phone — the whole point is somebody keeping a photo off a thread while
   standing on the shop floor. */
.chat-save {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    color: var(--brand-muted);
    font-size: 0.7rem;
    line-height: 1;
    text-decoration: none;
    padding: 0.2rem 0.15rem;
    margin: -0.2rem 0;
}

.chat-save svg {
    width: 0.78rem;
    height: 0.78rem;
}

.chat-save:hover {
    color: var(--brand-gold-dark);
    text-decoration: underline;
}

/* Composer */

.chat-composer {
    display: flex;
    align-items: flex-end;
    gap: 0.4rem;
    position: relative; /* anchors the slide-to-lock pill */
}

/* The mic is dragged, so the page must not scroll under the finger. */
#chat-record { touch-action: none; }

.chat-input {
    flex: 1;
    resize: none;
    max-height: 8rem;
    padding: 0.55rem 0.85rem;
}

.chat-icon-btn,
.chat-send-btn {
    flex-shrink: 0;
    width: 42px;
    height: 42px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--brand-gold-light);
    background-color: #fff;
    color: var(--brand-charcoal);
    -webkit-tap-highlight-color: transparent;
}

.chat-icon-btn svg,
.chat-send-btn svg {
    width: 18px;
    height: 18px;
}

.chat-send-btn {
    border-color: var(--brand-gold);
    background-color: var(--brand-gold);
    color: #fff;
}

.chat-send-btn:disabled,
.chat-icon-btn:disabled {
    opacity: 0.5;
}

/* Recording state: the mic turns into a live stop button. */
.chat-icon-btn.recording {
    background-color: var(--brand-rush);
    border-color: var(--brand-rush);
    color: #fff;
    animation: chat-pulse 1.2s ease-in-out infinite;
}

@keyframes chat-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(168, 70, 47, 0.5); }
    50% { box-shadow: 0 0 0 6px rgba(168, 70, 47, 0); }
}

.chat-pending {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.5rem;
    padding: 0.4rem 0.7rem;
    border: 1px dashed var(--brand-gold);
    border-radius: var(--radius-md);
    background-color: var(--brand-cream-soft);
    font-size: 0.82rem;
}

/* A video on a phone takes long enough that a composer sitting still reads as
   a broken one. The transfer goes straight to the bucket, so its progress is
   the only thing that can say the send is alive. */
.chat-upload {
    margin-top: 0.5rem;
    padding: 0.5rem 0.7rem;
    border: 1px solid var(--brand-gold-light);
    border-radius: var(--radius-md);
    background-color: var(--brand-cream-soft);
    font-size: 0.82rem;
}

.chat-upload-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 0.4rem;
}

.chat-upload-head span:first-child {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.chat-upload-pct {
    flex: none;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    color: var(--brand-gold-on-white-text);
}

.chat-upload-track {
    height: 5px;
    border-radius: var(--radius-pill);
    background-color: var(--brand-gold-pale);
    overflow: hidden;
}

.chat-upload-fill {
    width: 0;
    height: 100%;
    border-radius: var(--radius-pill);
    background-color: var(--brand-gold);
    transition: width 0.15s linear;
}

[data-theme="dark"] .chat-upload {
    background-color: var(--surface);
    border-color: var(--line);
}

.chat-pending-name {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
    .chat-icon-btn.recording { animation: none; }
}

/* @mention autocomplete above the chat box. */
.mention-box {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
}

.mention-box .chat-input { width: 100%; }

.mention-suggest {
    position: absolute;
    bottom: calc(100% + 6px);
    left: 0;
    min-width: 220px;
    max-width: 100%;
    background: #fff;
    border-radius: var(--radius-md);
    box-shadow: 0 10px 28px rgba(33, 33, 33, 0.16);
    border: 1px solid rgba(184, 158, 106, 0.35);
    overflow: hidden;
    z-index: 30;
}

.mention-item {
    display: block;
    width: 100%;
    text-align: left;
    padding: 0.5rem 0.85rem;
    border: 0;
    background: none;
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--brand-charcoal);
}

.mention-item.active,
.mention-item:hover {
    background-color: var(--brand-gold);
    color: #fff;
}

/* @mentions inside chat messages. */
.chat-mention {
    color: var(--brand-gold-dark);
    font-weight: 700;
}

/* A mention of YOU: unmissable. */
.chat-mention-me {
    background-color: var(--brand-gold-pale);
    color: var(--brand-charcoal);
    border-radius: 5px;
    padding: 0 4px;
}

/* The "Viewing as" picker, on the header bar between the brand and the
   icons. Hidden until "View as another user" asks for it; shown for good,
   in gold, once somebody is actually being viewed as. The bar is cream
   inside a solution and charcoal on the front door and the phone, so the
   pill has a light form and a dark one. */
.viewing-as {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
}

.viewing-as.is-hidden { display: none; }

.viewing-as-label {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--brand-muted);
    flex-shrink: 0;
}

.viewing-as-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    max-width: 15rem;
    padding: 0.15rem 0.6rem 0.15rem 0.2rem;
    border: 1px solid var(--line-strong, rgba(0, 0, 0, 0.18));
    border-radius: var(--radius-pill);
    background-color: var(--surface, #fff);
    color: var(--brand-text);
    font-size: 0.85rem;
    font-weight: 600;
}

.viewing-as-pill:hover { border-color: var(--brand-gold); }

.viewing-as-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.viewing-as-avatar {
    flex-shrink: 0;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background-color: var(--brand-gold-light);
    color: var(--brand-charcoal);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.72rem;
    font-weight: 700;
}

.viewing-as-me,
.viewing-as-username {
    color: var(--brand-muted);
    font-weight: 500;
    margin-left: 0.3rem;
}

/* On a dark bar: the front door, and the phone's header. */
body.is-shell .viewing-as-label,
.mobile-topbar .viewing-as-label { color: rgba(239, 233, 221, 0.6); }

body.is-shell .viewing-as-pill,
.mobile-topbar .viewing-as-pill {
    border-color: rgba(255, 255, 255, 0.28);
    background-color: rgba(255, 255, 255, 0.08);
    color: #efe9dd;
}

body.is-shell .viewing-as-pill .viewing-as-me,
.mobile-topbar .viewing-as-pill .viewing-as-me { color: rgba(239, 233, 221, 0.6); }

/* Being somebody else is the one loud thing allowed on the bar. */
.viewing-as.is-acting .viewing-as-pill,
body.is-shell .viewing-as.is-acting .viewing-as-pill,
.mobile-topbar .viewing-as.is-acting .viewing-as-pill {
    background-color: var(--brand-gold);
    border-color: var(--brand-gold);
    color: #fff;
}

.viewing-as-exit {
    flex-shrink: 0;
    width: 24px;
    height: 24px;
    line-height: 1;
    border: 1px solid rgba(255, 255, 255, 0.6);
    background: rgba(255, 255, 255, 0.15);
    color: #fff;
    border-radius: 50%;
    font-size: 1rem;
    font-weight: 600;
}

.viewing-as-exit:hover { background: rgba(255, 255, 255, 0.3); }

/* On the cream bar the same button needs the opposite contrast. */
body:not(.is-shell) .shell-bar .viewing-as-exit {
    border-color: var(--brand-gold);
    background: var(--brand-gold);
    color: #fff;
}

/* The list. One row per person, the one you are on marked, so the menu
   answers "who am I?" as well as "who could I be?". */
.viewing-as-menu {
    max-height: 60vh;
    overflow-y: auto;
    min-width: 15rem;
}

.viewing-as-menu .dropdown-item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.viewing-as-menu .dropdown-item.is-current {
    background-color: var(--brand-cream-soft);
    font-weight: 700;
}

.shell-avatar-btn {
    border: none;
    background: transparent;
    padding: 0;
    line-height: 0;
}

@media (max-width: 991px) {
    /* The phone bar is narrow: the pill carries the name, so the words in
       front of it are the first thing to go, and the bar stays on one line
       rather than wrapping the picker underneath the brand. */
    .viewing-as-label { display: none; }
    .viewing-as-pill { max-width: 46vw; }

    .mobile-topbar .container-fluid {
        flex-wrap: nowrap;
        gap: 0.5rem;
    }

    .mobile-topbar .navbar-brand { min-width: 0; }

    /* Room for the picker comes out of the solution's name — you can see
       which solution you are in from the page; you cannot see whose view
       you are on from anywhere but here. */
    .mobile-topbar.has-viewing-as .navbar-brand-text { display: none; }
}

/* Flyout menu for the sidebar's Catalog icon. */
.sidebar-flyout {
    background-color: var(--brand-charcoal);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: var(--radius-md);
    padding: 0.35rem;
    min-width: 11rem;
}

.sidebar-flyout .dropdown-header {
    color: rgba(255, 255, 255, 0.45);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    padding: 0.35rem 0.75rem 0.25rem;
}

.sidebar-flyout .dropdown-item {
    color: rgba(255, 255, 255, 0.8);
    border-radius: var(--radius-sm);
    padding: 0.4rem 0.75rem;
    font-size: 0.92rem;
}

.sidebar-flyout .dropdown-item:hover {
    background-color: rgba(255, 255, 255, 0.08);
    color: #fff;
}

.sidebar-flyout .dropdown-item.active {
    background-color: var(--brand-gold-light);
    color: var(--brand-charcoal);
    font-weight: 600;
}

/* Per-user permission editor on Team Access. */
.caps-row td { padding-top: 0; }

.caps-switch { margin: 0 0 0.25rem 0.1rem; }

.caps-badge {
    display: inline-block;
    background-color: var(--brand-gold-pale);
    color: var(--brand-gold-dark);
    border-radius: var(--radius-pill);
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    padding: 0.05rem 0.5rem;
    margin-left: 0.3rem;
}

.caps-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
    gap: 0.35rem 1.25rem;
    background-color: var(--brand-cream);
    border-radius: var(--radius-md);
    padding: 0.75rem 0.9rem;
    margin-bottom: 0.5rem;
}

.caps-item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.88rem;
    margin: 0;
    cursor: pointer;
}

/* Editable role defaults on Team Access. */
.role-block + .role-block { border-top: 1px solid rgba(33, 33, 33, 0.08); }

.role-head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    width: 100%;
    text-align: left;
    background: none;
    border: 0;
    padding: 0.55rem 0.2rem;
}

.role-head .role-desc {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.role-head .role-chev {
    width: 16px;
    height: 16px;
    color: var(--brand-muted);
    flex-shrink: 0;
    transition: transform var(--transition-fast);
}

.role-head[aria-expanded="true"] .role-chev { transform: rotate(180deg); }

/* Keyboard users land on ten sidebar icons before any page content; this
   jumps them straight there. Hidden until focused. */
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 3000;
    background: var(--brand-charcoal);
    color: #fff;
    padding: 0.7rem 1.1rem;
    border-radius: 0 0 var(--radius-md) 0;
    font-weight: 600;
    text-decoration: none;
}

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

/* ------------------------------------------------------------------
   Catalog import
   ------------------------------------------------------------------
   Upload a spreadsheet, see what it would do, then apply it. The
   preview table is the whole point, so its verdicts are colour-coded
   down the left edge and readable at a glance on a long sheet.
   ------------------------------------------------------------------ */

.import-kinds {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

.import-kind {
    padding: 0.3rem 0.75rem;
    border: 1px solid #e3ddd1;
    border-radius: 999px;
    background-color: #fff;
    color: #3a342c;
    font-size: 0.85rem;
    font-weight: 600;
    text-decoration: none;
}

.import-kind:hover {
    border-color: var(--brand-gold);
}

.import-kind.is-active {
    border-color: var(--brand-gold);
    background-color: var(--brand-gold-pale);
    color: #6b5228;
}

.import-counts {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.import-count {
    padding: 0.25rem 0.7rem;
    border-radius: 999px;
    font-size: 0.85rem;
    font-weight: 700;
    border: 1px solid transparent;
}

.import-count-new { background-color: #e8f3ec; border-color: #bcdcc8; color: #1f6b3a; }
.import-count-update { background-color: var(--brand-gold-pale); border-color: #e2d8bf; color: #6b5228; }
.import-count-same { background-color: #f2f0ec; border-color: #e3ddd1; color: #6f6a62; }
.import-count-error { background-color: #fdeceb; border-color: #f0c2bd; color: #9a2b1f; }

.import-badge {
    display: inline-block;
    margin-right: 0.4rem;
    padding: 0.05rem 0.45rem;
    border-radius: 999px;
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.import-badge-new { background-color: #e8f3ec; color: #1f6b3a; }
.import-badge-update { background-color: var(--brand-gold-pale); color: #6b5228; }
.import-badge-same { background-color: #f2f0ec; color: #6f6a62; }
.import-badge-error { background-color: #fdeceb; color: #9a2b1f; }

/* A stripe on the row, so scanning a long sheet does not mean reading
   every badge. */
.import-table tbody tr { box-shadow: inset 3px 0 0 transparent; }
.import-row-new { box-shadow: inset 3px 0 0 #4c9a68 !important; }
.import-row-update { box-shadow: inset 3px 0 0 var(--brand-gold) !important; }
.import-row-error { box-shadow: inset 3px 0 0 #d3402f !important; }
.import-row-same td { color: var(--brand-muted); }

/* On a phone the preview table was 672px wide inside a 390px screen, which put
   "What happens" — the whole reason the preview exists — off the right edge.
   Each row becomes a card instead: the verdict first, then the values with
   their headings beside them. Same markup, no sideways scrolling. */
@media (max-width: 767.98px) {
    .import-table thead {
        display: none;
    }

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

    .import-table tr {
        margin-bottom: 0.6rem;
        padding: 0.6rem 0.75rem;
        border: 1px solid #ece6d8;
        border-radius: var(--radius-md);
        background-color: #fff;
    }

    .import-table td {
        display: flex;
        justify-content: space-between;
        align-items: baseline;
        gap: 1rem;
        padding: 0.15rem 0;
        border: 0;
        text-align: right;
    }

    .import-table td::before {
        content: attr(data-label);
        flex: 0 0 auto;
        color: var(--brand-muted);
        font-size: 0.75rem;
        font-weight: 700;
        letter-spacing: 0.04em;
        text-transform: uppercase;
        text-align: left;
    }

    /* The verdict leads, because it is what the row is being read for. */
    .import-table tr { display: flex; flex-direction: column; }
    .import-table .import-verdict {
        order: -1;
        flex-direction: column;
        align-items: flex-start;
        gap: 0.15rem;
        margin-bottom: 0.35rem;
        text-align: left;
    }
    .import-table .import-verdict::before { content: none; }

    .import-table .import-line { order: -2; opacity: 0.7; }
    .import-table .import-line::before { content: "Line"; }

    /* The column reference on the upload step is a two-column list, not a
       table's worth of vertical padding. */
    .import-columns td,
    .import-columns th {
        padding-top: 0.3rem;
        padding-bottom: 0.3rem;
    }
}

/* "Marked up" beside the style name on an order, so it is clear the picture
   below is the annotated one rather than the catalog diagram. */
.badge-markup {
    display: inline-block;
    margin-left: 0.35rem;
    padding: 0.05rem 0.45rem;
    border: 1px solid rgba(171, 140, 82, 0.4);
    border-radius: 999px;
    background-color: var(--brand-gold-pale);
    color: #6b5228;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

/* Batch print page — the toolbar sits above a viewer that takes the rest of
   the window, because the sheets are what somebody came here to look at. */
.printer-page {
    margin: 0;
    height: 100vh;
    display: flex;
    flex-direction: column;
    background: #1c1c1c;
}

.printer-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.7rem 1rem;
    background: #212121;
    color: #f5f3ef;
    border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}

.printer-bar-text {
    display: flex;
    flex-direction: column;
    line-height: 1.3;
}

.printer-bar-text span {
    font-size: 0.82rem;
    color: rgba(245, 243, 239, 0.7);
}

.printer-bar-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.printer-bar-actions .btn-outline-secondary {
    /* The app's outline button is white-on-white against this dark bar. */
    background: transparent;
    color: #f5f3ef;
    border-color: rgba(255, 255, 255, 0.35);
}

.printer-bar-actions .btn-outline-secondary:hover {
    background: rgba(255, 255, 255, 0.12);
    border-color: rgba(255, 255, 255, 0.55);
    color: #fff;
}

.printer-frame {
    flex: 1 1 auto;
    width: 100%;
    border: 0;
    min-height: 0;
}

/* Print queue — a list of sheets owed to the floor, which empties as they
   come off the printer. */
.print-queue-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin: 1rem 0 1.25rem;
    padding: 0.75rem 1rem;
    background: var(--surface, #fff);
    border: 1px solid rgba(0, 0, 0, 0.08);
    border-radius: 14px;
}

.print-queue-all {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0;
    font-size: 0.9rem;
    font-weight: 600;
    cursor: pointer;
}

.print-queue-all input {
    width: 1.05rem;
    height: 1.05rem;
}

#queue-print {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}

#queue-print svg {
    width: 1.05rem;
    height: 1.05rem;
}

.print-queue {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin: 0;
}

.print-row {
    display: grid;
    grid-template-columns: auto 56px 1fr auto;
    align-items: center;
    gap: 0.85rem;
    padding: 0.7rem 0.9rem;
    background: var(--surface, #fff);
    border: 1px solid rgba(0, 0, 0, 0.08);
    border-radius: 12px;
}

/* A rush sheet is the one that must not be missed in a stack of thirty. */
.print-row-rush {
    border-left: 3px solid var(--rush, #b3261e);
}

.print-row-pick {
    margin: 0;
    display: flex;
    cursor: pointer;
}

.print-row-pick input {
    width: 1.05rem;
    height: 1.05rem;
}

.print-row-thumb {
    width: 56px;
    height: 56px;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border-radius: 8px;
    background: rgba(0, 0, 0, 0.03);
}

.print-row-thumb img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

.print-row-thumb-empty {
    color: rgba(0, 0, 0, 0.25);
}

.print-row-main {
    min-width: 0;
}

.print-row-number {
    font-weight: 700;
    text-decoration: none;
}

.print-row-sub {
    font-size: 0.85rem;
    color: rgba(0, 0, 0, 0.55);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.print-row-meta {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    white-space: nowrap;
}

.print-rush-chip {
    padding: 0.15rem 0.5rem;
    border-radius: 999px;
    background: #fdeceb;
    border: 1px solid #f0c2bd;
    color: #9a2b1f;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
}

.print-row-due {
    font-size: 0.82rem;
    font-weight: 600;
}

.print-row-due-none {
    font-weight: 400;
    color: rgba(0, 0, 0, 0.4);
}

.print-empty {
    text-align: center;
    padding: 3.5rem 1rem;
    border: 1px dashed rgba(0, 0, 0, 0.15);
    border-radius: 16px;
}

.print-empty svg {
    width: 44px;
    height: 44px;
    color: rgba(0, 0, 0, 0.25);
    margin-bottom: 0.9rem;
}

.print-done {
    margin-top: 1.75rem;
}

.print-done summary {
    cursor: pointer;
    font-size: 0.9rem;
    font-weight: 600;
    color: rgba(0, 0, 0, 0.6);
}

.print-done-row {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    padding: 0.4rem 0;
    font-size: 0.87rem;
    border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}

.print-done-who {
    color: rgba(0, 0, 0, 0.55);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.print-done-when {
    margin-left: auto;
    color: rgba(0, 0, 0, 0.45);
    white-space: nowrap;
}

@media (max-width: 575.98px) {
    .print-row {
        grid-template-columns: auto 44px 1fr;
        row-gap: 0.5rem;
    }

    .print-row-thumb {
        width: 44px;
        height: 44px;
    }

    /* The chips drop under the order rather than squeezing it to nothing. */
    .print-row-meta {
        grid-column: 2 / -1;
    }

    .print-queue-bar {
        position: sticky;
        top: 0.5rem;
        z-index: 3;
    }

    #queue-print {
        flex: 1 1 auto;
        justify-content: center;
    }
}

/* ------------------------------------------------------------------
   The Mefoar bar
   The operating system's own strip, above whichever solution you are
   in. It carries the way back out, who you are, the dark/light switch
   and the way out — the things that are the business's rather than one
   solution's, and so must exist once rather than once per rail.

   Quiet on purpose: a black band across the top fought the front door
   below it, and inside a solution it is chrome, not the subject.
   ------------------------------------------------------------------ */

/* Measured, not guessed: the rail is pinned to this, and a value that
   disagrees with the bar leaves a seam at the top of the rail. */
:root { --os-bar-h: 59px; }

.shell-bar {
    position: sticky;
    top: 0;
    z-index: 1040;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.75rem 2rem;
    background: var(--brand-cream);
    border-bottom: 1px solid rgba(0, 0, 0, 0.06);
    color: var(--brand-text);
}

.shell-brand {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.92rem;
    letter-spacing: 0.01em;
    color: inherit;
    text-decoration: none;
}

.shell-brand img {
    width: 22px;
    height: 22px;
}

.shell-brand-name { font-weight: 700; }
.shell-brand-sep { color: var(--brand-muted); }
.shell-brand-where { color: var(--brand-muted); }
.shell-brand:hover .shell-brand-where { color: var(--brand-text); }

.shell-account {
    display: inline-flex;
    align-items: center;
    gap: 0.7rem;
}

/* Chrome, not a portrait: the rail's 40px plate made the bar tall enough
   to notice. */
.shell-account .sidebar-avatar {
    width: 30px;
    height: 30px;
    font-size: 0.78rem;
}

.shell-user {
    font-size: 0.88rem;
    color: var(--brand-muted);
}

.shell-logout {
    padding: 0.25rem 0.7rem;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    background: transparent;
    color: var(--brand-text);
    font-size: 0.8rem;
    font-weight: 600;
}

.shell-logout:hover {
    background: rgba(128, 128, 128, 0.14);
}

/* The rail starts under the bar: the bar spans the window, because what
   it holds is above every solution, not beside one. */
.sidebar { top: var(--os-bar-h); }


/* ==================================================================
   The front door
   ------------------------------------------------------------------
   The shell the three layouts sit in: a dark room with the gold lit,
   and a light mode that is the same room with the lights on rather
   than a different design. The layouts themselves live in
   home-layouts.css, which only this page loads.
   ================================================================== */

/* The bar above the page belongs to the page, or the front door opens
   with a stripe of the wrong colour across the top. */
body.is-shell {
    background: #14140f;
}

/* A page outside the solutions has no icon rail, but the layout still
   reserved the 76px gutter the rail sits in everywhere else — empty
   here, so it pushed the whole page right by that much. */
body.no-rail .main-wrapper {
    margin-left: 0;
    padding-bottom: 2rem;
}

/* The front door supplies its own gutter and wants the whole window;
   Settings is an ordinary centred page and keeps the container's. */
body.is-shell .container {
    max-width: none;
    padding-left: clamp(1.25rem, 3vw, 3rem);
    padding-right: clamp(1.25rem, 3vw, 3rem);
}

[data-theme="light"] body.is-shell {
    background: var(--brand-cream);
}

/* The front door carries no brand in the bar — the page below is the
   wordmark — so the account has to be told to keep to the right. */
body.is-shell .shell-bar {
    justify-content: flex-end;
    background: transparent;
    border-bottom-color: rgba(255, 255, 255, 0.08);
}

body.is-shell .shell-user { color: rgba(239, 233, 221, 0.6); }

body.is-shell .shell-logout {
    color: #efe9dd;
    border-color: rgba(255, 255, 255, 0.28);
}

body.is-shell .shell-logout:hover {
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
}

[data-theme="light"] body.is-shell .shell-bar { border-bottom-color: rgba(0, 0, 0, 0.06); }
[data-theme="light"] body.is-shell .shell-user { color: rgba(0, 0, 0, 0.55); }

[data-theme="light"] body.is-shell .shell-logout {
    color: rgba(0, 0, 0, 0.7);
    border-color: rgba(0, 0, 0, 0.18);
}

[data-theme="light"] body.is-shell .shell-logout:hover {
    background: rgba(0, 0, 0, 0.05);
    color: #000;
}

/* --- the toggle ---------------------------------------------------- */

/* Tokens, not literals: the switch now rides the bar on every page, and
   a cream glyph that only suited the dark front door was invisible on a
   cream one. The front door's own default is set below. */
.theme-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-pill);
    background: transparent;
    color: var(--brand-text);
}

.theme-toggle:hover { background: rgba(128, 128, 128, 0.16); }

/* The front door opens dark whether or not a theme was chosen. */
body.is-shell .theme-toggle {
    border-color: rgba(255, 255, 255, 0.22);
    color: #efe9dd;
}

body.is-shell .theme-toggle:hover { background: rgba(255, 255, 255, 0.12); }

.theme-icon { width: 17px; height: 17px; }

/* One button, two glyphs: it shows what you will get, not what you have. */
.theme-icon-sun { display: block; }
.theme-icon-moon { display: none; }

[data-theme="light"] .theme-icon-sun { display: none; }
[data-theme="light"] .theme-icon-moon { display: block; }

[data-theme="light"] .theme-toggle {
    border-color: rgba(0, 0, 0, 0.18);
    color: rgba(0, 0, 0, 0.7);
}

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

/* The phone's top bar is dark in both modes, so its copy of the switch
   stays cream whatever the tokens say. Last, so it wins in either. */
.mobile-topbar .theme-toggle {
    border-color: rgba(255, 255, 255, 0.22);
    color: #efe9dd;
}

.mobile-topbar .theme-toggle:hover { background: rgba(255, 255, 255, 0.12); }

.mobile-actions {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

/* ==================================================================
   Dark mode, app-wide
   ------------------------------------------------------------------
   Added as a layer rather than by rewriting the four thousand lines
   above it: every rule here is guarded by [data-theme="dark"], so
   light mode cannot regress — nothing that already existed changed.

   Most of it is free. The app already paints from --brand-* tokens,
   so redefining those flips the bulk of it; what follows is the parts
   that were written as literal colours, plus Bootstrap's own defaults,
   which assume white.

   No choice means light, except the front door, which was chosen dark.
   ================================================================== */

[data-theme="dark"] {
    --brand-cream: #14140f;
    --brand-cream-soft: #1e1e17;
    --brand-text: #efe9dd;
    --brand-muted: #a8a296;
    --brand-charcoal: #0f0f0b;
    --brand-charcoal-soft: #1a1a13;
    /* Gold has to carry text on a dark ground now, so it lightens. */
    --brand-gold-on-white-text: #e5c07f;
    --brand-gold-dark: #e5c07f;
    --brand-gold-pale: #3a3020;
    --brand-rush: #e08a6d;

    --surface: #1c1c15;
    --surface-raised: #23231a;
    --line: rgba(255, 255, 255, 0.1);
    --line-strong: rgba(255, 255, 255, 0.18);

    /* Shadows do nothing on a dark ground; depth comes from the edge. */
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.45);
    --shadow-lg: 0 12px 28px rgba(0, 0, 0, 0.55);

    color-scheme: dark;
}

[data-theme="dark"] body {
    background-color: var(--brand-cream);
    color: var(--brand-text);
}

/* Hovering a link darkened it to --brand-charcoal, which in light mode is
   near-black on cream and reads fine. Dark mode redefines charcoal to
   #0f0f0b and the page behind it to #14140f, so the rule was painting the
   link almost exactly its own background: every link in the app vanished
   under the cursor, at about 1.04:1. Hover now lightens instead, which is
   the same intent — "this one is under the pointer" — pointed the way the
   background went. */
[data-theme="dark"] a:hover {
    color: var(--brand-text);
}

/* --- surfaces ------------------------------------------------------ */

[data-theme="dark"] .card,
[data-theme="dark"] .modal-content,
[data-theme="dark"] .panel,
[data-theme="dark"] .cutting-spec-box,
[data-theme="dark"] .form-section,
[data-theme="dark"] .rail-card,
[data-theme="dark"] .row-remove,
[data-theme="dark"] .caps-grid,
[data-theme="dark"] .import-kind,
[data-theme="dark"] .customer-results,
[data-theme="dark"] .mention-suggest,
[data-theme="dark"] .print-row,
[data-theme="dark"] .color-row,
[data-theme="dark"] .chat-row,
[data-theme="dark"] .gallery-grid a,
[data-theme="dark"] .dashboard-empty,
[data-theme="dark"] .page-header-sticky,
[data-theme="dark"] .print-queue-bar {
    background-color: var(--surface);
    border-color: var(--line);
    color: var(--brand-text);
}

[data-theme="dark"] .import-table tr:nth-child(even),
[data-theme="dark"] .chat-row.is-unread {
    background-color: var(--surface-raised);
}

/* The rows sit on a section that is already --surface, so they lift rather
   than match it — the same separation the cream/white pair gives in light. */
[data-theme="dark"] .color-row,
[data-theme="dark"] .front-fabric-row,
[data-theme="dark"] .fabric-picker {
    background-color: var(--surface-raised);
    border-color: var(--line);
}

[data-theme="dark"] .form-subhead {
    border-top-color: var(--line);
}

/* The surfaces rule above sets every .form-section to --line; the accent has
   to win it back, or Linked orders loses the edge that makes it stand out. */
[data-theme="dark"] .form-section-accent {
    border-color: var(--brand-gold);
}

[data-theme="dark"] .btn-add-row {
    border-color: var(--line);
}

[data-theme="dark"] .order-form-bar {
    background-color: rgba(20, 20, 15, 0.92);
    border-color: var(--line);
}

[data-theme="dark"] .chat-row:hover,
[data-theme="dark"] .customer-result-item:hover,
[data-theme="dark"] .customer-result-item:focus,
[data-theme="dark"] .mention-item.active,
[data-theme="dark"] .mention-item:hover,
[data-theme="dark"] .table-hover tbody tr:hover,
[data-theme="dark"] a.card-header:hover,
[data-theme="dark"] .sidebar-flyout .dropdown-item:hover {
    background-color: rgba(255, 255, 255, 0.07);
    color: var(--brand-text);
}

/* --- type ---------------------------------------------------------- */

[data-theme="dark"] .text-muted,
[data-theme="dark"] .form-text,
[data-theme="dark"] .form-label {
    color: var(--brand-muted) !important;
}

[data-theme="dark"] .text-danger { color: #f0907a !important; }

[data-theme="dark"] hr,
[data-theme="dark"] .dropdown-divider {
    border-color: var(--line);
    opacity: 1;
}

/* --- forms --------------------------------------------------------- */

[data-theme="dark"] .form-control,
[data-theme="dark"] .form-select,
[data-theme="dark"] .input-group-text,
[data-theme="dark"] textarea {
    background-color: #17170f;
    border-color: var(--line-strong);
    color: var(--brand-text);
}

[data-theme="dark"] .form-control:focus,
[data-theme="dark"] .form-select:focus {
    background-color: #17170f;
    border-color: var(--brand-gold);
    color: var(--brand-text);
    box-shadow: 0 0 0 3px rgba(229, 192, 127, 0.18);
}

[data-theme="dark"] .form-control::placeholder { color: rgba(239, 233, 221, 0.35); }

[data-theme="dark"] .form-check-input {
    background-color: #17170f;
    border-color: var(--line-strong);
}

[data-theme="dark"] .form-check-input:checked {
    background-color: var(--brand-gold);
    border-color: var(--brand-gold);
}

/* --- buttons ------------------------------------------------------- */

[data-theme="dark"] .btn-outline-secondary,
[data-theme="dark"] .btn-outline-primary,
[data-theme="dark"] .order-nav-btn,
[data-theme="dark"] .chat-icon-btn {
    background-color: transparent;
    border-color: var(--line-strong);
    color: var(--brand-text);
}

[data-theme="dark"] .btn-outline-secondary:hover,
[data-theme="dark"] .btn-outline-primary:hover,
[data-theme="dark"] .order-nav-btn:not(.is-disabled):hover,
[data-theme="dark"] .chat-icon-btn:hover {
    background-color: rgba(255, 255, 255, 0.12);
    border-color: var(--line-strong);
    color: #fff;
}

[data-theme="dark"] .btn-outline-danger { color: #f0907a; border-color: rgba(240, 144, 122, 0.5); }

/* --- tables, lists, menus ------------------------------------------ */

[data-theme="dark"] .table {
    --bs-table-bg: transparent;
    --bs-table-color: var(--brand-text);
    --bs-table-border-color: var(--line);
    color: var(--brand-text);
}

[data-theme="dark"] .dropdown-menu {
    background-color: var(--surface-raised);
    border-color: var(--line);
}

[data-theme="dark"] .dropdown-item { color: var(--brand-text); }
[data-theme="dark"] .dropdown-item:hover,
[data-theme="dark"] .dropdown-item:focus {
    background-color: rgba(255, 255, 255, 0.08);
    color: #fff;
}

[data-theme="dark"] .list-group-item {
    background-color: var(--surface);
    border-color: var(--line);
    color: var(--brand-text);
}

[data-theme="dark"] .alert {
    background-color: var(--surface-raised);
    border-color: var(--line);
    color: var(--brand-text);
}

[data-theme="dark"] .badge.bg-secondary { background-color: #3a3a2c !important; }

/* --- the board ----------------------------------------------------- */

[data-theme="dark"] .stage-card,
[data-theme="dark"] .stage-card .card-body {
    background-color: var(--surface);
    color: var(--brand-text);
}

[data-theme="dark"] .stage-dropzone { background-color: rgba(255, 255, 255, 0.02); }

[data-theme="dark"] .stage-chip {
    background-color: var(--surface);
    border-color: var(--line);
    color: var(--brand-text);
}

[data-theme="dark"] .stage-chip.is-active {
    background-color: var(--brand-gold);
    color: #1a1a13;
}

[data-theme="dark"] .stage-chip.is-active .stage-chip-count {
    background-color: rgba(0, 0, 0, 0.25);
    color: #1a1a13;
}

/* --- chat ---------------------------------------------------------- */

[data-theme="dark"] .chat-bubble {
    background-color: var(--surface-raised);
    color: var(--brand-text);
}

[data-theme="dark"] .chat-pending { background-color: var(--surface-raised); }

/* --- the mark-up editor -------------------------------------------- */

[data-theme="dark"] .markup-bar,
[data-theme="dark"] .markup-foot {
    background-color: #17170f;
    border-color: var(--line);
    color: var(--brand-text);
}

[data-theme="dark"] .markup-tool {
    background-color: var(--surface-raised);
    border-color: var(--line-strong);
    color: var(--brand-text);
}

/* The canvas keeps its white ground: it is a sheet of paper being drawn
   on, and the mark-up is printed on white later. */

/* --- glass ---------------------------------------------------------- */

[data-theme="dark"] .glass-card {
    background-color: rgba(255, 255, 255, 0.05);
    border-color: var(--line);
}

[data-theme="dark"] [data-tooltip]::after {
    background-color: #2a2a20;
    color: var(--brand-text);
}

/* --- what stays light ---------------------------------------------- */

/* Paper is white whatever the screen is. Printing a dark page wastes
   toner and comes out unreadable, so every sheet prints light. */
@media print {
    [data-theme="dark"] body,
    [data-theme="dark"] .card,
    [data-theme="dark"] .panel {
        background: #fff !important;
        color: #000 !important;
    }
}

/* The rail's toggle is a rail icon, not the pill the shell bar uses. */
.sidebar-icon.theme-toggle {
    width: auto;
    height: auto;
    border: 0;
    border-radius: var(--radius-md);
    background: transparent;
}

.sidebar-icon.theme-toggle .theme-icon {
    width: 20px;
    height: 20px;
}

/* --- the board, continued ------------------------------------------
   Three things on the board are painted in translucent white, which is
   invisible on cream and a grey slab on charcoal. */

[data-theme="dark"] .stage-column {
    background-color: rgba(255, 255, 255, 0.035);
}

[data-theme="dark"] .stage-empty {
    background-color: rgba(255, 255, 255, 0.02);
    border-color: rgba(229, 192, 127, 0.22);
}

/* The glass tile is a stack of white inset highlights — the thing that
   makes it read as lit glass on cream makes it read as a smear on
   charcoal. Dark gets the same idea built the other way up: a lighter
   pane, one hairline, and no white bloom. */
[data-theme="dark"] .glass-card {
    border-color: rgba(255, 255, 255, 0.12);
    background: rgba(255, 255, 255, 0.055);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.07);
}

/* The displacement samples whatever is behind it; behind it is near
   black, so it returns mud. */
[data-theme="dark"] .glass-card > .glass-refraction {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
}

[data-theme="dark"] .glass-card::after {
    background: linear-gradient(
        100deg,
        transparent 20%,
        rgba(255, 255, 255, 0.14) 50%,
        transparent 80%
    );
}

[data-theme="dark"] .stat-tile {
    border-color: rgba(229, 192, 127, 0.28);
}

[data-theme="dark"] .stat-icon {
    background-color: rgba(255, 255, 255, 0.08);
    color: var(--brand-gold);
}

/* The tile's big number is charcoal for a white pane. On a dark pane that
   is the pane's own colour, so the figure disappears. */
[data-theme="dark"] .glass-card .stat-value {
    color: var(--brand-text);
    text-shadow: none;
}

[data-theme="dark"] .glass-card .stat-icon {
    background-color: rgba(255, 255, 255, 0.09);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}

/* Cards sit above their lane, so in dark the card is the lighter of the
   two — the reverse of light mode, where the lane is the tint and the card
   is white. Getting this backwards makes every card look sunken. */
[data-theme="dark"] .stage-column {
    background-color: rgba(255, 255, 255, 0.02);
}

[data-theme="dark"] .stage-card,
[data-theme="dark"] .stage-card .card-body {
    background-color: var(--surface-raised);
}

/* Two literals from the light design that survived onto a dark card and
   turned the two lines you actually read into black on black: the order
   number was painted charcoal (1.12:1 on the card) and the customer name
   was Bootstrap's own body colour (1.11:1). Both take the page's ink in
   dark, which measures 14.2:1.

   The order number is the first thing anyone looks for on this list — it
   is what the paper sheet, the barcode and the customer all say. */
[data-theme="dark"] .list-card .fw-semibold,
[data-theme="dark"] .text-body {
    color: var(--brand-text) !important;
}

/* The catalogs drop backdrop-filter for scrolling speed and paint a near
   white instead, which the dark surface rules above do not reach. */
[data-theme="dark"] .style-grid .card,
[data-theme="dark"] .style-catalog-card,
[data-theme="dark"] .spec-card,
[data-theme="dark"] .list-card {
    background: var(--surface);
    border-color: var(--line);
    color: var(--brand-text);
}

/* Every .card carries the glass look's stack of white inset highlights.
   On cream that is lit glass; on charcoal it is a white halo round every
   panel on every page. Depth in dark comes from one hairline instead. */
[data-theme="dark"] .card,
[data-theme="dark"] .list-card,
[data-theme="dark"] .spec-card,
[data-theme="dark"] .stat-tile,
[data-theme="dark"] .stage-card {
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
}

[data-theme="dark"] .card:hover,
[data-theme="dark"] .stage-card:hover,
[data-theme="dark"] .list-card:hover {
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.07), var(--shadow-md);
}

[data-theme="dark"] .split-detail,
[data-theme="dark"] .split-detail-wide-only {
    background-color: var(--surface);
    border-color: var(--line);
}

[data-theme="dark"] .browse-catalog-btn {
    background-color: var(--surface-raised);
    border-color: var(--line-strong);
    color: var(--brand-text);
}

[data-theme="dark"] .browse-catalog-btn:hover {
    background-color: rgba(255, 255, 255, 0.1);
}

[data-theme="dark"] .print-rush-chip {
    background-color: rgba(224, 138, 109, 0.16);
    border-color: rgba(224, 138, 109, 0.4);
    color: #f0a68d;
}

[data-theme="dark"] .badge[class*="role-"] {
    background-color: rgba(255, 255, 255, 0.1) !important;
    color: var(--brand-text) !important;
}

/* .style-card-img keeps its white ground on purpose: the style diagrams
   are black line art, and on charcoal they would be invisible. A white
   plate under the drawing is what a drawing wants in either mode. */

/* The picker sits outside the .look wrapper, so it carries its own
   copy of the front door's ink rather than inheriting the app's. */
.layout-picker {
    --edge: rgba(255, 255, 255, 0.14);
    --ink: #efe9dd;
    --ink-soft: rgba(239, 233, 221, 0.6);
    --ink-faint: rgba(239, 233, 221, 0.4);
}

[data-theme="light"] .layout-picker {
    --edge: rgba(0, 0, 0, 0.12);
    --ink: #282c2e;
    --ink-soft: rgba(40, 44, 46, 0.62);
    --ink-faint: rgba(40, 44, 46, 0.45);
}

/* ==================================================================
   Settings
   ------------------------------------------------------------------
   Its own place, above the four solutions. These pages take no
   solution rail, so this is the furniture that stands in for one: the
   area's name, and the rest of the area beside it.
   ================================================================== */

/* Settings is an area, not a handful of pages that happen to be related,
   so it carries its own sidebar: the whole of it visible from anywhere in
   it, with the current page marked and the way out at the top. */
.settings-shell {
    display: grid;
    grid-template-columns: 210px minmax(0, 1fr);
    gap: clamp(1.25rem, 3vw, 2.75rem);
    align-items: start;
}

.settings-main { min-width: 0; }

.settings-nav {
    position: sticky;
    top: calc(var(--os-bar-h, 59px) + 1rem);
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.settings-back {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-bottom: 1.1rem;
    padding-bottom: 0.9rem;
    border-bottom: 1px solid var(--line, rgba(0, 0, 0, 0.09));
    color: var(--brand-muted);
    font-size: 0.9rem;
    font-weight: 600;
    text-decoration: none;
}

.settings-back:hover { color: var(--brand-text); }
.settings-back svg { width: 16px; height: 16px; flex-shrink: 0; }

.settings-nav-group { margin-bottom: 1.4rem; }

.settings-nav-head {
    margin: 0 0 0.5rem 0.15rem;
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--brand-text);
}

.settings-nav a {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    min-height: 40px;
    padding: 0.4rem 0.7rem;
    border-radius: 10px;
    color: var(--brand-muted);
    font-size: 0.95rem;
    text-decoration: none;
}

.settings-nav a:hover { background: rgba(128, 128, 128, 0.12); color: var(--brand-text); }

.settings-nav a.is-on {
    background: var(--brand-cream-soft);
    color: var(--brand-text);
    font-weight: 600;
}

[data-theme="dark"] .settings-nav a.is-on { background: rgba(255, 255, 255, 0.08); }

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

@media (max-width: 767.98px) {
    /* No room for a column beside the page. The list becomes one row that
       scrolls sideways, above the page it belongs to — the same links, in
       the same order, still saying which one you are on. */
    .settings-shell { grid-template-columns: 1fr; gap: 1.1rem; }

    .settings-nav {
        position: static;
        flex-direction: row;
        gap: 0.4rem;
        overflow-x: auto;
        padding-bottom: 0.5rem;
        -webkit-overflow-scrolling: touch;
    }

    .settings-nav-group {
        display: contents;
    }

    /* The group headings are the sidebar's shape, not the row's. */
    .settings-nav-head { display: none; }

    .settings-back {
        margin: 0;
        padding: 0.4rem 0.2rem 0.4rem 0;
        border-bottom: 0;
        flex-shrink: 0;
    }

    .settings-nav a {
        flex-shrink: 0;
        min-height: 44px;
        border: 1px solid var(--line, rgba(0, 0, 0, 0.09));
        white-space: nowrap;
    }
}

.settings-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem 1.5rem;
    margin-bottom: 1.5rem;
    padding-bottom: 0.9rem;
    border-bottom: 1px solid var(--line, rgba(0, 0, 0, 0.09));
}

.settings-title {
    margin: 0;
    font-size: 1.5rem;
    font-weight: 700;
}

.settings-title a { color: inherit; text-decoration: none; }
.settings-title a:hover { text-decoration: underline; }
.settings-sep { color: var(--brand-muted); font-weight: 400; }

.settings-lead {
    margin: -0.5rem 0 1.75rem;
    color: var(--brand-muted);
}

.settings-group { margin-bottom: 2.25rem; }

.settings-group-name {
    margin: 0 0 0.85rem;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--brand-muted);
}

.settings-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 1rem;
    max-width: 900px;
}

.settings-card {
    display: block;
    padding: 1.3rem 1.4rem;
    border: 1px solid var(--line, rgba(0, 0, 0, 0.09));
    border-radius: var(--radius-lg);
    background: var(--surface, #fffdf9);
    color: inherit;
    text-decoration: none;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.settings-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }

.settings-card-name { margin: 0 0 0.35rem; font-size: 1.15rem; font-weight: 700; }
.settings-card-blurb { margin: 0; color: var(--brand-muted); font-size: 0.92rem; }

.settings-empty { color: var(--brand-muted); }

/* ==================================================================
   Deleting orders
   ------------------------------------------------------------------
   The tick box sits beside its card, not in it: inside the card it
   would be inside the link, and unclickable without opening the order.
   ================================================================== */

.order-pick-row {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
}

.order-pick-row > .list-card { flex: 1 1 auto; min-width: 0; }

.order-pick {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    margin-top: 0.9rem;
    flex: 0 0 auto;
    border-radius: var(--radius-md);
    cursor: pointer;
}

.order-pick:hover { background: rgba(128, 128, 128, 0.14); }
.order-pick input { width: 17px; height: 17px; cursor: pointer; }

/* The bar only exists while something is ticked, and it sits above the
   phone's tab bar rather than under it. */
.bulk-bar {
    position: sticky;
    bottom: 1rem;
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: 0.75rem;
    padding: 0.6rem 0.9rem;
    border: 1px solid var(--line, rgba(0, 0, 0, 0.09));
    border-radius: var(--radius-pill);
    background: var(--surface, #fffdf9);
    box-shadow: var(--shadow-md);
}

.bulk-bar[hidden] { display: none; }
.bulk-count { margin-right: auto; font-size: 0.9rem; }

.bulk-count { white-space: nowrap; }

/* "Hand to", the name box and the button are one action and wrap as one.
   Split apart, the label ended up stranded on a line of its own above a
   bar that then looked empty — which happens on any narrow board, not only
   on a phone: the orders list keeps its cards in a column about a third of
   a wide screen. So this wraps by what is in it, not by the window. */
.bulk-move {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
}

.bulk-move-label {
    margin: 0;
    font-size: 0.9rem;
    color: var(--brand-muted);
    white-space: nowrap;
}

/* Sized by the longest name in it rather than fixed: a box wide enough for
   "Yechiel Rosenfeld" is a box mostly empty for "Leah", and one narrow
   enough for "Leah" cuts the other in half. Capped so one long name cannot
   push the buttons off the end of the bar. */
.bulk-move-who {
    width: auto;
    min-width: 0;
    max-width: 14rem;
}

/* Two rows deep, a pill radius reads as a blob rather than a bar, so the
   bar rounds like the cards above it instead. */
.bulk-bar {
    flex-wrap: wrap;
    border-radius: var(--radius-lg, 14px);
}

@media (max-width: 575.98px) {
    /* The count keeps the first row to itself; the action shares the second. */
    .bulk-count { flex: 1 0 100%; margin-right: 0; }
    .bulk-move { flex: 1 1 auto; }
    .bulk-move-who { flex: 1 1 6rem; }
}

.bulk-list {
    max-height: 40vh;
    margin: 0 0 0.9rem;
    padding-left: 1.1rem;
    overflow-y: auto;
    font-size: 0.9rem;
}

@media (max-width: 991.98px) {
    .bulk-bar { bottom: 5.5rem; }
}

/* ==================================================================
   The orders list: what it is narrowed to, and what is ticked
   ================================================================== */

.scope-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin: 0.75rem 0 0.25rem;
}

.scope-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.25rem 0.65rem 0.25rem 0.8rem;
    border: 1px solid var(--brand-gold);
    border-radius: var(--radius-pill);
    background: var(--brand-gold-pale);
    color: var(--brand-gold-on-white-text);
    font-size: 0.82rem;
    font-weight: 600;
    text-decoration: none;
}

.scope-chip:hover { background: var(--brand-gold); color: #fff; }
.scope-chip-x { font-size: 1.05rem; line-height: 1; opacity: 0.7; }

.list-head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin: 0.9rem 0 0.35rem;
}

.list-head-label { font-size: 0.85rem; color: var(--brand-muted); }

/* The ticked row, readable from a step back rather than by its box.
   Not gold-pale itself: that is exactly the status badge's ground, and a
   ticked card swallowed its own badge whole. Half that tint over white
   keeps the badge a badge. */
.order-pick-row.is-picked > .list-card {
    border-color: var(--brand-gold);
    background-color: #f6ecd8;
}

[data-theme="dark"] .order-pick-row.is-picked > .list-card {
    border-color: var(--brand-gold-dark);
    background-color: #26200f;
}

/* ------------------------------------------------------------------
   Ticking cards on the board, to hand them to another designer.
   ------------------------------------------------------------------ */

/* Top-right of the card, out of the way of the order number on the left
   and the due pill it shares that corner with — so the pill moves over
   rather than sitting under the box. */
.card-pick {
    position: absolute;
    top: 0.35rem;
    right: 0.35rem;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: var(--radius-md);
    cursor: pointer;
}

.card-pick:hover { background: rgba(128, 128, 128, 0.14); }
.card-pick input { width: 17px; height: 17px; cursor: pointer; }

.stage-card:has(.card-pick) .card-body { position: relative; }
.stage-card:has(.card-pick) .due-pill { margin-right: 1.9rem; }

/* Same tint the list uses for a ticked row, so one selection reads the
   same way whichever screen it was made on. */
.stage-card.is-picked {
    border-color: var(--brand-gold);
    background-color: #f6ecd8;
}

[data-theme="dark"] .stage-card.is-picked {
    border-color: var(--brand-gold-dark);
    background-color: #26200f;
}

/* The bar floats over the foot of the board on a phone. */
@media (max-width: 991.98px) {
    .board-lanes.has-picks { padding-bottom: 4.25rem; }
}

.list-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.75rem;
    padding: 2.5rem 1rem;
    color: var(--brand-muted);
    text-align: center;
}

.list-empty svg { width: 34px; height: 34px; opacity: 0.45; }
.list-empty-line { margin: 0; }

/* The bar floats over the foot of the list on a phone. Give back exactly
   the height it takes, and only while it is there. */
@media (max-width: 991.98px) {
    .order-cards.has-picks { padding-bottom: 4.25rem; }
}

/* ==================================================================
   Focus
   ------------------------------------------------------------------
   The browser's own ring is a 1px near-black hairline, which on the
   charcoal rail and the dark front door is no ring at all. One rule,
   the brand gold, everywhere a keyboard can land.
   ================================================================== */

:focus-visible {
    outline: 2px solid var(--brand-gold);
    outline-offset: 2px;
}

/* On the dark chrome the gold needs to be the lighter cut to carry. */
.sidebar :focus-visible,
.mobile-topbar :focus-visible,
.mobile-tabbar :focus-visible,
body.is-shell :focus-visible {
    outline-color: var(--brand-gold-light);
}

/* The recycle bin. Quieter than the board: these orders are not work. */
.bin-card { opacity: 0.92; }
.bin-actions { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.75rem; }
.bin-forever { color: var(--brand-rush); text-decoration: none; }
.bin-forever:hover { text-decoration: underline; }


/* ==================================================================
   Deleting from a catalog
   The bin sits beside Edit on a row, and over the corner of a style
   card — the card is a link, so nothing clickable can live inside it.
   ================================================================== */

.cat-pick-col { width: 40px; }
.cat-pick-col input { width: 17px; height: 17px; }

.cat-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.4rem;
    white-space: nowrap;
}

.cat-del {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
}

.cat-del svg { width: 15px; height: 15px; }

.style-cell { position: relative; }

.style-pick {
    position: absolute;
    top: 0.4rem;
    left: 0.4rem;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: var(--radius-md);
    background: rgba(255, 255, 255, 0.92);
    cursor: pointer;
}

.style-pick input { width: 16px; height: 16px; cursor: pointer; }

.style-del {
    position: absolute;
    top: 0.4rem;
    right: 0.4rem;
    z-index: 2;
    margin: 0;
}

/* On a card the bin is quiet until the card is under the pointer. */
.style-del .cat-del { background: rgba(255, 255, 255, 0.92); opacity: 0; }
.style-cell:hover .style-del .cat-del,
.style-del .cat-del:focus-visible { opacity: 1; }

[data-theme="dark"] .style-pick,
[data-theme="dark"] .style-del .cat-del { background: rgba(28, 28, 21, 0.92); }

/* --- Permissions, per solution ---------------------------------------- */

.permset {
    border: 1px solid var(--line, #ded8cb);
    border-radius: .8rem;
    background: var(--brand-cream-soft);
    margin: .75rem 0;
    overflow: hidden;
}
.permset-top {
    display: flex;
    align-items: baseline;
    gap: .5rem;
    flex-wrap: wrap;
    padding: .7rem .9rem;
    border-bottom: 1px solid var(--line, #ded8cb);
}
.permset-name { font-weight: 700; }
.permset-sub { font-size: .78rem; color: var(--brand-muted); }
.permset-count {
    margin-left: auto;
    font-size: .76rem;
    color: var(--brand-muted);
    font-variant-numeric: tabular-nums;
}
.permset-group {
    margin: .8rem .9rem .1rem;
    font-size: .67rem;
    letter-spacing: .09em;
    text-transform: uppercase;
    font-weight: 700;
    color: var(--brand-muted);
}
.permset.is-locked { opacity: .72; }

.perm {
    display: flex;
    align-items: flex-start;
    gap: .85rem;
    padding: .55rem .9rem;
    margin: 0;
    cursor: pointer;
    border-top: 1px solid color-mix(in srgb, var(--line, #ded8cb) 55%, transparent);
}
.permset-group + .perm { border-top: 0; }
/* Indented, so a dependency is visible as one before it is read. */
.perm.is-child { padding-left: 2.5rem; }
.perm.is-blocked { cursor: default; opacity: .5; }
.perm-b { flex: 1 1 auto; min-width: 0; }
.perm-t { display: block; font-weight: 600; font-size: .9rem; }
.perm.is-child .perm-t { font-weight: 500; }
.perm-d { display: block; font-size: .78rem; color: var(--brand-muted); }

/* The switch. The checkbox stays in the DOM and keeps working with a
   keyboard; it is the drawn track that is visible. */
.perm-input {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
}
.perm-sw {
    flex: 0 0 auto;
    width: 42px;
    height: 25px;
    margin-top: .1rem;
    border-radius: 2rem;
    background: color-mix(in srgb, var(--brand-muted) 34%, transparent);
    position: relative;
    transition: background .15s ease;
}
.perm-sw::after {
    content: "";
    position: absolute;
    top: 3px;
    left: 3px;
    width: 19px;
    height: 19px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .28);
    transition: left .15s ease;
}
.perm-input:checked ~ .perm-sw { background: var(--brand-gold); }
.perm-input:checked ~ .perm-sw::after { left: 20px; }
/* Focus has to be visible on the drawn track, since the real control is
   off-screen — a switch nobody can see themselves land on is unusable by
   keyboard however correct the markup is. */
.perm-input:focus-visible ~ .perm-sw {
    outline: 2px solid var(--brand-gold);
    outline-offset: 2px;
}
.perm-sw.is-locked::after { background: var(--brand-muted); left: 20px; }

@media (prefers-reduced-motion: reduce) {
    .perm-sw, .perm-sw::after { transition: none; }
}

/* A person's permissions, folded away until asked for. */
.caps-fold { margin-top: .4rem; }
.caps-fold-head {
    cursor: pointer;
    font-size: .85rem;
    font-weight: 600;
    color: var(--brand-muted);
    padding: .3rem 0;
    list-style: none;
}
.caps-fold-head::-webkit-details-marker { display: none; }
.caps-fold-head::before {
    content: "";
    display: inline-block;
    width: .4rem; height: .4rem;
    margin-right: .5rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(-45deg) translateY(-1px);
    transition: transform .15s ease;
}
.caps-fold[open] > .caps-fold-head::before { transform: rotate(45deg); }
.caps-fold-head:hover { color: var(--brand-text); }
@media (prefers-reduced-motion: reduce) { .caps-fold-head::before { transition: none; } }

/* Auto-save. Fixed to the corner rather than beside the control that
   changed: this page is thirty people long, the save covers all of it, and
   a message pinned to one row would claim only that row was written. */
.autosave {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 1080;
    padding: .5rem .85rem;
    border-radius: 2rem;
    font-size: .85rem;
    font-weight: 600;
    box-shadow: 0 2px 10px rgba(40, 44, 46, .18);
    opacity: 0;
    transform: translateY(6px);
    transition: opacity .15s ease, transform .15s ease;
    pointer-events: none;
}
.autosave.is-saving,
.autosave.is-saved,
.autosave.is-failed { opacity: 1; transform: none; }
.autosave.is-saving { background: var(--brand-cream-soft); color: var(--brand-muted); border: 1px solid var(--line, #ded8cb); }
.autosave.is-saved { background: #2f6b4f; color: #fff; }
/* A failure keeps its own colour and does not fade — a save that did not
   happen must not look like one that did. */
.autosave.is-failed {
    background: var(--brand-rush);
    color: #fff;
    pointer-events: auto;
}
@media (prefers-reduced-motion: reduce) { .autosave { transition: none; } }


/* ==================================================================
   Settings / Who has no shop
   ------------------------------------------------------------------
   A gap in the team's records, made loud. One row per person: who they
   are on the left, where they work on the right, and the whole list
   saved in one press rather than one round trip each.
   ================================================================== */

.no-shop-gap { border-color: var(--brand-gold); }

.no-shop-rows {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.no-shop-row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 0.65rem;
    border-radius: var(--radius-md);
}

/* Banded, so a long list is read across rather than down two columns
   that have drifted apart. */
.no-shop-row:nth-child(odd) { background-color: rgba(128, 128, 128, 0.05); }

.no-shop-who {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;
    line-height: 1.3;
}

.no-shop-pick {
    width: auto;
    min-width: 10rem;
    flex: 0 0 auto;
}

@media (max-width: 575.98px) {
    /* Name over box rather than a box squeezed to four letters. */
    .no-shop-row { align-items: stretch; flex-direction: column; gap: 0.4rem; }
    .no-shop-pick { width: 100%; min-width: 0; }
}

.no-shop-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.no-shop-list li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
    padding: 0.3rem 0;
}

/* The people this page has nothing to ask of. Present so the list is the
   whole team and not a puzzle about who is missing from it. */
.no-shop-list-quiet li { opacity: 0.72; }

.no-shop-clear {
    display: flex;
    align-items: flex-start;
    gap: 0.85rem;
}

.no-shop-clear svg {
    flex: 0 0 auto;
    width: 22px;
    height: 22px;
    margin-top: 0.15rem;
    color: var(--brand-gold);
}


/* ==================================================================
   "Mefoar has been updated"
   ------------------------------------------------------------------
   A bar, not a dialog, and it never reloads by itself: somebody halfway
   through typing an order must not have the page pulled out from under
   them. Above the phone's tab bar, out of the thumb's way.
   ================================================================== */

.new-version {
    position: fixed;
    z-index: 1040;
    left: 1rem;
    right: 1rem;
    bottom: 1rem;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    max-width: 34rem;
    margin: 0 auto;
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--line, rgba(0, 0, 0, 0.09));
    border-radius: var(--radius-lg, 14px);
    background: var(--surface, #fffdf9);
    box-shadow: var(--shadow-lg, 0 10px 30px rgba(0, 0, 0, 0.16));
}

.new-version[hidden] { display: none; }

.new-version-words {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    min-width: 0;
    font-size: 0.9rem;
    line-height: 1.35;
}

@media (max-width: 991.98px) {
    /* Clear of the tab bar the phone draws along the bottom. */
    .new-version { bottom: 5.5rem; }
}
