/* ============================================================
   Smart Solutions — Design System
   These tokens (colors, spacing, fonts, shadows) are reused across
   the Shop, Admin, and Driver portals. Every class name below was
   already in use before this pass — this file only refines how
   they look, so no HTML or JS needed to change alongside it.
   ============================================================ */

:root {
    /* Colors — softened slightly from the original palette, same
       overall navy + teal identity. */
    --color-bg: #F5F7FA;
    --color-surface: #FFFFFF;
    --color-ink: #202942;
    --color-ink-soft: #667085;
    --color-border: #E5E9F0;
    --color-border-soft: #EEF1F6;

    --color-primary: #362A7A;
    --color-primary-soft: #4E3AAE;

    --color-accent: #6B4FE0;
    --color-accent-hover: #4E3AAE;
    --color-accent-soft: #EFEAFC;

    --color-warning: #C77C1F;
    --color-warning-soft: #FDF1DE;
    --color-danger: #C0392B;
    --color-danger-soft: #FBEAE7;
    --color-success: #1E8E5A;
    --color-success-soft: #E7F5EE;
    --color-info: #1B5FA8;
    --color-info-soft: #E3F0FC;
    --color-neutral-soft: #EAEEF5;

    /* Layout */
    --radius-sm: 8px;
    --radius: 12px;
    --radius-lg: 18px;
    --radius-full: 999px;

    --shadow-sm: 0 1px 2px rgba(32, 41, 66, 0.05);
    --shadow-card: 0 1px 2px rgba(32, 41, 66, 0.05), 0 6px 20px rgba(32, 41, 66, 0.07);
    --shadow-lift: 0 2px 4px rgba(32, 41, 66, 0.06), 0 12px 28px rgba(32, 41, 66, 0.10);

    /* Motion — subtle and quick, never gratuitous */
    --transition-fast: 120ms ease;
    --transition: 180ms ease;

    /* Type */
    --font-body: "Inter", -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    --font-heading: "Inter", -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* Dark theme — applied by setting data-theme="dark" on <html>, done by
   public/js/theme.js on every page based on the admin's Settings choice.
   Because every component in this file is styled using the variables
   above rather than hardcoded colors, overriding them here is enough
   to re-theme the whole app without touching any component rule. */
[data-theme="dark"] {
    --color-bg: #12161F;
    --color-surface: #1B212E;
    --color-ink: #E8ECF3;
    --color-ink-soft: #9AA3B5;
    --color-border: #2A3242;
    --color-border-soft: #232B39;

    --color-primary: #B3A2FA;
    --color-primary-soft: #8C74F5;

    --color-accent: #8C74F5;
    --color-accent-hover: #B3A2FA;
    --color-accent-soft: #241A55;

    --color-warning: #E0A339;
    --color-warning-soft: #362B18;
    --color-danger: #E27C6E;
    --color-danger-soft: #38211D;
    --color-success: #4FC98A;
    --color-success-soft: #17301F;
    --color-info: #6FA8DC;
    --color-info-soft: #1C2C3D;
    --color-neutral-soft: #232B39;

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
    --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.3), 0 6px 20px rgba(0, 0, 0, 0.35);
    --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.35), 0 12px 28px rgba(0, 0, 0, 0.45);
}

* {
    box-sizing: border-box;
}

/* This is the real, permanent fix for a bug that's bitten this project
   twice now: any class that sets "display: flex/grid/etc." will, at
   equal CSS specificity, override the browser's default "[hidden] {
   display: none }" rule, simply because it loads later in the
   cascade. Rather than adding a matching [hidden] override every time
   a new component needs a non-block display value (easy to forget —
   see .modal-overlay, .error-message, .success-message, .empty-state
   above, all of which needed this patched in individually), this one
   rule guarantees the hidden attribute always wins, everywhere,
   permanently. The individual [hidden] overrides above are now
   redundant but left in place as harmless documentation.
*/
[hidden] {
    display: none !important;
}

body {
    margin: 0;
    font-family: var(--font-body);
    background-color: var(--color-bg);
    color: var(--color-ink);
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
    font-family: var(--font-heading);
    font-weight: 700;
    color: var(--color-primary);
    letter-spacing: -0.01em;
    margin: 0 0 8px 0;
}

/* ---- Shared small pieces, used across every page ---- */

.badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-accent);
    background: var(--color-accent-soft);
    padding: 5px 11px;
    border-radius: var(--radius-full);
    margin-bottom: 16px;
}

.badge svg {
    width: 12px;
    height: 12px;
}

.subtitle {
    color: var(--color-ink-soft);
    margin-top: 0;
}

.error-message {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    color: var(--color-danger);
    background: var(--color-danger-soft);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    margin: 4px 0 4px 0;
    animation: fadeSlideIn var(--transition) ease-out;
}

.error-message[hidden] {
    display: none;
}

/* Same shape as .error-message, informational rather than an error —
   e.g. the Whish Money payment instructions on storefront checkout. */
.info-callout {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-size: 14px;
    color: var(--color-info);
    background: var(--color-info-soft);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    margin: 4px 0;
    animation: fadeSlideIn var(--transition) ease-out;
}

.info-callout[hidden] {
    display: none;
}

.info-callout svg {
    flex-shrink: 0;
    margin-top: 2px;
}

/* The delivery company's status across the top of every staff page —
   paused, overdue, on trial, or the platform owner inside as support
   (see session-guard.js). */
.company-banner {
    position: sticky;
    top: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 10px;
    padding: 10px 16px;
    font-size: 14px;
    font-weight: 500;
    text-align: center;
    color: var(--color-info);
    background: var(--color-info-soft);
    border-bottom: 1px solid var(--color-border-soft);
}

.company-banner-warning {
    color: var(--color-warning);
    background: var(--color-warning-soft);
}

.company-banner-danger {
    color: var(--color-danger);
    background: var(--color-danger-soft);
}

/* A new delivery company's Getting started guide (admin-dashboard.js). */
.setup-steps {
    list-style: none;
    margin: 12px 0 0;
    padding: 0;
    display: grid;
    gap: 8px;
}

.setup-steps li {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    border: 1px solid var(--color-border-soft);
    border-radius: var(--radius-sm);
}

.setup-steps .setup-check {
    flex: 0 0 26px;
    height: 26px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 700;
    border: 2px solid var(--color-border);
    color: var(--color-text-muted);
}

.setup-steps li.done .setup-check {
    border-color: var(--color-success);
    background: var(--color-success);
    color: #fff;
}

.setup-steps li.done .setup-text {
    color: var(--color-text-muted);
    text-decoration: line-through;
}

.setup-steps .setup-text {
    flex: 1;
}

.setup-steps .setup-text small {
    display: block;
    color: var(--color-text-muted);
}

/* A delivery company's own name and logo on its staff pages (theme.js). */
.company-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 10px;
    font-weight: 600;
    font-size: 15px;
    color: var(--color-ink);
}

.company-brand img {
    width: 36px;
    height: 36px;
    object-fit: contain;
    border-radius: 8px;
    background: var(--color-surface);
    border: 1px solid var(--color-border-soft);
}

.company-banner .secondary-button {
    padding: 4px 12px;
    font-size: 13px;
}

.error-message svg {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

.primary-button,
.secondary-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    font-family: var(--font-body);
    font-size: 15px;
    font-weight: 600;
    border-radius: var(--radius-sm);
    padding: 11px 20px;
    border: none;
    cursor: pointer;
    text-decoration: none;
    transition: background-color var(--transition), border-color var(--transition), transform var(--transition-fast), box-shadow var(--transition);
}

.primary-button svg,
.secondary-button svg {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

.primary-button {
    background: var(--color-accent);
    color: #FFFFFF;
    box-shadow: 0 1px 2px rgba(107, 79, 224, 0.15);
}

.primary-button:hover {
    background: var(--color-accent-hover);
    box-shadow: 0 4px 10px rgba(107, 79, 224, 0.25);
}

.primary-button:active {
    transform: translateY(1px);
}

.primary-button:disabled {
    background: var(--color-border);
    color: var(--color-ink-soft);
    cursor: not-allowed;
    box-shadow: none;
    transform: none;
}

/* Same shape/sizing as .primary-button/.secondary-button, but reserved
   for genuinely destructive or urgent actions — the driver's Emergency
   button, the confirm-send inside its modal. */
.danger-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    font-family: var(--font-body);
    font-size: 15px;
    font-weight: 600;
    border-radius: var(--radius-sm);
    padding: 11px 20px;
    border: none;
    cursor: pointer;
    text-decoration: none;
    background: var(--color-danger);
    color: #FFFFFF;
    box-shadow: 0 1px 2px rgba(192, 57, 43, 0.2);
    transition: background-color var(--transition), transform var(--transition-fast), box-shadow var(--transition);
}

.danger-button svg {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

.danger-button:hover {
    background: #A8321F;
    box-shadow: 0 4px 10px rgba(192, 57, 43, 0.3);
}

.danger-button:active {
    transform: translateY(1px);
}

.danger-button:disabled {
    background: var(--color-border);
    color: var(--color-ink-soft);
    cursor: not-allowed;
    box-shadow: none;
    transform: none;
}

.secondary-button {
    background: var(--color-surface);
    color: var(--color-primary);
    border: 1px solid var(--color-border);
}

.secondary-button:hover {
    background: var(--color-bg);
    border-color: var(--color-ink-soft);
}

.secondary-button:active {
    transform: translateY(1px);
}

/* ---- Portal selection page (the homepage) ---- */

.portal-select {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 24px;
    gap: 32px;
}

.portal-header {
    text-align: center;
    max-width: 480px;
}

.portal-cards {
    display: grid;
    grid-template-columns: repeat(3, minmax(180px, 220px));
    gap: 20px;
}

.portal-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    padding: 28px 24px;
    text-decoration: none;
    color: inherit;
    display: block;
    transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}

.portal-card:hover {
    border-color: var(--color-accent);
    box-shadow: var(--shadow-lift);
    transform: translateY(-2px);
}

.portal-card-icon {
    width: 40px;
    height: 40px;
    border-radius: var(--radius-sm);
    background: var(--color-accent-soft);
    color: var(--color-accent);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 14px;
}

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

.portal-card h2 {
    color: var(--color-primary);
    margin-bottom: 8px;
}

.portal-card p {
    color: var(--color-ink-soft);
    font-size: 14px;
    margin: 0;
}

@media (max-width: 640px) {
    .portal-cards {
        grid-template-columns: 1fr;
        width: 100%;
        max-width: 320px;
    }
}

/* ---- Login pages (Admin, Shop, Driver) ---- */

.auth-page {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
}

.auth-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    padding: 40px;
    max-width: 380px;
    width: 100%;
    display: flex;
    flex-direction: column;
}

.auth-card label {
    font-size: 13px;
    font-weight: 600;
    color: var(--color-ink);
    margin-top: 16px;
    margin-bottom: 6px;
}

.auth-card input {
    font-family: var(--font-body);
    font-size: 15px;
    padding: 10px 12px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-bg);
    color: var(--color-ink);
    transition: border-color var(--transition), box-shadow var(--transition), background-color var(--transition);
}

.auth-card input:focus {
    outline: none;
    border-color: var(--color-accent);
    background: var(--color-surface);
    box-shadow: 0 0 0 3px var(--color-accent-soft);
}

.auth-card .primary-button {
    margin-top: 24px;
}

.back-link {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    text-align: center;
    margin-top: 20px;
    font-size: 13px;
    color: var(--color-ink-soft);
    text-decoration: none;
    transition: color var(--transition-fast);
}

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

.back-link:hover {
    color: var(--color-accent);
}

/* ---- Placeholder dashboard pages ---- */

.dashboard-placeholder {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
}

.dashboard-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    padding: 40px;
    max-width: 480px;
    width: 100%;
}

.dashboard-card .secondary-button {
    margin-top: 8px;
}

/* ---- Real dashboard layout ---- */

.dashboard {
    max-width: 1100px;
    margin: 0 auto;
    padding: 32px 24px 64px;
}

.dashboard-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 16px;
    margin-bottom: 28px;
}

.dashboard-header h1 {
    margin: 0;
}

.panel {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    padding: 28px;
    margin-bottom: 24px;
    animation: fadeSlideIn var(--transition) ease-out;
}

.panel h2 {
    font-size: 18px;
    margin-bottom: 20px;
}

/* ---- Forms (new order, manage order, driver/shop forms) ---- */

.form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px 20px;
}

/* A day of opening hours: name, Closed, opens, closes. On a phone the
   two times go on a line of their own rather than past the screen edge. */
.form-grid.hours-row {
    grid-template-columns: 110px 100px minmax(0, 1fr) minmax(0, 1fr);
    align-items: center;
    gap: 12px;
}
.form-grid.hours-row input[type="time"] {
    min-width: 0;
    width: 100%;
}
@media (max-width: 640px) {
    .form-grid.hours-row {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        gap: 8px 12px;
    }
}

.form-field {
    display: flex;
    flex-direction: column;
    /* A field may be narrower than its content (a dropdown with a long
       option, say), so a form never pushes past a phone's screen. */
    min-width: 0;
}
.form-field select {
    max-width: 100%;
}

.form-field-wide {
    grid-column: 1 / -1;
}

.form-hint {
    font-size: 13px;
    color: var(--color-ink-soft);
}

.form-hint-success {
    color: var(--color-success);
}

.form-hint-error {
    color: var(--color-danger);
}

.location-field-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px 14px;
}

.form-field label {
    font-size: 13px;
    font-weight: 600;
    color: var(--color-ink);
    margin-bottom: 6px;
}

.form-field input,
.form-field textarea,
.form-field select {
    font-family: var(--font-body);
    font-size: 15px;
    padding: 10px 12px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-bg);
    color: var(--color-ink);
    resize: vertical;
    transition: border-color var(--transition), box-shadow var(--transition), background-color var(--transition);
}

.form-field input:focus,
.form-field textarea:focus,
.form-field select:focus {
    outline: none;
    border-color: var(--color-accent);
    background: var(--color-surface);
    box-shadow: 0 0 0 3px var(--color-accent-soft);
}

.order-form .error-message,
.order-form .success-message {
    margin-top: 18px;
}

.success-message {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    color: var(--color-success);
    background: var(--color-success-soft);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    animation: fadeSlideIn var(--transition) ease-out;
}

.success-message[hidden] {
    display: none;
}

.success-message svg {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

.order-form .primary-button {
    margin-top: 18px;
}

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

/* ---- Tabs ---- */

.tabs {
    display: flex;
    gap: 4px;
    border-bottom: 1px solid var(--color-border);
    margin-bottom: 20px;
}

/* A tab bar with more than a couple of buttons (e.g. admin-expansion's
   four) doesn't fit a phone's width — rather than let it overflow the
   page or squish every label, it scrolls horizontally on its own,
   same "contain the overflow, don't fight it" approach as
   .table-scroll below. Labels stay intact and tappable instead of
   getting truncated or icon-only. */
@media (max-width: 640px) {
    .tabs {
        flex-wrap: nowrap;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }
    .tab-button {
        flex-shrink: 0;
    }
}

.tab-button {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--font-body);
    font-size: 14px;
    font-weight: 600;
    color: var(--color-ink-soft);
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    padding: 10px 4px;
    margin-inline-end: 20px;
    cursor: pointer;
    transition: color var(--transition-fast), border-color var(--transition-fast);
}

.tab-button:hover {
    color: var(--color-primary);
}

.tab-button.active {
    color: var(--color-primary);
    border-bottom-color: var(--color-accent);
}

.tab-panel {
    padding-top: 4px;
    animation: fadeSlideIn var(--transition) ease-out;
}

/* ---- Tables ---- */

.table-scroll {
    overflow-x: auto;
}

/* A tall table otherwise just grows the whole page taller with every
   row — this caps it and scrolls internally instead, with the header
   (and a totals row) pinned so they stay visible while scrolling.
   public/js/table-tools.js puts every list table on a dashboard in it,
   with a search box above. */
.table-scroll-bounded {
    max-height: min(440px, 70vh);
    overflow-y: auto;
}
.table-scroll-bounded thead th {
    position: sticky;
    top: 0;
    background: var(--color-surface);
    z-index: 2;
}
/* The header's actions cell is pinned both ways, so it sits above both. */
.table-scroll-bounded thead th.actions-cell {
    z-index: 3;
}
.table-scroll-bounded tfoot td,
.table-scroll-bounded tfoot th {
    position: sticky;
    bottom: 0;
    background: var(--color-surface);
    z-index: 2;
}

/* The search box above a list table (public/js/table-tools.js). */
.table-tools {
    margin-bottom: 12px;
}
.table-tools-count {
    align-self: center;
    font-size: 13px;
    color: var(--color-ink-soft);
    white-space: nowrap;
}
.table-tools-empty {
    margin: 8px 0 0;
}
/* A class rather than the hidden attribute: some tables lay their rows
   out as cards on a phone (display: block), which would override it. */
.orders-table tr.table-tools-hidden,
tr.table-tools-hidden {
    display: none !important;
}

/* Printed, a table shows every row and no search box. */
@media print {
    .table-scroll-bounded {
        max-height: none !important;
        overflow: visible !important;
    }
    .table-tools,
    .table-tools-search,
    .table-tools-count,
    .table-tools-empty {
        display: none !important;
    }
}

.orders-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
}

.orders-table th {
    text-align: start;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--color-ink-soft);
    padding: 8px 10px;
    border-bottom: 1px solid var(--color-border);
    white-space: nowrap;
}

.orders-table td {
    padding: 12px 10px;
    border-bottom: 1px solid var(--color-border-soft);
    white-space: nowrap;
}

.orders-table tbody tr {
    transition: background-color var(--transition-fast);
}

.orders-table tbody tr:hover {
    background: var(--color-bg);
}

/* Rows that open a details view on click (see shop-dashboard.js's
   openOrderDetailsModal) — buttons inside them (Cancel, Start
   Preparing, etc.) still work as normal since the click handler skips
   anything inside .actions-cell or a prep-cell button. */
.orders-table tbody tr.order-row-clickable {
    cursor: pointer;
}

.orders-table tbody tr:last-child td {
    border-bottom: none;
}

/* The actions cell (row buttons like Cancel/Edit/Deactivate) pins to
   the right edge of .table-scroll instead of scrolling away with the
   rest of a wide row — a cashier or shop owner shouldn't have to
   scroll all the way across a 10-column order row just to reach the
   Cancel button. */
/* A <td> must stay a table cell: the generic .actions-cell below is a flex row, and a flex <td> leaves its row,
   which drew every actions cell as a box of its own, out of line with the rest of the row. */
/* Its buttons wrap inside a column of limited width (the admin's Shops row has ten of them); a row with only two or
   three still sits on one line. */
.orders-table td.actions-cell { display: table-cell; width: 1%; min-width: 230px; max-width: 320px; white-space: normal; }
.orders-table td.actions-cell > * { margin: 2px 12px 2px 0; }
[dir="rtl"] .orders-table td.actions-cell > * { margin: 2px 0 2px 12px; }

.orders-table th.actions-cell,
.orders-table td.actions-cell {
    position: sticky;
    inset-inline-end: 0;
    z-index: 1;
    background: var(--color-surface);
    box-shadow: -8px 0 12px -8px rgba(0, 0, 0, 0.18);
}

.orders-table tbody tr:hover td.actions-cell {
    background: var(--color-bg);
}

/* Purchase List (shop-inventory.html) above the card-layout breakpoint
   below: with up to 10 columns, its width was almost entirely driven
   by the HEADER labels ("Qty to Purchase", "Current Stock", etc.)
   forcing themselves onto one line via .orders-table th's own
   white-space: nowrap, even though every cell's actual data (a short
   number, a two-word select) needs far less room — letting the
   header text wrap, and giving the two free-width form controls
   (Paid/Supplier, native <select>s with no width of their own) a firm
   cap, was enough on its own to fit a normal desktop width with no
   horizontal scroll at all, without shrinking any real data. */
#purchaseListTable {
    font-size: 13px;
}
#purchaseListTable th {
    white-space: normal;
    line-height: 1.25;
    font-size: 10.5px;
}
#purchaseListTable td, #purchaseListTable th {
    padding: 8px 5px;
}
#purchaseListTable .purchase-payment-select {
    max-width: 92px;
}
#purchaseListTable .purchase-supplier-select {
    max-width: 110px;
}

/* Purchase List (shop-inventory.html) has too many columns (up to 10
   in ingredients mode) to ever fit a phone-width screen as a real
   table — below this width it drops the row/column grid entirely and
   restacks each row as its own card, one field per line via the
   row's own data-label attributes (set in shop-inventory.js's
   renderPurchaseListTable), so nothing needs a sideways drag to read
   or fill in. Scoped to this one table by id, not every .orders-table
   in the app, since every other table here still fits its own content
   fine within .table-scroll's ordinary horizontal scroll. */
@media (max-width: 640px) {
    #purchaseListTable thead {
        display: none;
    }
    #purchaseListTable, #purchaseListTable tbody, #purchaseListTable tr, #purchaseListTable td {
        display: block;
        width: 100%;
    }
    #purchaseListTable tbody tr {
        border: 1px solid var(--color-border);
        border-radius: 10px;
        padding: 10px 12px;
        margin-bottom: 10px;
    }
    #purchaseListTable tbody tr:last-child {
        margin-bottom: 0;
    }
    #purchaseListTable td {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        white-space: normal;
        padding: 6px 0;
        border-bottom: 1px solid var(--color-border-soft);
    }
    #purchaseListTable td:last-child {
        border-bottom: none;
    }
    #purchaseListTable td::before {
        content: attr(data-label);
        font-size: 12px;
        font-weight: 600;
        letter-spacing: 0.03em;
        text-transform: uppercase;
        color: var(--color-ink-soft);
        flex-shrink: 0;
    }
    /* The name is this card's own title, not a label:value pair like
       every other field — full-width, no label, bolder. */
    #purchaseListTable td[data-label="Name"], #purchaseListTable td[data-label="Item"] {
        font-weight: 700;
        font-size: 15px;
    }
    #purchaseListTable td[data-label="Name"]::before, #purchaseListTable td[data-label="Item"]::before {
        content: none;
    }
    #purchaseListTable input, #purchaseListTable select {
        width: auto;
        max-width: 55%;
    }
}

/* Dispatcher's Current/Completed Orders tables (dispatcher-dashboard.html)
   — same "too many columns for their own header labels" problem as
   Purchase List above, made worse by two genuinely free-text columns
   (Customer, Address) that can run long. Same header-wrap/padding fix
   for the columns that fit fine once their labels can wrap, PLUS a
   firm cap + ellipsis on Customer/Address specifically — the full text
   is still one tap away via the row's own details modal (or a hover
   tooltip on desktop, via each cell's title attribute set in
   dispatcher-dashboard.js), so nothing is actually lost, just not
   forced to blow out the whole table's width for one long address. */
.dispatcher-orders-table {
    font-size: 12px;
}
.dispatcher-orders-table th {
    white-space: normal;
    line-height: 1.25;
    font-size: 10px;
}
.dispatcher-orders-table td, .dispatcher-orders-table th {
    padding: 7px 4px;
}
.dispatcher-orders-table td[data-label="Customer"] {
    max-width: 110px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.dispatcher-orders-table td[data-label="Address"] {
    max-width: 170px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Fee/COD use CurrencyKit.dualHtml() (not .dual()) specifically in this
   table: the L.L. amount renders as a block-level .dual-lbp span, which
   always drops to its own line below the USD figure instead of competing
   with it for horizontal space, so the column only needs to be as wide
   as the longer of the two lines rather than both combined. */
.dispatcher-orders-table td[data-label="Fee"],
.dispatcher-orders-table td[data-label="COD"] {
    max-width: 80px;
}
.dispatcher-orders-table .dual-lbp {
    display: block;
    font-size: 10.5px;
    font-weight: 500;
    color: var(--color-ink-soft);
    margin-top: 1px;
}
/* Created/Last Updated is a full localized date+time string — too long
   to fit one line at this column budget. Rather than truncate part of a
   timestamp (misleading) it's allowed to wrap onto two lines. */
.dispatcher-orders-table td[data-label="Created"],
.dispatcher-orders-table td[data-label="Last Updated"] {
    max-width: 88px;
    white-space: normal;
    line-height: 1.3;
}
/* Prep's "Start Preparing"/"Mark Ready" button label is allowed to wrap
   the same way, and shrinks slightly further since it's a UI label, not
   data. */
.dispatcher-orders-table td[data-label="Prep"] {
    max-width: 90px;
}
.dispatcher-orders-table td[data-label="Prep"] .text-button {
    font-size: 11px;
    white-space: normal;
    text-align: start;
}

/* Below phone width, drop the grid entirely and restack each order as
   its own card — same technique and same reasoning as #purchaseListTable
   above (see that block's own comment). Customer/Address go back to
   wrapping normally here instead of the desktop ellipsis, since a
   stacked card has no row-width to protect. */
@media (max-width: 640px) {
    .dispatcher-orders-table thead {
        display: none;
    }
    .dispatcher-orders-table, .dispatcher-orders-table tbody, .dispatcher-orders-table tr, .dispatcher-orders-table td {
        display: block;
        width: 100%;
    }
    .dispatcher-orders-table tbody tr {
        border: 1px solid var(--color-border);
        border-radius: 10px;
        padding: 10px 12px;
        margin-bottom: 10px;
    }
    .dispatcher-orders-table tbody tr:last-child {
        margin-bottom: 0;
    }
    .dispatcher-orders-table td {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        max-width: none;
        overflow: visible;
        text-overflow: clip;
        white-space: normal;
        padding: 6px 0;
        border-bottom: 1px solid var(--color-border-soft);
    }
    .dispatcher-orders-table td:last-child {
        border-bottom: none;
    }
    .dispatcher-orders-table td::before {
        content: attr(data-label);
        font-size: 12px;
        font-weight: 600;
        letter-spacing: 0.03em;
        text-transform: uppercase;
        color: var(--color-ink-soft);
        flex-shrink: 0;
    }
    .dispatcher-orders-table td[data-label="Order #"] {
        font-weight: 700;
        font-size: 15px;
    }
    .dispatcher-orders-table td[data-label="Order #"]::before {
        content: none;
    }
    .dispatcher-orders-table td.actions-cell {
        justify-content: flex-start;
        flex-wrap: wrap;
        position: static;
        box-shadow: none;
    }
    .dispatcher-orders-table td[data-label="Prep"] .text-button {
        font-size: 13px;
        text-align: end;
    }
}

/* Same card-stacking treatment as .dispatcher-orders-table above,
   generalized as a reusable class instead of tied to one page — used
   by admin-expansion.html's Today/Prospects tables, each of which has
   too many columns (up to 10, on Prospects) to stay readable at phone
   width even with .table-scroll's horizontal scroll. */
@media (max-width: 640px) {
    .expansion-table thead {
        display: none;
    }
    .expansion-table, .expansion-table tbody, .expansion-table tr, .expansion-table td {
        display: block;
        width: 100%;
    }
    .expansion-table tbody tr {
        border: 1px solid var(--color-border);
        border-radius: 10px;
        padding: 10px 12px;
        margin-bottom: 10px;
    }
    .expansion-table tbody tr:last-child {
        margin-bottom: 0;
    }
    .expansion-table td {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        white-space: normal;
        text-align: end;
        padding: 6px 0;
        border-bottom: 1px solid var(--color-border-soft);
    }
    .expansion-table td:last-child {
        border-bottom: none;
    }
    .expansion-table td::before {
        content: attr(data-label);
        font-size: 12px;
        font-weight: 600;
        letter-spacing: 0.03em;
        text-transform: uppercase;
        color: var(--color-ink-soft);
        flex-shrink: 0;
        text-align: start;
    }
    .expansion-table td[data-label="Business"] {
        font-weight: 700;
        font-size: 15px;
    }
    .expansion-table td[data-label="Business"]::before {
        content: none;
    }
    .expansion-table td.actions-cell {
        justify-content: flex-start;
        flex-wrap: wrap;
        position: static;
        box-shadow: none;
    }
}

/* Same card-stacking treatment again, for admin-subscriptions.html's
   Billing/Fees tables — copied rather than shared with
   .expansion-table since the two pages' tables have different
   emphasized first column, but otherwise identical. */
@media (max-width: 640px) {
    .subscriptions-table thead {
        display: none;
    }
    .subscriptions-table, .subscriptions-table tbody, .subscriptions-table tr, .subscriptions-table td {
        display: block;
        width: 100%;
    }
    .subscriptions-table tbody tr {
        border: 1px solid var(--color-border);
        border-radius: 10px;
        padding: 10px 12px;
        margin-bottom: 10px;
    }
    .subscriptions-table tbody tr:last-child {
        margin-bottom: 0;
    }
    .subscriptions-table td {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        white-space: normal;
        text-align: end;
        padding: 6px 0;
        border-bottom: 1px solid var(--color-border-soft);
    }
    .subscriptions-table td:last-child {
        border-bottom: none;
    }
    .subscriptions-table td::before {
        content: attr(data-label);
        font-size: 12px;
        font-weight: 600;
        letter-spacing: 0.03em;
        text-transform: uppercase;
        color: var(--color-ink-soft);
        flex-shrink: 0;
        text-align: start;
    }
    .subscriptions-table td[data-label="Business"] {
        font-weight: 700;
        font-size: 15px;
    }
    .subscriptions-table td[data-label="Business"]::before {
        content: none;
    }
    .subscriptions-table td.actions-cell {
        justify-content: flex-start;
        flex-wrap: wrap;
        position: static;
        box-shadow: none;
    }
}

.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    text-align: center;
    color: var(--color-ink-soft);
    font-size: 14px;
    padding: 40px 0;
}

.empty-state[hidden] {
    display: none;
}

.empty-state svg {
    width: 32px;
    height: 32px;
    color: var(--color-border);
}

.text-button {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-family: var(--font-body);
    font-size: 13px;
    font-weight: 600;
    color: var(--color-accent);
    background: none;
    border: none;
    cursor: pointer;
    padding: 0;
    transition: color var(--transition-fast);
}

.text-button svg {
    width: 14px;
    height: 14px;
}

.text-button:hover {
    text-decoration: underline;
}

/* ---- Status badges ---- */

.status-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    font-weight: 600;
    padding: 4px 10px;
    border-radius: var(--radius-full);
    white-space: nowrap;
}

.status-badge svg {
    width: 11px;
    height: 11px;
}

.status-new {
    background: var(--color-neutral-soft);
    color: var(--color-primary);
}

.status-assigned {
    background: var(--color-warning-soft);
    color: var(--color-warning);
}

.status-progress {
    background: var(--color-info-soft);
    color: var(--color-info);
}

.status-success {
    background: var(--color-accent-soft);
    color: var(--color-success);
}

.status-cancelled {
    background: var(--color-danger-soft);
    color: var(--color-danger);
}

/* ---- Admin dashboard stats ---- */

.stats-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 16px;
    margin-bottom: 24px;
}

.stat-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    transition: box-shadow var(--transition), transform var(--transition);
}

.stat-card:hover {
    box-shadow: var(--shadow-lift);
    transform: translateY(-1px);
}

.stat-label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--color-ink-soft);
}

.stat-label svg {
    width: 14px;
    height: 14px;
    color: var(--color-accent);
}

.stat-value {
    font-size: 30px;
    font-weight: 700;
    color: var(--color-primary);
}

/* CurrencyKit.dualHtml() values ("$1,029.00" + a muted "(92,610,000
   L.L.)" line) run much longer than the plain-USD figures .stat-value
   was sized for elsewhere in the app — at the default 30px on one
   line this overflows a narrow card in a 4-up grid. Sized down and
   the .dual-lbp span (below) breaks onto its own smaller line instead
   of the two currencies competing for space at equal size. */
.stats-grid-dual-currency .stat-value {
    font-size: clamp(17px, 2vw, 24px);
    line-height: 1.25;
    overflow-wrap: break-word;
}

.stats-grid-dual-currency .stat-value .dual-lbp {
    display: block;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-ink-soft);
    margin-top: 2px;
}

/* Cards in this row can end up taller than their content (e.g. a
   short "66" next to a wrapped two-line dual-currency figure) since
   grid rows stretch every card to match the tallest — center each
   card's content instead of leaving it pinned to the top with dead
   space below. */
.stats-grid-dual-currency .stat-card {
    justify-content: center;
}

.cash-balance-figure {
    font-size: 44px;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    color: var(--color-warning);
}

@media (max-width: 800px) {
    .stats-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

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

/* ---- Panel header row (title + filter dropdown) ---- */

.panel-header-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 20px;
}

.panel-header-row h2 {
    margin-bottom: 0;
}

/* A panel-header h2 wrapped in a button, so the whole title row toggles
   a collapsible section below it (see shop-dashboard.js's storefront
   panel) — reset all the default button chrome so it reads exactly
   like the plain, non-interactive h2 it replaces. */
.panel-collapse-toggle {
    display: flex;
    align-items: center;
    gap: 6px;
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    font: inherit;
    color: inherit;
    cursor: pointer;
    text-align: start;
}

/* Rotated on its own <span> wrapper rather than directly on the lucide
   <svg> icon — CSS transform on a root <svg> element computes
   unreliably in some environments, a plain HTML element doesn't have
   that problem. */
.panel-collapse-chevron {
    display: inline-flex;
    flex-shrink: 0;
    transition: transform var(--transition-fast);
}

.panel-collapse-chevron svg {
    width: 18px;
    height: 18px;
    color: var(--color-ink-soft);
}

.panel-collapse-toggle[aria-expanded="true"] .panel-collapse-chevron {
    transform: rotate(90deg);
}

.filter-select {
    font-family: var(--font-body);
    font-size: 14px;
    padding: 8px 10px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    color: var(--color-ink);
    transition: border-color var(--transition-fast);
    /* A long option (an account's full name) would otherwise widen it
       past a phone's screen. */
    max-width: 100%;
}

.filter-select:focus {
    outline: none;
    border-color: var(--color-accent);
}

.actions-cell {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 14px;
}

.danger-text {
    color: var(--color-danger);
}

.prep-timer {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    color: var(--color-warning);
}

/* ---- Modal ---- */

.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(32, 41, 66, 0.5);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    z-index: 100;
    animation: fadeIn var(--transition) ease-out;
}

/* Without this, the hidden attribute has no effect: an author
   stylesheet rule like "display: flex" above beats the browser's
   built-in "[hidden] { display: none }" rule at equal specificity,
   since ours loads later in the cascade. This rule restores it. */
.modal-overlay[hidden] {
    display: none;
}

.modal {
    background: var(--color-surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lift);
    padding: 32px;
    max-width: 560px;
    width: 100%;
    max-height: 90vh;
    overflow-y: auto;
    animation: modalPopIn var(--transition) ease-out;
}

.modal h2 {
    font-size: 18px;
    margin-bottom: 20px;
}

.modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 20px;
}

.modal-header h2 {
    margin-bottom: 0;
}

.modal-close-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    flex-shrink: 0;
    border-radius: var(--radius-full);
    border: none;
    background: none;
    color: var(--color-ink-soft);
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

.modal-close-btn:hover {
    background: var(--color-bg);
    color: var(--color-ink);
}

.modal-close-btn svg {
    width: 18px;
    height: 18px;
}

.modal .error-message {
    margin-top: 16px;
}

.modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 12px;
    margin-top: 24px;
}

/* ---- Order timeline (Manage Order modal) ---- */

.order-timeline {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-bottom: 20px;
    padding: 16px;
    background: var(--color-bg);
    border-radius: var(--radius-sm);
}

.timeline-item {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 13px;
}

.timeline-item svg {
    width: 15px;
    height: 15px;
    color: var(--color-accent);
    flex-shrink: 0;
}

.timeline-item-label {
    font-weight: 600;
    color: var(--color-ink);
}

.timeline-item-time {
    color: var(--color-ink-soft);
    margin-inline-start: auto;
    white-space: nowrap;
}

/* ---- Search + sort controls above tables ---- */

.table-controls {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 16px;
}

.search-input-wrap {
    position: relative;
    flex: 1;
    min-width: 200px;
}

.search-input-wrap svg {
    position: absolute;
    inset-inline-start: 12px;
    top: 50%;
    transform: translateY(-50%);
    width: 15px;
    height: 15px;
    color: var(--color-ink-soft);
    pointer-events: none;
}

.search-input-wrap input {
    width: 100%;
    font-family: var(--font-body);
    font-size: 14px;
    padding-block: 9px;
    padding-inline: 34px 12px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    color: var(--color-ink);
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.search-input-wrap input:focus {
    outline: none;
    border-color: var(--color-accent);
    box-shadow: 0 0 0 3px var(--color-accent-soft);
}

/* ---- Printing a single order ---- */

.print-only {
    display: none;
}

@media print {
    body * {
        visibility: hidden;
    }
    .print-only, .print-only * {
        visibility: visible;
    }
    .print-only {
        display: block;
        position: absolute;
        top: 0;
        inset-inline-start: 0;
        width: 100%;
        padding: 24px;
    }
    .print-only h2 {
        margin-bottom: 4px;
    }
    .print-only table {
        width: 100%;
        border-collapse: collapse;
        margin-top: 16px;
    }
    .print-only td {
        padding: 6px 4px;
        border-bottom: 1px solid #ccc;
        font-size: 13px;
    }
    .print-only td:first-child {
        font-weight: 600;
        width: 160px;
    }

    /* The rule above was written for the single-order label/value
       table (admin-dashboard.js's printArea) — wrong for a real
       multi-column table like the cash movements report, where the
       first column is just a timestamp like any other cell. */
    .print-only table.report-table td:first-child {
        font-weight: normal;
        width: auto;
    }

    /* visibility:hidden keeps an element's box in the layout (unlike
       display:none), so the full-height dashboard behind .print-only
       was still forcing extra blank pages based on its own height —
       collapse it outright so pagination is based on the printed
       content alone. */
    .dashboard {
        display: none !important;
    }
    /* Text inherits --color-ink, which is a near-white color under
       the dark theme — fine on a dark screen, but it printed as
       barely-visible pale gray on white paper. Force solid ink
       regardless of which theme is active on screen. */
    :root, [data-theme="dark"] {
        --color-ink: #000000;
        --color-ink-soft: #333333;
    }
}

/* ---- Toast notifications ---- */

.toast-container {
    position: fixed;
    bottom: 24px;
    inset-inline-end: 24px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    z-index: 200;
    max-width: calc(100vw - 48px);
}

.toast {
    display: flex;
    align-items: center;
    gap: 10px;
    background: var(--color-surface);
    color: var(--color-ink);
    border: 1px solid var(--color-border);
    border-inline-start: 4px solid var(--color-accent);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-lift);
    padding: 12px 16px;
    font-size: 14px;
    min-width: 240px;
    max-width: 360px;
    animation: toastIn var(--transition) ease-out;
}

.toast.toast-leaving {
    animation: toastOut var(--transition) ease-in forwards;
}

.toast svg {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}

.toast-success {
    border-inline-start-color: var(--color-success);
}

.toast-success svg {
    color: var(--color-success);
}

.toast-error {
    border-inline-start-color: var(--color-danger);
}

.toast-error svg {
    color: var(--color-danger);
}

.toast-info svg {
    color: var(--color-accent);
}

@media (max-width: 480px) {
    .toast-container {
        left: 16px;
        right: 16px;
        bottom: 16px;
    }

    .toast {
        min-width: 0;
        max-width: none;
        width: 100%;
    }
}

/* ---- Skeleton loaders ---- */

.skeleton-row td {
    padding: 12px 10px;
}

.skeleton-bar {
    height: 14px;
    border-radius: var(--radius-sm);
    background: linear-gradient(90deg, var(--color-border-soft) 25%, var(--color-border) 37%, var(--color-border-soft) 63%);
    background-size: 400% 100%;
    animation: skeletonShimmer 1.4s ease infinite;
}

.skeleton-bar--short {
    width: 60%;
}

.skeleton-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/* ---- Motion ---- */

@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes fadeSlideIn {
    from { opacity: 0; transform: translateY(4px); }
    to { opacity: 1; transform: translateY(0); }
}

@keyframes modalPopIn {
    from { opacity: 0; transform: scale(0.96) translateY(6px); }
    to { opacity: 1; transform: scale(1) translateY(0); }
}

@keyframes toastIn {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: translateY(0); }
}

@keyframes toastOut {
    from { opacity: 1; transform: translateY(0); }
    to { opacity: 0; transform: translateY(8px); }
}

@keyframes skeletonShimmer {
    0% { background-position: 100% 0; }
    100% { background-position: 0 0; }
}

@media (prefers-reduced-motion: reduce) {
    * {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
    }
}

/* ---- Menu items (Shop's own catalog) ---- */

.category-heading {
    font-size: 15px;
    font-weight: 700;
    color: var(--color-primary);
    margin: 24px 0 12px 0;
}

.category-heading:first-child {
    margin-top: 0;
}

.menu-items-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 16px;
    margin-bottom: 8px;
}

.menu-item-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-card);
    display: flex;
    flex-direction: column;
    transition: box-shadow var(--transition), transform var(--transition);
}

.menu-item-card:hover {
    box-shadow: var(--shadow-lift);
    transform: translateY(-2px);
}

.menu-item-card.menu-item-unavailable {
    opacity: 0.6;
}

.menu-item-image,
.menu-item-image-placeholder {
    width: 100%;
    height: 140px;
    background: var(--color-bg);
}

.menu-item-image {
    object-fit: cover;
    display: block;
}

.menu-item-image-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-border);
}

.menu-item-image-placeholder svg {
    width: 36px;
    height: 36px;
}

.menu-item-body {
    padding: 14px 16px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    flex: 1;
}

.menu-item-name-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 8px;
}

.menu-item-name {
    font-weight: 600;
    color: var(--color-ink);
    min-width: 0;
}

/* CurrencyKit.dual() values ("$1,029.00 (92,610,000 L.L.)") are much
   longer than a plain price and don't fit on one line in a 220px-min
   card — wrap and shrink instead of overflowing past the card edge. */
.menu-item-price {
    font-weight: 700;
    color: var(--color-primary);
    font-size: clamp(12px, 3vw, 15px);
    text-align: end;
    overflow-wrap: break-word;
    flex-shrink: 0;
    max-width: 45%;
}

.menu-item-description {
    font-size: 13px;
    color: var(--color-ink-soft);
}

.menu-item-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    font-size: 12px;
    color: var(--color-ink-soft);
    margin-top: 4px;
}

.badge-popular {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: 11px;
    font-weight: 700;
    color: var(--color-warning);
    background: var(--color-warning-soft);
    padding: 2px 8px;
    border-radius: var(--radius-full);
}

.badge-popular svg {
    width: 11px;
    height: 11px;
}

.menu-item-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    padding: 10px 16px;
    border-top: 1px solid var(--color-border-soft);
}

/* ---- Extras editor (inside Add/Edit Item modal) ---- */

.extra-row {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
}

.extra-row input.extra-nutrition-input {
    flex: 0 0 60px;
}

.extra-row input[type="text"] {
    flex: 2;
    font-family: var(--font-body);
    font-size: 14px;
    padding: 8px 10px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-bg);
    color: var(--color-ink);
}

.extra-row input[type="number"] {
    flex: 1;
    font-family: var(--font-body);
    font-size: 14px;
    padding: 8px 10px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-bg);
    color: var(--color-ink);
}

/* ---- Ingredient/Variant rows (inside Add/Edit Item modal, and the
   extra-row's own optional ingredient link) — same row layout as
   .extra-row above, just also covering <select>. ---- */

.ingredient-row, .variant-row {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
}

.ingredient-row input, .ingredient-row select,
.variant-row input, .variant-row select,
.extra-row select {
    font-family: var(--font-body);
    font-size: 14px;
    padding: 8px 10px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-bg);
    color: var(--color-ink);
}

.variant-row input:disabled {
    color: var(--color-ink-soft);
    cursor: not-allowed;
}

/* ---- Item picker (New Order form) ---- */

.menu-picker-categories {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 10px;
}

.menu-picker-category-pill {
    font-family: var(--font-body);
    font-size: 13px;
    font-weight: 600;
    color: var(--color-ink-soft);
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-full);
    padding: 5px 12px;
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}

.menu-picker-category-pill:hover {
    border-color: var(--color-accent);
}

.menu-picker-category-pill.active {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: #fff;
}

.menu-picker-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: 8px;
    max-height: 320px;
    overflow-y: auto;
    padding: 2px;
}

.menu-picker-card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    text-align: start;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    cursor: pointer;
    transition: border-color var(--transition-fast), background-color var(--transition-fast);
}

.menu-picker-card:hover {
    border-color: var(--color-accent);
    background: var(--color-accent-soft);
}

.menu-picker-card-name {
    font-size: 13px;
    font-weight: 600;
    color: var(--color-ink);
    line-height: 1.3;
}

.menu-picker-card-price {
    font-size: 12px;
    color: var(--color-ink-soft);
}

.order-items-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 12px 0;
}

.order-items-list-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 8px 12px;
    background: var(--color-bg);
    border-radius: var(--radius-sm);
    font-size: 14px;
}

.cart-qty-stepper {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.cart-qty-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    font-size: 14px;
    font-weight: 700;
    line-height: 1;
    color: var(--color-primary);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.cart-qty-btn:hover {
    border-color: var(--color-accent);
    color: var(--color-accent);
}

.order-items-subtotal {
    display: flex;
    justify-content: space-between;
    font-weight: 700;
    padding: 8px 12px;
    font-size: 14px;
    color: var(--color-primary);
}

/* ---- Real-time connection indicator ---- */

.connection-indicator {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 600;
    color: var(--color-ink-soft);
}

.connection-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--color-ink-soft);
    flex-shrink: 0;
}

.connection-connected .connection-dot {
    background: var(--color-success);
}
.connection-connected .connection-text {
    color: var(--color-success);
}

.connection-reconnecting .connection-dot {
    background: var(--color-warning);
    animation: connectionPulse 1s ease-in-out infinite;
}
.connection-reconnecting .connection-text {
    color: var(--color-warning);
}

.connection-disconnected .connection-dot {
    background: var(--color-danger);
}
.connection-disconnected .connection-text {
    color: var(--color-danger);
}

@keyframes connectionPulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.35; }
}

/* ---- Notification bell ---- */

.notification-bell-wrap {
    position: relative;
}

.notification-badge {
    position: absolute;
    top: -4px;
    inset-inline-end: -4px;
    background: var(--color-danger);
    color: #FFFFFF;
    font-size: 10px;
    font-weight: 700;
    min-width: 16px;
    height: 16px;
    border-radius: var(--radius-full);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 4px;
    pointer-events: none;
}

/* A small header chip linking to /portal/driver/profile — a round
   photo (or a fallback icon when the driver hasn't set one) plus their
   first name, styled to sit alongside the other icon buttons in
   .actions-cell. */
.profile-chip {
    display: flex;
    align-items: center;
    gap: 8px;
    padding-block: 6px;
    padding-inline: 6px 12px;
    border-radius: var(--radius-full);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text);
    text-decoration: none;
    font-size: 13px;
    font-weight: 600;
}

.profile-chip:hover {
    background: var(--color-surface-alt, var(--color-bg-soft));
}

.profile-chip-avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    object-fit: cover;
    background: var(--color-primary-soft, var(--color-border-soft));
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.profile-chip-avatar img {
    width: 100%;
    height: 100%;
    border-radius: 50%;
    object-fit: cover;
}

.profile-chip-avatar i {
    width: 16px;
    height: 16px;
    color: var(--color-primary);
}

/* position/top/left/width/max-height are all set in JS (see
   public/js/notifications.js's positionDropdown()) every time this
   opens — the bell is rarely the last button in the header's row (more
   nav buttons usually follow it), so a fixed CSS `right: 0` anchored to
   the bell's own narrow wrapper routinely pushed this off the LEFT edge
   of the screen, leaving it mostly invisible. position: fixed +
   viewport-clamped coordinates computed at open-time is the only way
   to guarantee this stays fully on-screen regardless of where the bell
   ends up sitting in the header, on any screen size — so there's no
   separate mobile breakpoint needed here either, the same JS logic
   handles both. */
.notification-dropdown {
    position: fixed;
    overflow-y: auto;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lift);
    z-index: 150;
}

.notification-dropdown-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 16px;
    border-bottom: 1px solid var(--color-border);
    font-weight: 600;
    font-size: 13px;
    color: var(--color-primary);
}

.notification-item {
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--color-border-soft);
    cursor: pointer;
    transition: background-color var(--transition-fast);
}

.notification-item:last-child {
    border-bottom: none;
}

.notification-item:hover {
    background: var(--color-bg);
}

.notification-item.unread {
    background: var(--color-accent-soft);
}

.notification-item-title {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-ink);
}

.notification-item-body {
    font-size: 12px;
    color: var(--color-ink-soft);
}

.notification-item-time {
    font-size: 11px;
    color: var(--color-ink-soft);
}

.notification-priority-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--color-danger);
    flex-shrink: 0;
}

/* ---- Shop pickup-location banner ---- */

.location-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
    padding: 12px 18px;
    border-radius: var(--radius-sm);
    margin-bottom: 20px;
    font-size: 14px;
}

.location-banner-text {
    display: flex;
    align-items: center;
    gap: 8px;
}

.location-banner.location-unset {
    background: var(--color-warning-soft);
    color: var(--color-warning);
}

.location-banner.location-set {
    background: var(--color-success-soft);
    color: var(--color-success);
}

/* ---- Emergency alert banner (admin) ---- */

@keyframes emergencyPulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.4; }
}

.emergency-banner {
    background: var(--color-danger-soft);
    border: 2px solid var(--color-danger);
    border-radius: var(--radius-lg);
    padding: 16px 20px;
    margin-bottom: 24px;
}

.emergency-banner-header {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 15px;
    font-weight: 700;
    color: var(--color-danger);
    margin-bottom: 12px;
}

.emergency-banner-icon {
    width: 20px;
    height: 20px;
    animation: emergencyPulse 1.2s ease-in-out infinite;
}

.emergency-alert-item {
    background: var(--color-surface);
    border: 1px solid var(--color-danger);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
    margin-bottom: 10px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 10px 16px;
}

.emergency-alert-item:last-child {
    margin-bottom: 0;
}

.emergency-alert-main {
    flex: 1;
    min-width: 220px;
    font-size: 13px;
}

.emergency-alert-main strong {
    font-size: 14px;
    color: var(--color-ink);
}

.emergency-alert-time {
    color: var(--color-ink-soft);
    margin-inline-start: 8px;
}

.emergency-alert-detail {
    color: var(--color-ink-soft);
    margin-top: 3px;
}

.emergency-alert-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-shrink: 0;
}

/* ---- Dispatch offer popup (driver) ---- */

.dispatch-offer-modal {
    max-width: 380px;
    border: 2px solid var(--color-accent);
    box-shadow: var(--shadow-lift);
}

.dispatch-offer-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px 0 20px;
}

.dispatch-offer-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    font-weight: 700;
    color: var(--color-accent);
}

.dispatch-offer-badge svg {
    width: 15px;
    height: 15px;
}

.dispatch-countdown-text {
    font-size: 20px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--color-warning);
}

.dispatch-countdown-bar-track {
    height: 5px;
    background: var(--color-border-soft);
    margin: 10px 20px 0 20px;
    border-radius: var(--radius-full);
    overflow: hidden;
}

.dispatch-countdown-bar-fill {
    height: 100%;
    width: 100%;
    background: var(--color-accent);
    border-radius: var(--radius-full);
}

.dispatch-countdown-bar-fill.dispatch-countdown-running {
    transition: width 20s linear, background-color 4s linear;
    width: 0%;
    background: var(--color-danger);
}

.dispatch-offer-body {
    padding: 18px 20px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.dispatch-offer-body h3 {
    font-size: 19px;
    font-weight: 700;
    color: var(--color-ink);
}

.dispatch-offer-detail {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-size: 14px;
    color: var(--color-ink-soft);
}

.dispatch-offer-detail svg {
    width: 15px;
    height: 15px;
    flex-shrink: 0;
    margin-top: 2px;
    color: var(--color-primary);
}

.dispatch-offer-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 16px 24px;
    margin-top: 6px;
    padding-top: 12px;
    border-top: 1px solid var(--color-border-soft);
}

.dispatch-offer-stats > div {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.dispatch-stat-label {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--color-ink-soft);
}

.dispatch-stat-value {
    font-size: 18px;
    font-weight: 700;
    color: var(--color-ink);
}

.dispatch-order-id {
    font-size: 12px;
    color: var(--color-ink-soft);
}

.dispatch-offer-actions {
    display: flex;
    gap: 10px;
    padding: 0 20px 20px 20px;
}

.dispatch-offer-actions button {
    flex: 1;
}

/* ---- Guided status-update stepper (driver dashboard) ---- */

.status-step-tracker {
    display: flex;
    align-items: flex-start;
    padding: 4px 8px 20px;
}

.status-step {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 8px;
    position: relative;
}

.status-step:not(:first-child)::before {
    content: '';
    position: absolute;
    top: 17px;
    inset-inline-end: 50%;
    width: 100%;
    height: 2px;
    background: var(--color-border);
    z-index: 0;
}

.status-step.is-done:not(:first-child)::before,
.status-step.is-current:not(:first-child)::before {
    background: var(--color-accent);
}

.status-step-dot {
    width: 34px;
    height: 34px;
    border-radius: var(--radius-full);
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--color-neutral-soft);
    color: var(--color-ink-soft);
    position: relative;
    z-index: 1;
    flex: none;
}

.status-step-dot svg {
    width: 16px;
    height: 16px;
}

.status-step.is-done .status-step-dot {
    background: var(--color-accent);
    color: #fff;
}

.status-step.is-current .status-step-dot {
    background: var(--color-primary);
    color: #fff;
    box-shadow: 0 0 0 4px var(--color-accent-soft);
}

.status-step-label {
    font-size: 11.5px;
    font-weight: 600;
    color: var(--color-ink-soft);
}

.status-step.is-current .status-step-label {
    color: var(--color-ink);
}

.status-step-context {
    font-size: 13.5px;
    color: var(--color-ink-soft);
    background: var(--color-bg);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
    margin: 0 0 16px;
    display: flex;
    align-items: flex-start;
    gap: 8px;
}

.status-step-context svg {
    width: 15px;
    height: 15px;
    flex: none;
    margin-top: 2px;
    color: var(--color-ink-soft);
}

.status-step-navigate {
    margin: 0 0 16px;
}

.status-step-navigate a {
    width: 100%;
    justify-content: center;
}

/* ---- POS Register ---- */

.pos-layout {
    display: grid;
    grid-template-columns: 1fr 340px;
    gap: 20px;
    align-items: start;
}
@media (max-width: 900px) {
    .pos-layout { grid-template-columns: 1fr; }
}

.pos-category-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 16px;
}
.pos-category-tab {
    font-family: var(--font-body);
    font-size: 13px;
    font-weight: 600;
    color: var(--color-ink-soft);
    background: var(--color-neutral-soft);
    border: 1px solid transparent;
    border-radius: var(--radius-full);
    padding: 7px 14px;
    cursor: pointer;
}
.pos-category-tab.active {
    color: #fff;
    background: var(--color-primary);
}

.pos-product-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: 12px;
}
.pos-product-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 12px;
    text-align: start;
    cursor: pointer;
    box-shadow: var(--shadow-sm);
    transition: box-shadow var(--transition-fast), transform var(--transition-fast);
}
.pos-product-card:hover {
    box-shadow: var(--shadow-lift);
    transform: translateY(-1px);
}
.pos-product-card img {
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    border-radius: var(--radius-sm);
    margin-bottom: 8px;
    background: var(--color-neutral-soft);
}
.pos-product-name {
    font-weight: 600;
    font-size: 14px;
    color: var(--color-ink);
    margin: 0 0 4px;
}
.pos-product-price {
    font-weight: 700;
    font-size: 14px;
    color: var(--color-accent);
    margin: 0;
}

.pos-cart {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 16px;
    position: sticky;
    top: 16px;
}
.pos-cart h3 {
    margin: 0 0 12px;
    font-size: 16px;
}
.pos-cart-line {
    display: flex;
    justify-content: space-between;
    gap: 8px;
    padding: 10px 0;
    border-bottom: 1px solid var(--color-border-soft);
    font-size: 14px;
}
.pos-cart-line-info { flex: 1; }
.pos-cart-line-name { font-weight: 600; color: var(--color-ink); }
.pos-cart-line-extras { font-size: 12px; color: var(--color-ink-soft); }
.pos-cart-line-remove {
    width: 24px; height: 24px;
    border-radius: var(--radius-full);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    flex: none;
}
.pos-cart-total {
    display: flex;
    justify-content: space-between;
    font-size: 18px;
    font-weight: 700;
    padding: 14px 0;
    margin-top: 4px;
    border-top: 1px solid var(--color-border);
}

/* A Subtotal/Discount row (see #posSubtotalRow/#posDiscountAmountRow)
   is smaller/lighter than the final Total below it — only the actual
   amount due should read as the "big number" on the register. */
.pos-cart-total.pos-cart-subtotal-row {
    font-size: 14px;
    font-weight: 500;
    color: var(--color-ink-soft);
    padding: 4px 0;
    margin-top: 0;
    border-top: none;
}

.pos-discount-row {
    display: flex;
    gap: 8px;
    margin-top: 8px;
}

.pos-discount-row select {
    flex: 1;
}

.pos-discount-row input {
    width: 90px;
}

.pos-extras-picker {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 12px 0;
}
.pos-extras-picker label {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    font-weight: 500;
}

.pos-qty-stepper {
    display: flex;
    align-items: center;
    gap: 10px;
    justify-content: center;
    margin: 12px 0;
}
.pos-qty-stepper button {
    width: 32px; height: 32px;
    border-radius: var(--radius-full);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    font-size: 16px;
    cursor: pointer;
}
.pos-qty-stepper span {
    font-size: 18px;
    font-weight: 700;
    min-width: 24px;
    text-align: center;
}

.pos-payment-methods {
    display: flex;
    gap: 8px;
    margin-bottom: 16px;
}
/* .store-payment-btn/.dine-in-payment-method-btn/.cash-direction-btn
   all share this exact look but each needs its own class — every one
   of these toggles' JS scopes its click handler to the class name
   alone, not the data attribute, so reusing a class would make two
   unrelated toggles fight over each other's active state. */
.pos-payment-method-btn,
.store-payment-btn,
.dine-in-payment-method-btn,
.cash-direction-btn {
    flex: 1;
    padding: 12px;
    border-radius: var(--radius-sm);
    border: 1.5px solid var(--color-border);
    background: var(--color-surface);
    font-weight: 600;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
}
.pos-payment-method-btn.active,
.store-payment-btn.active,
.dine-in-payment-method-btn.active,
.cash-direction-btn.active {
    border-color: var(--color-accent);
    background: var(--color-accent-soft);
    color: var(--color-accent);
}

/* The Daily/Monthly/Yearly granularity toggle on the shop Reports
   page's Sales Performance panel — a lighter-weight active state than
   .pos-payment-method-btn etc. above, since these sit inline next to
   plain text labels rather than as large tap-target cards. */
.view-btn.active {
    border-color: var(--color-accent);
    background: var(--color-accent-soft);
    color: var(--color-accent);
}

.pos-receipt {
    font-family: 'Courier New', monospace;
    font-size: 13px;
}
.pos-receipt-line {
    display: flex;
    justify-content: space-between;
    padding: 4px 0;
}
.pos-receipt-total {
    border-top: 1px dashed var(--color-border);
    margin-top: 8px;
    padding-top: 8px;
    font-weight: 700;
}

.pos-receipt-header {
    text-align: center;
    padding-bottom: 10px;
    margin-bottom: 8px;
    border-bottom: 1px dashed var(--color-border);
}
.pos-receipt-logo {
    width: 64px;
    height: 64px;
    object-fit: contain;
    margin: 0 auto 6px;
    display: block;
}
.pos-receipt-shop-name {
    font-weight: 700;
    font-size: 14px;
}
/* Same size/weight as the rest of the receipt (.pos-receipt is 13px
   regular) rather than the smaller/lighter treatment this line and
   .pos-receipt-powered-by originally had — a thermal printer's
   ~203 DPI print head doesn't have enough dots to render small, thin
   glyphs as solid black; they come out as faint, sparse dithering no
   matter what color they're nominally set to. Confirmed on a real
   receipt: the always-13px+/bold lines (shop name, item lines, Total)
   printed crisp while these two, at 11-12px regular, printed faint
   even after they were already forced to #000. Bumping size+weight
   fixes the actual cause; color was never the problem. */
.pos-receipt-shop-meta {
    font-size: 13px;
    font-weight: 600;
}

.pos-receipt-curiosity {
    text-align: center;
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px dashed var(--color-border);
}
.pos-receipt-curiosity-label {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}
.pos-receipt-curiosity-question {
    font-style: italic;
    margin: 4px 0 8px;
}
.pos-receipt-curiosity-caption {
    font-size: 11px;
    margin-top: 4px;
}

.pos-receipt-footer {
    text-align: center;
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px dashed var(--color-border);
}
.pos-receipt-qr {
    width: 96px;
    height: 96px;
    display: block;
    margin: 0 auto 6px;
    background: #fff;
}
.pos-receipt-powered-by {
    font-size: 13px;
    font-weight: 600;
}

@media print {
    /* The @page size override for this receipt (80mm thermal roll,
       auto height) lives in backend/views/cashier-dashboard.html
       instead of here — @page has no selector scoping, so putting it
       in this SHARED stylesheet would also apply it to the unrelated
       "print a single order" feature above (.print-only, used on
       admin-dashboard.html), which is a normal full-page printout and
       would break under an 80mm page size. */

    body * { visibility: hidden; }
    #posReceiptPrintArea, #posReceiptPrintArea * { visibility: visible; }
    #posReceiptPrintArea { position: absolute; top: 0; left: 0; width: 100%; padding: 8px; }

    /* Every line of the receipt prints pure black, full stop — no
       reliance on --color-ink/--color-ink-soft resolving correctly
       through inheritance and the theme override below. Belt-and-
       suspenders: on a real thermal printer, faint lines turned out to
       be caused by font-size/weight (see .pos-receipt-shop-meta's
       comment), not color — but there's no reason to leave any
       cascade/inheritance path where a line COULD come out as
       anything but solid black either. */
    .pos-receipt, .pos-receipt * {
        color: #000000 !important;
    }

    /* Same two fixes as the .print-only block above: collapse the
       full-height POS screen behind the receipt (visibility:hidden
       alone left it forcing extra blank pages), and force solid ink
       so the receipt doesn't print as pale gray under the dark theme. */
    .dashboard {
        display: none !important;
    }
    :root, [data-theme="dark"] {
        --color-ink: #000000;
        --color-ink-soft: #333333;
    }
}

/* ---- Shop-side Offers management (shop-menu.html) ---- */

.offers-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.offer-items-picker {
    max-height: 220px;
    overflow-y: auto;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.offer-item-picker-row {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 13.5px;
}

.offer-item-picker-row input[type="checkbox"] {
    width: auto;
}

.offer-item-picker-row .offer-item-qty {
    width: 56px;
    margin-inline-start: auto;
}

.offer-card {
    display: flex;
    align-items: stretch;
    gap: 16px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
}

.offer-card.offer-card-inactive {
    opacity: 0.6;
}

.offer-card-image {
    width: 140px;
    flex: none;
    object-fit: cover;
    display: block;
}

.offer-card-image.offer-card-image-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--color-bg);
    color: var(--color-border);
}

.offer-card-body {
    flex: 1;
    padding: 14px 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
    justify-content: center;
    min-width: 0;
}

.offer-card .menu-item-actions {
    border-top: none;
    border-inline-start: 1px solid var(--color-border-soft);
    flex-direction: column;
    justify-content: center;
    flex-wrap: nowrap;
    padding: 10px 16px;
}

/* ==========================================================================
   PUBLIC STOREFRONT (public/store.html)
   Deliberately its own namespace (store-*), separate from the admin/
   cashier UI's pos-* classes above — a customer-facing marketing page
   has different goals (make the food look good, build trust fast) than
   an internal work tool, even though they share the same token system.
   ========================================================================== */

.store-page {
    max-width: 1100px;
    margin: 0 auto;
    padding: 0 20px 60px;
}

.store-hero {
    position: relative;
    margin: 0 -20px 28px;
    padding: 48px 20px 40px;
    background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-soft) 100%);
    color: #fff;
    text-align: center;
}

/* A shop-uploaded banner (see store.js's shop.banner_data_url handling)
   — background-size/position live here rather than as inline styles so
   the mobile override below can actually win. Same fix and reasoning as
   brand.css's .gp-marketplace .store-hero.has-custom-hero-image: on a
   phone this box is much narrower than the wide banner image is
   designed for, so a "cover" crop centered on the image's horizontal
   middle mostly shows empty background and misses the subject, which
   typically sits right-of-center. Biasing the crop right matches what
   desktop shows instead of cropping it out. */
.store-hero.has-custom-hero-image {
    background-size: cover;
    background-position: center;
}

@media (max-width: 640px) {
    .store-hero.has-custom-hero-image {
        background-position: 75% center;
    }
}

.store-hero-back-link {
    position: absolute;
    top: 16px;
    inset-inline-start: 20px;
    padding: 7px 14px;
    font-size: 13px;
    background: rgba(255, 255, 255, 0.14);
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.3);
}

.store-hero-back-link:hover {
    background: rgba(255, 255, 255, 0.24);
}

@media (max-width: 520px) {
    .store-hero-back-link {
        position: static;
        display: inline-flex;
        margin-bottom: 14px;
    }
}

/* .store-hero-back-row wraps the back-link together with its ghost
   mascot sibling (see below) — ONLY on store.html, which is the only
   page that uses this wrapper (track.html, marketplace.html,
   my-orders.html, and group-order.html all use a bare
   .store-hero-back-link with no wrapper, so its own positioning above
   must stay untouched for them). Takes over the exact same corner
   position the bare link used to occupy, and demotes the link itself
   to a plain flex child via the more-specific override just below —
   so the link and its ghost move together as one unit in both
   layouts, without the ghost needing its own breakpoint-specific
   position math to stay visually anchored next to the button. */
.store-hero-back-row {
    position: absolute;
    top: 16px;
    inset-inline-start: 20px;
    display: flex;
    align-items: center;
    gap: 4px;
}

.store-hero-back-row .store-hero-back-link {
    position: static;
    margin-bottom: 0;
}

@media (max-width: 520px) {
    .store-hero-back-row {
        position: static;
        display: inline-flex;
        margin-bottom: 14px;
    }
}

/* ---- Ghost mascot — the GhostPatch character, simplified into a
   small friendly floating mark, nudging attention toward "All Shops"
   so a customer notices there's a whole marketplace beyond this one
   shop. Purely decorative (aria-hidden — the existing text link is
   still the one real control), so it's safe to disable entirely for
   prefers-reduced-motion without losing any functionality. ---- */

.store-hero-ghost-mascot {
    position: relative;
    width: 24px;
    height: 26px;
    margin-inline-start: 8px;
    flex: none;
}

.store-hero-ghost-arrow {
    position: absolute;
    inset-inline-start: -9px;
    top: 6px;
    width: 9px;
    height: 9px;
    color: #fff;
    opacity: 0.85;
    animation: store-hero-ghost-nudge 1.4s ease-in-out infinite;
}

.store-hero-ghost-svg {
    /* Matches the width/height attributes already on the <svg> tag in
       store.html — those are the real fallback (an <svg> with only a
       viewBox and no explicit size defaults to a fixed ~300x150 box,
       which is exactly what made this render enormous when a caching
       layer somewhere between origin and a customer's phone served
       this file stale, mid-rollout). These rules just need to not
       fight that baseline; they don't need to establish it. */
    width: 100%;
    height: 100%;
    filter: drop-shadow(0 2px 4px rgba(20, 10, 50, 0.4));
    animation: store-hero-ghost-float 2.6s ease-in-out infinite;
    transform-origin: 50% 85%;
}

@keyframes store-hero-ghost-float {
    0%, 100% { transform: translateY(0) rotate(-3deg); }
    50% { transform: translateY(-6px) rotate(3deg); }
}

@keyframes store-hero-ghost-nudge {
    0%, 100% { transform: translateX(0); opacity: 0.5; }
    50% { transform: translateX(-4px); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .store-hero-ghost-arrow,
    .store-hero-ghost-svg {
        animation: none;
    }
}

/* ---- Customer account (optional sign-in, alongside guest checkout —
   see public/js/customer-auth.js) — same top-right chip treatment as
   .store-hero-back-link gets on the opposite corner. ---- */

.store-hero-account {
    position: absolute;
    top: 16px;
    inset-inline-end: 20px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.store-hero-account-signed-in {
    display: flex;
    align-items: center;
    gap: 8px;
}

.store-hero-account-name {
    font-size: 13px;
    color: rgba(255, 255, 255, 0.85);
}

.store-hero-chip {
    padding: 7px 14px;
    font-size: 13px;
    background: rgba(255, 255, 255, 0.14);
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.3);
}

.store-hero-chip:hover {
    background: rgba(255, 255, 255, 0.24);
}

@media (max-width: 520px) {
    /* Stays in the corner on a phone rather than dropping into the flow
       above the shop's own name, where it was the largest, most
       prominent control on a page nobody opens in order to sign in.
       Icon only signed-out; the signed-in chips keep their labels but
       lose the greeting, which is the part with no action attached. */
    .store-hero-account {
        top: 12px;
        inset-inline-end: 12px;
        gap: 6px;
    }

    .store-hero-account .store-hero-chip {
        padding: 7px 9px;
        font-size: 12px;
    }

    #customerAuthSignedOut .store-hero-chip-label,
    .store-hero-account-name {
        display: none;
    }
}

/* ---- Customer sign-in / create-account modal tabs — same look as the
   store's category-filter tabs (.store-category-tab), just repurposed
   for a 2-way form switch instead of a category filter. ---- */

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

.store-hero .badge {
    background: rgba(255, 255, 255, 0.16);
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.3);
}

.store-hero h1 {
    margin: 14px 0 6px;
    font-size: 34px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: #fff;
}

.store-hero-meta {
    font-size: 14px;
    color: rgba(255, 255, 255, 0.85);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    flex-wrap: wrap;
}

.store-hero-meta a {
    color: #fff;
    text-decoration: underline;
}

.store-hero-meta svg {
    width: 14px;
    height: 14px;
}

/* ---- Offers strip ---- */

.store-offers {
    display: flex;
    gap: 14px;
    overflow-x: auto;
    padding: 4px 4px 16px;
    margin-bottom: 20px;
    scrollbar-width: thin;
}

.store-offer-card {
    flex: none;
    width: 280px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-card);
}

.store-offer-image,
.store-offer-image-placeholder {
    width: 100%;
    height: 110px;
    object-fit: cover;
    display: block;
    background: linear-gradient(135deg, var(--color-accent-soft), var(--color-warning-soft));
}

.store-offer-body {
    padding: 12px 14px 14px;
}

.store-offer-title {
    font-weight: 700;
    font-size: 14px;
    color: var(--color-ink);
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0 0 4px;
}

.store-offer-title svg {
    width: 15px;
    height: 15px;
    color: var(--color-warning);
    flex: none;
}

.store-offer-description {
    font-size: 12.5px;
    color: var(--color-ink-soft);
    margin: 0;
}

.store-offer-price-row {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin-top: 8px;
}

.store-offer-price {
    font-size: 17px;
    font-weight: 700;
    color: var(--color-accent);
}

.store-offer-price-original {
    font-size: 13px;
    color: var(--color-ink-soft);
    text-decoration: line-through;
}

.store-offer-add-btn {
    width: 100%;
    margin-top: 10px;
    justify-content: center;
}

/* ---- Category tabs ---- */

.store-category-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 18px;
    position: sticky;
    top: 0;
    background: var(--color-bg);
    padding: 10px 0;
    z-index: 5;
}

.store-category-tab {
    font-family: var(--font-body);
    font-size: 13.5px;
    font-weight: 600;
    color: var(--color-ink-soft);
    background: var(--color-surface);
    border: 1.5px solid var(--color-border);
    border-radius: var(--radius-full);
    padding: 8px 16px;
    cursor: pointer;
    transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}

.store-category-tab.active {
    color: #fff;
    background: var(--color-primary);
    border-color: var(--color-primary);
}

/* ---- Layout + product grid ---- */

.store-layout {
    display: grid;
    grid-template-columns: 1fr 340px;
    gap: 24px;
    align-items: start;
}
@media (max-width: 900px) {
    .store-layout { grid-template-columns: 1fr; }
}

.store-product-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 18px;
}

.store-product-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    text-align: start;
    cursor: pointer;
    box-shadow: var(--shadow-card);
    transition: box-shadow var(--transition-fast), transform var(--transition-fast);
    display: flex;
    flex-direction: column;
}

.store-product-card:hover {
    box-shadow: var(--shadow-lift);
    transform: translateY(-3px);
}

.store-product-image,
.store-product-image-placeholder {
    width: 100%;
    height: 150px;
    object-fit: cover;
    display: block;
    background: var(--color-neutral-soft);
}

.store-product-image-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-border);
}

.store-product-body {
    padding: 14px 16px 16px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    flex: 1;
}

.store-product-name-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 8px;
}

.store-product-name {
    font-weight: 600;
    font-size: 15px;
    color: var(--color-ink);
}

.store-product-description {
    font-size: 12.5px;
    color: var(--color-ink-soft);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    flex: 1;
}

.store-product-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 4px;
}

.store-product-price {
    font-weight: 700;
    font-size: 15px;
    color: var(--color-accent);
}

.store-product-add {
    width: 32px;
    height: 32px;
    border-radius: var(--radius-full);
    border: none;
    background: var(--color-primary);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
}

.store-product-add svg {
    width: 16px;
    height: 16px;
}

/* ---- Storefront layouts (/store/<slug>) ----
   Three ways of browsing the same catalog, one per shop, picked from
   shops.storefront_layout or derived from its category — see
   backend/utils/shopCategories.js, and store.js, which puts
   .storefront-<layout> on #storeApp before the first render.

   'menu' is everything ABOVE this comment: the original layout, which
   deliberately has no rules of its own here, so a shop on it renders
   exactly as it did before these blocks existed. The two blocks below
   override only what genuinely differs.

   Every selector is scoped under .storefront-market/.storefront-retail
   on purpose — marketplace.html and group-order.html reuse .store-page
   and the .store-product-* classes for their own grids, and neither
   page ever carries a layout class, so neither can be reached from
   here. */

/* Market (Grocery, Pharmacy): a few hundred packaged SKUs nobody
   browses by photograph. The gallery becomes one scannable list —
   thumbnail, name, price, add — at roughly four times the rows per
   screen. The card's markup is untouched; only its axis, its chrome
   and the size of its image box change. */
.storefront-market .store-product-grid {
    grid-template-columns: 1fr;
    gap: 0;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-card);
}

.storefront-market .store-product-card {
    flex-direction: row;
    align-items: center;
    gap: 12px;
    padding: 8px 12px;
    border: none;
    border-bottom: 1px solid var(--color-border);
    border-radius: 0;
    box-shadow: none;
}

.storefront-market .store-product-card:last-child {
    border-bottom: none;
}

/* A row that lifts reads as a card peeling off the list. It tints
   instead, which is what a pressable row does everywhere else. */
.storefront-market .store-product-card:hover {
    transform: none;
    box-shadow: none;
    background: var(--color-neutral-soft);
}

.storefront-market .store-product-image,
.storefront-market .store-product-image-placeholder {
    width: 52px;
    height: 52px;
    flex: none;
    border-radius: var(--radius-sm);
}

/* Name left, price and + pinned right. It was the body's own column
   axis that stacked them, so that flips here rather than the markup. */
.storefront-market .store-product-body {
    flex-direction: row;
    align-items: center;
    gap: 12px;
    padding: 0;
}

.storefront-market .store-product-name-row {
    flex: 1;
    min-width: 0;
}

.storefront-market .store-product-name {
    display: block;
    font-size: 14px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Two lines of prose per row would undo the density this layout exists
   for — the description is still there in the item's own modal. */
.storefront-market .store-product-description {
    display: none;
}

.storefront-market .store-product-footer {
    margin-top: 0;
    gap: 12px;
    flex: none;
}

/* On a phone the dual-currency price ("$4.50 (405,000 L.L.)") is wider
   than the name it sits beside, and since the price can't shrink it was
   the NAME that got ellipsed away to "Ba..." — the one thing a shopper
   is actually scanning for. Below 600px the row keeps its thumbnail but
   stacks name over price, so both are readable and the + still lands on
   the right where a thumb expects it. */
@media (max-width: 600px) {
    .storefront-market .store-product-body {
        flex-wrap: wrap;
        gap: 2px 12px;
    }

    .storefront-market .store-product-name-row {
        flex: 1 0 100%;
    }

    .storefront-market .store-product-footer {
        flex: 1 0 100%;
        justify-content: space-between;
    }
}

/* Retail: few items, and the photograph IS the product. Portrait
   images, no card chrome competing with them, and two-up on a phone
   the way a clothing rail is actually shown. */
.storefront-retail .store-product-grid {
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 24px 16px;
}

.storefront-retail .store-product-card {
    background: none;
    border: none;
    border-radius: 0;
    box-shadow: none;
    overflow: visible;
}

.storefront-retail .store-product-image,
.storefront-retail .store-product-image-placeholder {
    height: auto;
    aspect-ratio: 3 / 4;
    border-radius: var(--radius);
}

/* With no card edge drawn any more there is nothing for a lift to lift,
   so the hover lands on the photo itself. */
.storefront-retail .store-product-card:hover {
    transform: none;
    box-shadow: none;
}

.storefront-retail .store-product-card:hover .store-product-image {
    filter: brightness(0.94);
}

.storefront-retail .store-product-body {
    padding: 10px 2px 0;
    gap: 4px;
}

.storefront-retail .store-product-description {
    -webkit-line-clamp: 1;
}

@media (max-width: 600px) {
    .storefront-retail .store-product-grid {
        grid-template-columns: repeat(2, 1fr);
        gap: 20px 12px;
    }

    .storefront-retail .store-product-name {
        font-size: 14px;
    }

    /* Two columns leave each price about 120px, which the dual-currency
       string overruns — it wraps to two lines rather than shoving the +
       button off the tile, so this just keeps that wrap compact. */
    .storefront-retail .store-product-price {
        font-size: 13px;
        line-height: 1.3;
    }
}

/* ---- Marketplace directory (/marketplace) ----
   Reuses .store-hero/.store-category-tabs/.store-product-card/
   .store-product-* wherever the shape already matches a shop-directory
   card — the rules below only cover what's genuinely different: the
   search+distance row in the hero, and a shop card's category/rating/
   address/featured-ribbon content. */

.marketplace-hero {
    padding-bottom: 28px;
}

.marketplace-search-row {
    display: flex;
    gap: 10px;
    max-width: 560px;
    margin: 22px auto 0;
}

.marketplace-search-input-wrap input {
    border: none;
}

.marketplace-distance-btn {
    background: rgba(255, 255, 255, 0.92);
    flex: none;
    white-space: nowrap;
}

@media (max-width: 560px) {
    .marketplace-search-row {
        flex-direction: column;
    }

    .marketplace-distance-btn {
        justify-content: center;
    }
}

.marketplace-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 18px;
    margin-top: 20px;
}

/* ---- Marketplace: Announcements carousel — full-width, auto-advancing
   platform banners for upcoming events/marketing, above the shop-level
   Hot Offers strip below. Hand-rolled flexbox carousel (see
   marketplace.js) — the outer element clips to one slide's width via
   overflow: hidden, the track shifts by whole container-widths. ---- */

.marketplace-announcements-carousel {
    position: relative;
    width: 100%;
    aspect-ratio: 21 / 7;
    max-height: 320px;
    border-radius: 24px;
    overflow: hidden;
    margin: 24px 0;
    background: var(--color-neutral-soft);
}

.marketplace-carousel-track {
    display: flex;
    width: 100%;
    height: 100%;
    transition: transform 0.6s cubic-bezier(0.65, 0, 0.35, 1);
}

.marketplace-carousel-slide {
    position: relative;
    flex: 0 0 100%;
    width: 100%;
    height: 100%;
    display: block;
    text-decoration: none;
    color: inherit;
}

.marketplace-carousel-image,
.marketplace-carousel-image-placeholder {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    background: linear-gradient(135deg, var(--gp-purple-700), var(--gp-purple-500));
}

.marketplace-carousel-caption {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 40px 28px 24px;
    background: linear-gradient(to top, rgba(8, 6, 15, 0.85), transparent);
    color: #fff;
    pointer-events: none;
}

.marketplace-carousel-title {
    font-family: var(--font-display);
    text-transform: uppercase;
    letter-spacing: 0.01em;
    font-size: clamp(18px, 2.6vw, 28px);
    margin: 0 0 4px;
}

.marketplace-carousel-description {
    font-size: 13.5px;
    color: rgba(255, 255, 255, 0.85);
    margin: 0;
    max-width: 560px;
}

.marketplace-carousel-arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: rgba(8, 6, 15, 0.45);
    color: #fff;
    border: none;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.15s ease;
}

.marketplace-carousel-arrow:hover {
    background: rgba(8, 6, 15, 0.7);
}

.marketplace-carousel-arrow-prev { left: 14px; }
.marketplace-carousel-arrow-next { right: 14px; }

.marketplace-carousel-arrow svg {
    width: 18px;
    height: 18px;
}

.marketplace-carousel-dots {
    position: absolute;
    bottom: 14px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 6px;
}

.marketplace-carousel-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.45);
    border: none;
    padding: 0;
    cursor: pointer;
    transition: background 0.15s ease, transform 0.15s ease;
}

.marketplace-carousel-dot.active {
    background: #fff;
    transform: scale(1.3);
}

@media (max-width: 640px) {
    .marketplace-announcements-carousel {
        aspect-ratio: 4 / 3;
        border-radius: 18px;
    }

    .marketplace-carousel-caption {
        padding: 28px 18px 18px;
    }
}

/* ---- Marketplace: "Hot Offers" strip — a distinct promotional band,
   deliberately louder than the plain shop grid below it (gradient
   background, pulsing discount badge, hover lift) so it reads as a
   dedicated marketing placement rather than just more shop cards. ---- */

.marketplace-offers-section {
    position: relative;
    margin: 24px 0 28px;
    padding: 22px 20px 18px;
    border-radius: 24px;
    background: linear-gradient(135deg, var(--gp-purple-800), var(--gp-purple-600));
    overflow: hidden;
}

.marketplace-offers-section::before {
    content: '';
    position: absolute;
    top: -110px;
    inset-inline-end: -60px;
    width: 320px;
    height: 320px;
    background: radial-gradient(circle, rgba(140, 116, 245, 0.45), transparent 70%);
    pointer-events: none;
}

.marketplace-offers-header {
    position: relative;
    margin-bottom: 14px;
}

.marketplace-offers-header h2 {
    display: flex;
    align-items: center;
    gap: 8px;
    color: #fff;
    font-family: var(--font-display);
    text-transform: uppercase;
    letter-spacing: 0.01em;
    font-size: 19px;
    margin: 0 0 3px;
}

.marketplace-offers-header h2 svg {
    width: 19px;
    height: 19px;
    color: #FFB648;
}

.marketplace-offers-header p {
    color: var(--gp-purple-200);
    font-size: 12.5px;
    margin: 0;
}

.marketplace-offers-strip {
    position: relative;
    display: flex;
    gap: 14px;
    overflow-x: auto;
    padding: 2px 2px 6px;
    scroll-snap-type: x proximity;
    scrollbar-width: thin;
}

.marketplace-offer-card {
    flex: none;
    scroll-snap-align: start;
    width: 210px;
    background: var(--color-surface);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: 0 10px 24px rgba(20, 10, 50, 0.28);
    text-decoration: none;
    color: inherit;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.marketplace-offer-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 16px 30px rgba(20, 10, 50, 0.38);
}

.marketplace-offer-image-wrap {
    position: relative;
}

.marketplace-offer-image,
.marketplace-offer-image-placeholder {
    width: 100%;
    height: 96px;
    object-fit: cover;
    display: block;
    background: linear-gradient(135deg, var(--color-accent-soft), #FFE8CC);
}

.marketplace-offer-image-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-accent);
}

.marketplace-offer-image-placeholder svg {
    width: 22px;
    height: 22px;
}

.marketplace-offer-badge {
    position: absolute;
    top: 8px;
    inset-inline-end: 8px;
    background: var(--color-danger);
    color: #fff;
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: 0.02em;
    padding: 3px 9px;
    border-radius: var(--radius-full);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
    animation: marketplace-offer-badge-pulse 2s ease-in-out infinite;
}

@keyframes marketplace-offer-badge-pulse {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.08); }
}

@media (prefers-reduced-motion: reduce) {
    .marketplace-offer-badge { animation: none; }
}

.marketplace-offer-body {
    padding: 9px 11px 12px;
}

.marketplace-offer-shop {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 10.5px;
    font-weight: 700;
    color: var(--color-accent);
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

.marketplace-offer-shop svg {
    width: 11px;
    height: 11px;
}

.marketplace-offer-title {
    font-weight: 700;
    font-size: 13.5px;
    color: var(--color-ink);
    margin: 4px 0 0;
    line-height: 1.3;
}

.marketplace-offer-description {
    font-size: 12px;
    color: var(--color-ink-soft);
    margin: 4px 0 0;
}

.marketplace-offer-price-row {
    display: flex;
    align-items: baseline;
    gap: 6px;
    margin-top: 6px;
}

.marketplace-offer-price {
    font-size: 16px;
    font-weight: 800;
    color: var(--color-accent);
}

.marketplace-offer-price-original {
    font-size: 12px;
    color: var(--color-ink-soft);
    text-decoration: line-through;
}

/* ---- Marketplace: "Top Sellers" strip — same promotional-band shape
   as Hot Offers above, but reads as a leaderboard (deeper gradient,
   gold/silver/bronze rank badges) rather than a deal — the marketing
   hook here is "this is what people are actually ordering", not a
   discount. ---- */

.marketplace-top-products-section {
    position: relative;
    margin: 24px 0 28px;
    padding: 22px 20px 18px;
    border-radius: 24px;
    background: linear-gradient(135deg, var(--gp-purple-900), var(--gp-purple-700) 85%);
    overflow: hidden;
}

.marketplace-top-products-section::before {
    content: '';
    position: absolute;
    bottom: -100px;
    inset-inline-start: -60px;
    width: 300px;
    height: 300px;
    background: radial-gradient(circle, rgba(240, 169, 59, 0.3), transparent 70%);
    pointer-events: none;
}

.marketplace-top-products-header {
    position: relative;
    margin-bottom: 14px;
}

.marketplace-top-products-header h2 {
    display: flex;
    align-items: center;
    gap: 8px;
    color: #fff;
    font-family: var(--font-display);
    text-transform: uppercase;
    letter-spacing: 0.01em;
    font-size: 19px;
    margin: 0 0 3px;
}

.marketplace-top-products-header h2 svg {
    width: 19px;
    height: 19px;
    color: #F0A93B;
}

.marketplace-top-products-header p {
    color: var(--gp-purple-200);
    font-size: 12.5px;
    margin: 0;
}

.marketplace-top-products-strip {
    position: relative;
    display: flex;
    gap: 14px;
    overflow-x: auto;
    padding: 2px 2px 6px;
    scroll-snap-type: x proximity;
    scrollbar-width: thin;
}

.marketplace-top-product-card {
    flex: none;
    scroll-snap-align: start;
    width: 170px;
    background: var(--color-surface);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: 0 10px 24px rgba(20, 10, 50, 0.28);
    text-decoration: none;
    color: inherit;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.marketplace-top-product-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 16px 30px rgba(20, 10, 50, 0.38);
}

.marketplace-top-product-image-wrap {
    position: relative;
}

.marketplace-top-product-image,
.marketplace-top-product-image-placeholder {
    width: 100%;
    height: 96px;
    object-fit: cover;
    display: block;
    background: linear-gradient(135deg, var(--color-accent-soft), #FFE8CC);
}

.marketplace-top-product-image-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-accent);
}

.marketplace-top-product-image-placeholder svg {
    width: 22px;
    height: 22px;
}

.marketplace-top-product-rank {
    position: absolute;
    top: 8px;
    inset-inline-start: 8px;
    font-size: 11px;
    font-weight: 800;
    letter-spacing: 0.02em;
    padding: 3px 8px;
    border-radius: var(--radius-full);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
    color: #fff;
}

.marketplace-top-product-rank-1 {
    background: linear-gradient(135deg, #FFD966, #E0A937);
    color: #4A3200;
}

.marketplace-top-product-rank-2 {
    background: linear-gradient(135deg, #E2E4EA, #B9BCC6);
    color: #33353C;
}

.marketplace-top-product-rank-3 {
    background: linear-gradient(135deg, #D7A16A, #A96A34);
}

.marketplace-top-product-rank-other {
    background: rgba(32, 41, 66, 0.6);
}

.marketplace-top-product-body {
    padding: 9px 11px 12px;
}

.marketplace-top-product-shop {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 10.5px;
    font-weight: 700;
    color: var(--color-accent);
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

.marketplace-top-product-shop svg {
    width: 11px;
    height: 11px;
}

.marketplace-top-product-name {
    font-weight: 700;
    font-size: 13.5px;
    color: var(--color-ink);
    margin: 4px 0 0;
    line-height: 1.3;
}

.marketplace-top-product-price {
    display: block;
    font-size: 15px;
    font-weight: 800;
    color: var(--color-accent);
    margin-top: 6px;
}

.shop-card {
    position: relative;
    /* .store-product-card was only ever a <button> before this reused
       it on an <a> — reset the browser's default link styling so card
       text doesn't render blue/underlined. */
    text-decoration: none;
    color: inherit;
}

.shop-card-category,
.shop-card-view-btn {
    text-decoration: none;
}

.shop-card-featured-ribbon {
    position: absolute;
    top: 10px;
    inset-inline-start: 10px;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: 11px;
    font-weight: 700;
    color: var(--color-warning);
    background: var(--color-warning-soft);
    padding: 3px 9px;
    border-radius: var(--radius-full);
}

.shop-card-featured-ribbon svg {
    width: 11px;
    height: 11px;
}

.shop-card-meta-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    font-size: 12.5px;
    color: var(--color-ink-soft);
}

.shop-card-category {
    background: var(--color-neutral-soft);
    padding: 2px 8px;
    border-radius: var(--radius-full);
}

.shop-card-rating {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    white-space: nowrap;
}

.marketplace-star {
    width: 13px;
    height: 13px;
    color: var(--color-warning);
    fill: var(--color-warning);
}

.marketplace-rating-count {
    color: var(--color-ink-soft);
}

.shop-card-address {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 12.5px;
    color: var(--color-ink-soft);
}

.shop-card-address svg {
    width: 13px;
    height: 13px;
    flex: none;
}

/* Was reserved for open/closed + delivery-fee/ETA/minimum-order badges
   until real per-shop data existed for them — now used by the "Closed"
   pill below (see marketplace.js's shopCard()). Zero height/margin so
   an empty row (an open shop, no badge) never shows as a visible gap. */
.shop-card-badge-row:empty {
    display: none;
}

.shop-card-closed-badge {
    display: inline-flex;
    align-items: center;
    font-size: 11px;
    font-weight: 700;
    color: var(--color-danger);
    background: var(--color-danger-soft);
    padding: 2px 8px;
    border-radius: var(--radius-full);
}

.shop-card-view-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    align-self: flex-start;
    margin-top: 4px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--color-accent);
}

.shop-card-view-btn svg {
    width: 13px;
    height: 13px;
}

/* ---- Cart panel ---- */

.store-cart {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: 18px;
    position: sticky;
    top: 70px;
    box-shadow: var(--shadow-card);
}

/* Floating "jump to cart" bar — mobile only, see store.html's comment on
   why: .store-layout collapses to one column under 900px, so the cart
   (and its checkout button) would otherwise be below the entire product
   grid with nothing to scroll to it. Hidden on desktop, where the cart
   panel is already sticky in the sidebar. */
.store-mobile-checkout-bar {
    display: none;
}

@media (max-width: 900px) {
    .store-mobile-checkout-bar {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        position: fixed;
        left: 12px;
        right: 12px;
        bottom: calc(12px + env(safe-area-inset-bottom, 0px));
        z-index: 30;
        background: var(--color-primary);
        color: #fff;
        border: none;
        border-radius: var(--radius-lg);
        padding: 14px 18px;
        font-family: var(--font-body);
        font-size: 14px;
        font-weight: 600;
        cursor: pointer;
        box-shadow: var(--shadow-lift);
    }

    /* Slides up from under the screen edge rather than appearing from
       nowhere, so the first item added visibly produces something. Only
       transform and opacity move — the two properties a phone can
       animate without repainting the page underneath, which matters on
       the cheap Android hardware most of these orders come from.

       The empty state is a CLASS rather than the hidden attribute on
       purpose. This stylesheet sets [hidden] { display: none !important }
       globally and deliberately (see the note at the top of this file
       about the bug it has fixed twice), and display:none cannot be
       transitioned anyway. store.js keeps the button out of the tab
       order and the accessibility tree while it is away, which is what
       the attribute was really there for. */
    .store-mobile-checkout-bar {
        transform: translateY(0);
        opacity: 1;
        transition: transform var(--transition), opacity var(--transition), visibility var(--transition);
    }

    .store-mobile-checkout-bar.is-away {
        transform: translateY(calc(100% + 24px));
        opacity: 0;
        pointer-events: none;
        visibility: hidden;
    }

    .store-mobile-checkout-cta {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        flex-shrink: 0;
    }

    .store-mobile-checkout-cta svg {
        width: 15px;
        height: 15px;
    }
}

.store-cart h3 {
    margin: 0 0 14px;
    font-size: 17px;
}

.store-cart-line {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    padding: 11px 0;
    border-bottom: 1px solid var(--color-border-soft);
    font-size: 14px;
}

.store-cart-line-info { flex: 1; min-width: 0; }
.store-cart-line-name { font-weight: 600; color: var(--color-ink); }
.store-cart-line-extras { font-size: 12px; color: var(--color-ink-soft); }

.store-cart-line-remove {
    width: 24px;
    height: 24px;
    border-radius: var(--radius-full);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
}

.store-cart-total {
    display: flex;
    justify-content: space-between;
    font-size: 19px;
    font-weight: 700;
    padding: 16px 0;
    margin-top: 4px;
    border-top: 1px solid var(--color-border);
}

/* Size/colour chips in the item picker (see store.js's
   renderVariantPicker). A required, single choice, so they read as
   pressable chips rather than the extras' checkbox list below — and a
   sold-out one stays visible but unpickable, because "they had it in
   blue but not my size" is information a shopper wants. */
.store-variant-picker {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 14px 0 4px;
}

.store-variant-chip {
    font-family: var(--font-body);
    font-size: 13.5px;
    font-weight: 600;
    color: var(--color-ink);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-full);
    padding: 7px 14px;
    cursor: pointer;
    transition: border-color var(--transition-fast), background var(--transition-fast);
}

.store-variant-chip:hover:not(:disabled) {
    border-color: var(--color-primary);
}

.store-variant-chip.selected {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: #fff;
}

.store-variant-chip:disabled {
    cursor: not-allowed;
    opacity: 0.5;
    text-decoration: line-through;
}

.store-variant-chip-price {
    font-weight: 500;
    opacity: 0.75;
}

/* inline-block, not just text-decoration:none — a parent's line-through
   paints straight through an inline child regardless, and "Sold out"
   struck out reads as though it were the thing cancelled. */
.store-variant-chip-note {
    display: inline-block;
    font-weight: 500;
    font-size: 12px;
    opacity: 0.8;
}

.store-extras-picker {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 14px 0;
}
.store-extras-picker label {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    font-weight: 500;
}

.store-nutrition-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 14px;
    margin: 14px 0;
    padding: 10px 12px;
    background: var(--color-neutral-soft);
    border-radius: var(--radius-sm);
    font-size: 13px;
    color: var(--color-ink-soft);
}
.store-nutrition-summary span:first-child {
    display: flex;
    align-items: center;
    gap: 4px;
    font-weight: 600;
    color: var(--color-ink);
}
.store-nutrition-summary svg {
    width: 14px;
    height: 14px;
}

.store-qty-stepper {
    display: flex;
    align-items: center;
    gap: 14px;
    justify-content: center;
    margin: 16px 0;
}
.store-qty-stepper button {
    width: 34px;
    height: 34px;
    border-radius: var(--radius-full);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    font-size: 17px;
    cursor: pointer;
}
.store-qty-stepper span {
    font-size: 19px;
    font-weight: 700;
    min-width: 26px;
    text-align: center;
}

.store-badge-popular {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 10.5px;
    font-weight: 700;
    color: var(--color-warning);
    background: var(--color-warning-soft);
    border-radius: var(--radius-full);
    padding: 3px 8px;
    align-self: flex-start;
}
.store-badge-popular svg { width: 10px; height: 10px; }

/* ---- Order tracking timeline ---- */

.track-timeline {
    display: flex;
    flex-direction: column;
}

.track-step {
    display: flex;
    gap: 14px;
    position: relative;
    padding-bottom: 28px;
}
.track-step:last-child {
    padding-bottom: 0;
}
.track-step::before {
    content: "";
    position: absolute;
    inset-inline-start: 15px;
    top: 32px;
    bottom: 0;
    width: 2px;
    background: var(--color-border);
}
.track-step:last-child::before {
    display: none;
}
.track-step.done::before {
    background: var(--color-accent);
}

.track-step-dot {
    flex: none;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--color-neutral-soft);
    color: var(--color-ink-soft);
    border: 2px solid var(--color-border);
}
.track-step-dot svg { width: 16px; height: 16px; }

.track-step.done .track-step-dot {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: #fff;
}
.track-step.current .track-step-dot {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: #fff;
    box-shadow: 0 0 0 4px var(--color-accent-soft);
}

.track-step-body {
    padding-top: 4px;
}
.track-step-label {
    font-weight: 600;
    font-size: 14.5px;
    color: var(--color-ink);
}
.track-step.done .track-step-label,
.track-step.current .track-step-label {
    color: var(--color-ink);
}
.track-step:not(.done):not(.current) .track-step-label {
    color: var(--color-ink-soft);
}
.track-step-time {
    font-size: 12.5px;
    color: var(--color-ink-soft);
    margin-top: 2px;
}

.track-cancelled-banner {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 16px;
    padding: 12px 14px;
    background: var(--color-danger-soft);
    color: var(--color-danger);
    border-radius: var(--radius-sm);
    font-weight: 600;
    font-size: 14px;
}

.track-item-row {
    display: flex;
    justify-content: space-between;
    padding: 8px 0;
    border-bottom: 1px solid var(--color-border-soft);
    font-size: 14px;
}
.track-item-row:last-child { border-bottom: none; }

.scheduled-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-top: 4px;
    font-size: 11px;
    font-weight: 600;
    color: var(--color-warning);
    background: var(--color-warning-soft);
    border-radius: var(--radius-full);
    padding: 2px 8px;
    white-space: nowrap;
}
.scheduled-badge svg { width: 10px; height: 10px; }

/* ---- Reviews (shop moderation page) ---- */

.reviews-list {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.review-card {
    display: flex;
    gap: 14px;
    padding: 14px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
}

.review-photo {
    flex: none;
    width: 72px;
    height: 72px;
    object-fit: cover;
    border-radius: var(--radius-sm);
}

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

.review-header-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    font-size: 14px;
}

.review-stars {
    color: var(--color-warning);
    letter-spacing: 1px;
}

.review-item-name {
    font-size: 12.5px;
    color: var(--color-ink-soft);
    margin-top: 2px;
}

.review-text {
    font-size: 14px;
    margin: 8px 0 0;
}

.review-meta {
    font-size: 12px;
    color: var(--color-ink-soft);
    margin-top: 8px;
}

.review-actions {
    display: flex;
    gap: 10px;
    margin-top: 10px;
}

/* ---- Reviews (storefront display) ---- */

.store-rating-summary {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    color: var(--color-warning);
    font-weight: 600;
}
.store-rating-summary svg { width: 12px; height: 12px; }

.store-reviews-section {
    margin-top: 18px;
    padding-top: 16px;
    border-top: 1px solid var(--color-border);
}

.store-review-item {
    display: flex;
    gap: 10px;
    padding: 10px 0;
    border-bottom: 1px solid var(--color-border-soft);
}
.store-review-item:last-child { border-bottom: none; }

.store-review-photo {
    flex: none;
    width: 48px;
    height: 48px;
    object-fit: cover;
    border-radius: var(--radius-sm);
}

.store-write-review-form {
    margin-top: 14px;
    padding-top: 14px;
    border-top: 1px dashed var(--color-border);
}

.store-star-picker {
    display: flex;
    gap: 4px;
    font-size: 24px;
    color: var(--color-border);
    cursor: pointer;
}
.store-star-picker .active {
    color: var(--color-warning);
}

/* ---- Upsell strip ("Goes well with") ---- */

.store-upsell-strip {
    margin: 14px 0;
    padding-top: 14px;
    border-top: 1px dashed var(--color-border);
}

.store-upsell-card {
    flex: none;
    width: 110px;
    text-align: center;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 8px;
    cursor: pointer;
}
.store-upsell-card img,
.store-upsell-card .store-upsell-placeholder {
    width: 100%;
    aspect-ratio: 1;
    object-fit: cover;
    border-radius: var(--radius-sm);
    background: var(--color-neutral-soft);
    margin-bottom: 6px;
}
.store-upsell-name {
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.store-upsell-price {
    font-size: 11.5px;
    color: var(--color-accent);
    font-weight: 600;
}

/* ---- Language toggle (see public/js/i18n.js) -------------------------
   A small segmented EN / عربي control rendered into any element marked
   [data-i18n-toggle]. Each operational window carries its own, because
   the language choice is per-window and per-device, not per-account. */
.lang-toggle {
    display: inline-flex;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    overflow: hidden;
    flex-shrink: 0;
}

.lang-toggle-btn {
    appearance: none;
    border: 0;
    background: transparent;
    color: var(--color-ink-soft);
    font: inherit;
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1;
    padding: 7px 11px;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.lang-toggle-btn + .lang-toggle-btn {
    border-inline-start: 1px solid var(--color-border);
}

.lang-toggle-btn:hover {
    background: var(--color-surface-soft, rgba(0, 0, 0, 0.04));
    color: var(--color-ink);
}

.lang-toggle-btn.active {
    background: var(--color-primary);
    color: #fff;
}

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

/* ---- "Still needs filling" highlight (dispatcher New Order form) -----
   A live indicator of which required fields are still empty, so a
   dispatcher taking an order over the phone can see at a glance what's
   left rather than discovering it at submit. Applied to the .form-field
   wrapper so the label turns red with the control.

   Deliberately a border + tint rather than an error message per field:
   an empty field that simply hasn't been reached yet isn't an error,
   and a form that shouts a validation message at every untouched box
   trains people to ignore it. */
.form-field.field-missing > label {
    color: var(--color-danger);
}

.form-field.field-missing input,
.form-field.field-missing select,
.form-field.field-missing textarea {
    border-color: var(--color-danger);
    background: var(--color-danger-soft, rgba(220, 38, 38, 0.05));
}

.form-field.field-missing input:focus,
.form-field.field-missing select:focus,
.form-field.field-missing textarea:focus {
    outline-color: var(--color-danger);
    border-color: var(--color-danger);
}

/* A field that stops being required (the zone once a customer location
   has been pasted) drops the highlight entirely rather than turning
   some other colour — nothing to do means nothing to look at. */

/* A product's "was" price, struck through beside the real one (see
   menu_items.original_price). Display only — what a customer is
   actually charged is always .store-product-price. Sits immediately
   after that price so the pair reads as one unit, rather than being
   pushed to the far end by .store-product-footer's space-between. */
.store-product-price-original {
    font-size: 12.5px;
    color: var(--color-ink-soft);
    text-decoration: line-through;
    margin-inline-start: 6px;
}

/* Country-code prefix pinned to a phone input (driver request forms).
   Shown rather than silently prepended so somebody typing a Lebanese
   number can see what's being stored — and so an international number
   typed with its own + is visibly the exception. */
.phone-country-code {
    display: inline-flex;
    align-items: center;
    padding: 0 10px;
    border: 1px solid var(--color-border);
    border-inline-end: 0;
    border-start-start-radius: var(--radius-sm);
    border-end-start-radius: var(--radius-sm);
    background: var(--color-surface-soft, rgba(0, 0, 0, 0.04));
    color: var(--color-ink-soft);
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
}

/* ---- Storefront item dialog: what you're actually buying ------------
   The dialog used to open with a quantity stepper and a cross-sell but
   no price for the item itself, so the only figure on screen belonged
   to a different product. This block is the item's own photo, price and
   description, kept above everything optional. */
.store-item-modal-summary {
    display: flex;
    gap: 14px;
    align-items: flex-start;
    margin-bottom: 4px;
}

.store-item-modal-image {
    width: 96px;
    height: 96px;
    border-radius: var(--radius);
    object-fit: cover;
    flex: none;
    background: var(--color-neutral-soft);
}

.store-item-modal-facts {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}

.store-item-modal-price {
    font-size: 20px;
    font-weight: 700;
    color: var(--color-primary);
}

.store-item-modal-price-original {
    font-size: 14px;
    color: var(--color-ink-soft);
    text-decoration: line-through;
}

.store-item-modal-facts .menu-item-description {
    margin: 0;
}

/* The stepper and the running line total share a row, so the number
   being charged sits right next to the control that changes it. */
.store-qty-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    margin: 16px 0;
}

.store-qty-row .store-qty-stepper {
    margin: 0;
    justify-content: flex-start;
}

.store-item-modal-line-total {
    font-size: 18px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--color-ink);
}

/* ---- Product card: add, then adjust, without opening anything ------- */
.store-product-stepper {
    display: flex;
    align-items: center;
    gap: 2px;
    flex: none;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-full);
    background: var(--color-surface);
    padding: 2px;
}

.store-product-step {
    width: 30px;
    height: 30px;
    border-radius: var(--radius-full);
    border: none;
    background: var(--color-accent-soft);
    color: var(--color-primary);
    font-size: 17px;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    /* Keeps the visible control compact while still meeting the 44px
       touch minimum, which the old 32px tap targets did not. */
    position: relative;
}

.store-product-step::after {
    content: "";
    position: absolute;
    inset: -7px;
}

.store-product-step:hover {
    background: var(--color-accent);
    color: #fff;
}

.store-product-step-qty {
    min-width: 26px;
    text-align: center;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--color-ink);
}

/* .store-product-add is now a real button rather than a decorative
   span, so it needs its own hit area and focus state. */
.store-product-add {
    cursor: pointer;
    position: relative;
}

.store-product-add::after {
    content: "";
    position: absolute;
    inset: -6px;
}

.store-product-add:hover {
    background: var(--color-accent);
}

.store-product-add:focus-visible,
.store-product-step:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}

/* ---- Storefront menu search ----------------------------------------
   Sits with the category tabs in the sticky strip, so it stays reachable
   while scrolling a long menu — which is the only kind of menu it is
   rendered for (see store.js's SEARCH_MIN_ITEMS). */
.store-search {
    display: flex;
    align-items: center;
    gap: 8px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-full);
    padding: 0 14px;
    margin-bottom: 10px;
    position: sticky;
    top: 0;
    z-index: 6;
}

.store-search svg {
    width: 17px;
    height: 17px;
    color: var(--color-ink-soft);
    flex: none;
}

.store-search input {
    flex: 1;
    min-width: 0;
    border: none;
    background: none;
    padding: 13px 0;
    font-family: var(--font-body);
    font-size: 15px;
    color: var(--color-ink);
}

.store-search input:focus {
    outline: none;
}

.store-search:focus-within {
    border-color: var(--color-accent);
    box-shadow: 0 0 0 3px var(--color-accent-soft);
}

/* The native search clear is inconsistent across browsers, so the page
   draws its own and hides theirs. */
.store-search input::-webkit-search-cancel-button {
    display: none;
}

.store-search button {
    border: none;
    background: none;
    padding: 6px;
    cursor: pointer;
    color: var(--color-ink-soft);
    display: flex;
    align-items: center;
    flex: none;
}

.store-search button:hover {
    color: var(--color-ink);
}

/* The tabs sit directly under the search box, so their own sticky stop
   has to clear it rather than overlapping. Scoped to :not([hidden])
   because a display:none sibling still matches "+", which would leave a
   56px sticky gap above the tabs on the short menus that get no search
   box at all. */
.store-search:not([hidden]) + .store-category-tabs {
    top: 56px;
}

/* The shop's own rating of a driver, shown in the completed-orders
   Driver column. Muted rather than gold: it is a record of something
   already done, not a control, and it should not out-shout the order
   data around it. */
.shop-driver-rating {
    font-size: 12px;
    color: var(--color-warning);
    letter-spacing: 1px;
    white-space: nowrap;
}

/* A driver's two ratings, stacked and labelled by who gave them —
   C(ustomers) and S(hops). Deliberately not merged into one figure; see
   schema.sql's shop_driver_reviews for why. */
.driver-rating-pair {
    display: flex;
    flex-direction: column;
    gap: 2px;
    font-size: 12px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.driver-rating-pair small {
    color: var(--color-ink-soft);
}

.driver-rating-none {
    color: var(--color-ink-soft);
}

/* ---- How a shop rates a driver ---------------------------------------
   Three named choices rather than a 1-5 scale: a shop knows whether the
   driver was pleasant or rude, and does not have a view on whether that
   is a 2 or a 3. */
.verdict-picker {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.verdict-option {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 12px 14px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    background: var(--color-surface);
    color: var(--color-ink);
    font-family: var(--font-body);
    font-size: 15px;
    text-align: start;
    cursor: pointer;
    transition: border-color var(--transition-fast), background var(--transition-fast);
}

.verdict-option:hover {
    border-color: var(--color-accent);
}

.verdict-option.active {
    border-color: var(--color-accent);
    background: var(--color-accent-soft);
    font-weight: 600;
}

.verdict-option:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}

.verdict-emoji {
    font-size: 22px;
    line-height: 1;
    flex: none;
}

/* A "Send Me a Driver" order has no preparation step — dispatch fires
   immediately and the details are collected by the driver — so the prep
   column says so rather than offering a button that does nothing. */
.prep-not-needed {
    font-size: 12px;
    color: var(--color-ink-soft);
    font-style: italic;
}

/* ---- Storefront: acknowledging a tap --------------------------------
   A quantity that just went up gets one short nudge. The point is only
   to confirm the tap landed on the card itself, since the running total
   lives at the bottom of the screen where a thumb may be covering it. */
@keyframes storeQtyBump {
    0%   { transform: scale(1); }
    45%  { transform: scale(1.35); }
    100% { transform: scale(1); }
}

.store-product-step-qty.just-added {
    animation: storeQtyBump 260ms ease-out;
    color: var(--color-accent);
}

/* The grid fades in when the CONTENT changes — a category chosen, a
   search typed — and never when the cart changes, because adding an item
   no longer rebuilds the grid at all (see store.js's syncCardControls).
   Without that split this replayed on every tap of +. */
@keyframes storeGridIn {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}

.store-product-grid.just-filtered {
    animation: storeGridIn 220ms ease-out;
}

/* Pressing a card should feel like pressing something. Paired with the
   existing hover lift, which is desktop-only in practice. */
.store-product-card:active {
    transform: translateY(-1px) scale(0.995);
}

.store-product-add,
.store-product-step {
    transition: transform var(--transition-fast), background var(--transition-fast);
}

.store-product-add:active,
.store-product-step:active {
    transform: scale(0.88);
}

@media (prefers-reduced-motion: reduce) {
    .store-mobile-checkout-bar,
    .store-product-step-qty.just-added,
    .store-product-grid.just-filtered,
    .store-product-card:active,
    .store-product-add:active,
    .store-product-step:active {
        animation: none;
        transition: none;
        transform: none;
    }
}

/* One shop/branch currently refusing a driver, on the admin Standing
   modal. */
.driver-block-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 0;
    border-bottom: 1px solid var(--color-border-soft);
}

.driver-block-row:last-child { border-bottom: none; }
.driver-block-where { min-width: 0; }
.driver-block-shop { font-weight: 600; }

.driver-block-scope {
    font-size: 12.5px;
    color: var(--color-text-muted);
    line-height: 1.45;
}

/* Delivery timers.
   Three states with three jobs: green is "you're fine, ignore me",
   amber is "this is the moment to hurry", red is "the office already
   knows". Semantic colours, deliberately not the app's indigo accent —
   a deadline that shares a colour with every other control on the page
   stops reading as a deadline. */
.timer-chip,
.delay-badge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    margin-top: 4px;
    padding: 2px 9px;
    border-radius: var(--radius-full);
    font-size: 11.5px;
    font-weight: 600;
    line-height: 1.7;
    white-space: nowrap;
}

.timer-chip i,
.delay-badge i { width: 13px; height: 13px; }

.timer-chip-ok {
    background: var(--color-success-soft);
    color: var(--color-success);
}

.timer-chip-soon,
.delay-badge-soon {
    background: var(--color-warning-soft);
    color: var(--color-warning);
}

.timer-chip-late,
.delay-badge-late {
    background: var(--color-danger-soft);
    color: var(--color-danger);
}

/* A driver who has said what's wrong is no longer being chased about
   it — neutral, not red. */
.timer-chip-held,
.delay-badge-held {
    background: var(--color-neutral-soft);
    color: var(--color-ink-soft);
}

/* Dark theme: the soft backgrounds above are light-theme tints, so they
   are re-derived from the semantic colour itself rather than inverted. */
[data-theme="dark"] .timer-chip-ok      { background: color-mix(in srgb, var(--color-success) 20%, transparent); }
[data-theme="dark"] .timer-chip-soon,
[data-theme="dark"] .delay-badge-soon   { background: color-mix(in srgb, var(--color-warning) 22%, transparent); }
[data-theme="dark"] .timer-chip-late,
[data-theme="dark"] .delay-badge-late   { background: color-mix(in srgb, var(--color-danger) 24%, transparent); }
[data-theme="dark"] .timer-chip-held,
[data-theme="dark"] .delay-badge-held   { background: color-mix(in srgb, var(--color-ink-soft) 20%, transparent); }

/* The driver's "what's holding you up" list. Big targets: this is
   tapped one-handed, often in the dark, sometimes in the rain. */
.delay-reason-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 6px 0 4px;
}

.delay-reason-option {
    padding: 14px 16px;
    border-radius: var(--radius);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-ink);
    font-size: 15px;
    text-align: start;
    cursor: pointer;
}

.delay-reason-option:hover,
.delay-reason-option:focus-visible {
    border-color: var(--color-primary-soft);
    background: var(--color-bg);
}

/* The settings preview — what the four fields actually mean in minutes.
   Nobody can evaluate base + per_km x distance in their head, and
   getting it wrong means flagging half the fleet or flagging nobody. */
.timer-subheading {
    margin: 22px 0 10px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-ink-soft);
}

.timer-preview {
    margin-top: 20px;
    padding: 14px 16px;
    border-radius: var(--radius);
    background: var(--color-bg);
    border: 1px solid var(--color-border-soft);
}

.timer-preview h4 {
    margin: 0 0 8px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-ink-soft);
}

.timer-preview-line { margin: 0 0 10px; font-size: 13px; }

.timer-preview-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
    font-variant-numeric: tabular-nums;
}

.timer-preview-table th {
    text-align: start;
    padding-block: 4px;
    padding-inline: 0 10px;
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-ink-soft);
}

.timer-preview-table td {
    padding-block: 4px;
    padding-inline: 0 10px;
    border-top: 1px solid var(--color-border-soft);
}

.timer-preview-note,
.timer-preview-off {
    margin: 12px 0 0;
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--color-ink-soft);
}

.timer-preview-off { margin: 0; }

/* Driver coverage — which shops dispatch may offer a driver.
   The summary line is deliberately the first thing in the modal: the
   two checklists below say what is ticked, but only the summary says
   what that MEANS for the driver, which is the thing an admin is
   actually deciding. */
.coverage-summary {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 4px 0 18px;
    padding: 10px 12px;
    border-radius: var(--radius);
    background: var(--color-bg);
    border: 1px solid var(--color-border-soft);
    font-size: 13px;
    line-height: 1.5;
}

.coverage-summary strong { font-weight: 600; }

/* Restricted is the state worth noticing on sight — an open driver is
   the norm and shouldn't shout. */
.coverage-summary.is-restricted {
    border-color: var(--color-primary-soft);
    background: color-mix(in srgb, var(--color-primary-soft) 8%, var(--color-surface));
}

.coverage-section { margin-bottom: 18px; }

.coverage-heading {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin: 0 0 10px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-ink-soft);
}

.coverage-count { letter-spacing: 0; text-transform: none; font-weight: 500; }

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

/* The checkbox itself is hidden rather than removed, so the label stays
   a real, focusable, keyboard-operable control. */
.coverage-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border-radius: var(--radius-full);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    font-size: 13px;
    cursor: pointer;
    user-select: none;
}

.coverage-chip input { position: absolute; opacity: 0; pointer-events: none; }
.coverage-chip:hover { border-color: var(--color-primary-soft); }

.coverage-chip:has(input:focus-visible) {
    outline: 2px solid var(--color-primary-soft);
    outline-offset: 2px;
}

.coverage-chip:has(input:checked) {
    border-color: var(--color-primary-soft);
    background: color-mix(in srgb, var(--color-primary-soft) 14%, var(--color-surface));
    color: var(--color-primary);
    font-weight: 600;
}

.coverage-search {
    width: 100%;
    margin-bottom: 10px;
    padding: 8px 12px;
    border-radius: var(--radius);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-ink);
    font-size: 13px;
}

.coverage-shop-list {
    max-height: 220px;
    overflow-y: auto;
    overflow-x: hidden;
    border: 1px solid var(--color-border-soft);
    border-radius: var(--radius);
}

.coverage-shop {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    border-bottom: 1px solid var(--color-border-soft);
    cursor: pointer;
    font-size: 13px;
}

.coverage-shop:last-child { border-bottom: none; }
.coverage-shop:hover { background: var(--color-bg); }
.coverage-shop-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.coverage-shop-meta {
    font-size: 11.5px;
    color: var(--color-ink-soft);
}

/* The courier pseudo-shop. Called out because switching a driver to any
   restriction quietly takes them off one-tap courier jobs unless this
   line is ticked — the one consequence of this screen nobody would
   predict. */
.coverage-shop-system .coverage-shop-name { font-style: italic; }

.coverage-empty {
    padding: 14px 12px;
    font-size: 13px;
    color: var(--color-ink-soft);
    text-align: center;
}

/* The restriction on the drivers table row. Shown on the row rather
   than only inside the modal so an admin scanning the fleet can see who
   is limited without opening anything. */
.coverage-tag {
    display: inline-block;
    padding: 1px 8px;
    border-radius: var(--radius-full);
    border: 1px solid var(--color-primary-soft);
    background: color-mix(in srgb, var(--color-primary-soft) 12%, transparent);
    color: var(--color-primary);
    font-size: 11.5px;
    font-weight: 600;
}

.coverage-tag-open { color: var(--color-ink-soft); }

/* Branch performance report — one row per location. */
.branch-unassigned { color: var(--color-text-muted); font-style: italic; }

.branch-unassigned-hint {
    font-size: 11.5px;
    color: var(--color-text-muted);
}

.branch-off-tag {
    display: inline-block;
    margin-inline-start: 6px;
    padding: 1px 7px;
    border-radius: var(--radius-full);
    background: var(--color-bg);
    border: 1px solid var(--color-border-soft);
    color: var(--color-text-muted);
    font-size: 10.5px;
    font-weight: 600;
}

/* A bar beside the percentage. The number alone makes you compare
   digits across rows; the bar makes the split readable in one look,
   which is the actual question ("is one branch carrying this?"). */
.branch-share {
    display: flex;
    align-items: center;
    gap: 8px;
}

.branch-share-bar {
    flex: 1;
    min-width: 44px;
    height: 6px;
    border-radius: var(--radius-full);
    background: var(--color-border-soft);
    overflow: hidden;
}

.branch-share-bar span {
    display: block;
    height: 100%;
    border-radius: var(--radius-full);
    background: var(--color-primary);
}

.branch-share-value {
    font-variant-numeric: tabular-nums;
    font-size: 12.5px;
    color: var(--color-text-muted);
}

.branch-total-row td {
    font-weight: 700;
    border-top: 2px solid var(--color-border-soft);
}

/* Branch list — the shop's delivery pickup locations. */
.branch-name { font-weight: 600; }

.branch-phone,
.branch-coords {
    font-size: 12px;
    color: var(--color-text-muted);
}

.branch-coords { font-variant-numeric: tabular-nums; }

/* ============================================================================
   Message Shops — the admin's broadcast composer
   (backend/views/admin-broadcasts.html).
   ============================================================================ */

/* Two cards, not a dropdown. The difference between these options is
   that one buzzes every shop owner's phone, which is not a distinction
   to bury in a select where it reads as a synonym for "important". */
.broadcast-priority {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 10px;
    margin: 18px 0 0;
    padding: 0;
    border: none;
}

.broadcast-priority legend {
    padding: 0 0 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-muted);
}

.broadcast-priority-option {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: 4px 10px;
    padding: 12px 14px;
    border: 1px solid var(--color-border-soft);
    border-radius: var(--radius);
    background: var(--color-surface);
    cursor: pointer;
    transition: border-color var(--transition-fast), background var(--transition-fast);
}

.broadcast-priority-option.is-active {
    border-color: var(--color-primary);
    background: var(--color-bg);
}

.broadcast-priority-option input { grid-row: 1 / span 2; }

.broadcast-priority-title {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 14px;
    font-weight: 600;
}

.broadcast-priority-title svg { width: 15px; height: 15px; }

.broadcast-priority-note {
    grid-column: 2;
    font-size: 12px;
    line-height: 1.4;
    color: var(--color-text-muted);
}

.broadcast-audience-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    margin: 14px 0 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-muted);
}

/* Scrolls rather than growing without limit — a platform with 200 shops
   should not push the Send button off the bottom of the page. */
.broadcast-audience-list {
    max-height: 280px;
    overflow-y: auto;
    border: 1px solid var(--color-border-soft);
    border-radius: var(--radius);
    background: var(--color-surface);
}

.broadcast-shop {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    border-bottom: 1px solid var(--color-border-soft);
    cursor: pointer;
    font-size: 14px;
}

.broadcast-shop:last-child { border-bottom: none; }
.broadcast-shop:hover { background: var(--color-bg); }
.broadcast-shop-name { font-weight: 600; }

.broadcast-shop-meta {
    font-size: 12px;
    color: var(--color-text-muted);
    text-align: end;
    white-space: nowrap;
}

.broadcast-shop-suspended { color: var(--color-danger); font-weight: 600; }

.broadcast-confirm-summary {
    margin: 0 0 12px;
    font-size: 14px;
    line-height: 1.55;
}

/* Shows the message as written, one last time. The commonest mistake
   here is not the audience but a half-finished sentence. */
.broadcast-confirm-preview {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 12px 14px;
    border-inline-start: 3px solid var(--color-primary);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    background: var(--color-bg);
    font-size: 14px;
}

.broadcast-confirm-preview span { color: var(--color-text-muted); font-size: 13px; }

.broadcast-history-title { font-weight: 600; }

.broadcast-history-body {
    font-size: 12.5px;
    color: var(--color-text-muted);
    max-width: 380px;
}

.broadcast-urgent-tag {
    display: inline-block;
    margin-top: 4px;
    padding: 1px 8px;
    border-radius: var(--radius-full);
    background: var(--color-warning-soft);
    color: var(--color-warning);
    font-size: 11px;
    font-weight: 700;
}

.broadcast-recipients-list {
    max-height: 300px;
    overflow-y: auto;
}

.broadcast-recipient {
    padding: 7px 2px;
    border-bottom: 1px solid var(--color-border-soft);
    font-size: 14px;
}

.broadcast-recipient:last-child { border-bottom: none; }

@media (prefers-reduced-motion: reduce) {
    .broadcast-priority-option { transition: none; }
}

/* ============================================================================
   Call a Waiter — the diner's table page (public/table.html) and the
   floor's live alert banner (backend/views/waiter-dashboard.html).
   ============================================================================ */

/* The diner's four choices. A grid of large, equal targets rather than
   a list: the person tapping is one-handed at a table, often in poor
   light, and has one decision to make. Nothing here is smaller than a
   comfortable thumb. */
.table-call-choices {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
}

.table-call-choice {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    min-height: 116px;
    padding: 18px 12px;
    border: 1px solid var(--color-border-soft);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
    box-shadow: var(--shadow-sm);
    color: inherit;
    font: inherit;
    cursor: pointer;
    text-align: center;
    transition: transform var(--transition-fast), box-shadow var(--transition-fast), border-color var(--transition-fast);
}

.table-call-choice:hover {
    border-color: var(--color-primary);
    box-shadow: var(--shadow-card);
}

.table-call-choice:active {
    transform: scale(0.97);
}

.table-call-choice:disabled {
    opacity: 0.55;
    cursor: default;
}

.table-call-choice-emoji {
    font-size: 30px;
    line-height: 1;
}

.table-call-choice-label {
    font-size: 14px;
    font-weight: 600;
    line-height: 1.3;
}

/* The tapped one stays visibly the one being sent while the request is
   in flight — on a slow restaurant connection this is the only feedback
   the diner gets, so it can't be subtle. */
.table-call-choice.is-sending {
    border-color: var(--color-primary);
    opacity: 1;
}

.table-call-sent {
    text-align: center;
}

.table-call-sent-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    margin-bottom: 12px;
    border-radius: var(--radius-full);
    background: var(--color-success-soft);
    color: var(--color-success);
}

.table-call-sent-mark i {
    width: 28px;
    height: 28px;
}

.table-call-sent-note {
    color: var(--color-text-muted);
    margin: 4px 0 18px;
}

/* ---- The diner's menu and basket ---------------------------------------- */

/* Two tabs, sized as one full-width control. A seated diner is holding
   the phone one-handed, so both halves have to be reachable without a
   grip change. */
.table-tabs {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px;
    padding: 5px;
    margin-bottom: 14px;
    border-radius: var(--radius-full);
    background: var(--color-bg);
    border: 1px solid var(--color-border-soft);
}

.table-tab {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 10px 8px;
    border: none;
    border-radius: var(--radius-full);
    background: transparent;
    color: var(--color-text-muted);
    font: inherit;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    transition: background var(--transition-fast), color var(--transition-fast);
}

.table-tab i { width: 15px; height: 15px; }

.table-tab.is-active {
    background: var(--color-surface);
    color: var(--color-primary);
    box-shadow: var(--shadow-sm);
}

.table-menu-search {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 0 14px;
    margin-bottom: 10px;
    border: 1px solid var(--color-border-soft);
    border-radius: var(--radius-full);
    background: var(--color-surface);
}

.table-menu-search svg {
    flex: none;
    width: 17px;
    height: 17px;
    color: var(--color-text-muted);
}

.table-menu-search input {
    flex: 1;
    min-width: 0;
    border: none;
    background: transparent;
    padding: 11px 0;
    font: inherit;
    color: inherit;
}

.table-menu-search input:focus {
    outline: none;
}

/* Horizontally scrolling chips rather than a wrapping block — a long
   category list must never push the menu itself off the first screen. */
.table-menu-categories {
    display: flex;
    gap: 8px;
    overflow-x: auto;
    padding-bottom: 10px;
    margin-bottom: 4px;
    scrollbar-width: none;
}

.table-menu-categories::-webkit-scrollbar { display: none; }

.table-menu-category {
    flex: 0 0 auto;
    padding: 7px 14px;
    border: 1px solid var(--color-border-soft);
    border-radius: var(--radius-full);
    background: var(--color-surface);
    color: var(--color-text-muted);
    font: inherit;
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
}

.table-menu-category.is-active {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: #FFFFFF;
}

.table-menu-group-title {
    margin: 18px 0 8px;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.table-menu-item {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 12px;
    margin-bottom: 8px;
    border: 1px solid var(--color-border-soft);
    border-radius: var(--radius);
    background: var(--color-surface);
    color: inherit;
    font: inherit;
    text-align: start;
    cursor: pointer;
    transition: border-color var(--transition-fast), transform var(--transition-fast);
}

.table-menu-item:active { transform: scale(0.99); }
.table-menu-item:hover { border-color: var(--color-primary); }

.table-menu-item-photo {
    flex: 0 0 auto;
    width: 56px;
    height: 56px;
    border-radius: var(--radius-sm);
    object-fit: cover;
}

.table-menu-item-text {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.table-menu-item-name { font-size: 15px; font-weight: 600; }

/* Two lines of description at most. A long paragraph on every row
   turns a menu into a wall and hides the prices below the fold. */
.table-menu-item-desc {
    font-size: 12.5px;
    line-height: 1.4;
    color: var(--color-text-muted);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.table-menu-item-price {
    margin-top: 2px;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-primary);
}

.table-menu-item-add {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: var(--radius-full);
    background: var(--color-primary);
    color: #FFFFFF;
}

.table-menu-item-add i { width: 16px; height: 16px; }

/* ---- Item picker --------------------------------------------------------- */

.table-item-modal-desc {
    margin: 0 0 12px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--color-text-muted);
}

.table-item-extra {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 2px;
    border-bottom: 1px solid var(--color-border-soft);
    cursor: pointer;
    font-size: 14px;
}

.table-item-extra-name { flex: 1; }
.table-item-extra-price { color: var(--color-text-muted); font-size: 13px; }

.table-item-qty {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 20px;
    margin: 18px 0 4px;
    font-size: 19px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.table-item-qty-btn {
    width: 42px;
    height: 42px;
    border: 1px solid var(--color-border-soft);
    border-radius: var(--radius-full);
    background: var(--color-surface);
    color: inherit;
    font-size: 21px;
    line-height: 1;
    cursor: pointer;
}

.table-item-qty-btn:active { transform: scale(0.94); }

.table-item-total {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    margin: 14px 0 2px;
    padding-top: 12px;
    border-top: 1px solid var(--color-border-soft);
    font-size: 15px;
    font-weight: 700;
}

.table-item-total span:last-child {
    font-variant-numeric: tabular-nums;
    color: var(--color-primary);
}

/* ---- Basket -------------------------------------------------------------- */

/* Fixed to the bottom so the menu stays readable while it fills. Slides
   away rather than disappearing, and only transform/opacity move — the
   two properties a phone can animate without repainting the menu
   underneath. */
.table-basket-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
    background: var(--color-surface);
    border-top: 1px solid var(--color-border-soft);
    box-shadow: var(--shadow-lift);
    transition: transform var(--transition-base), opacity var(--transition-base);
}

.table-basket-bar.is-away {
    transform: translateY(120%);
    opacity: 0;
    visibility: hidden;
}

.table-basket-open {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    max-width: 456px;
    margin: 0 auto;
    padding: 13px 16px;
    border: none;
    border-radius: var(--radius-full);
    background: var(--color-primary);
    color: #FFFFFF;
    font: inherit;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
}

.table-basket-open:active { transform: scale(0.99); }

.table-basket-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 26px;
    height: 26px;
    padding: 0 7px;
    border-radius: var(--radius-full);
    background: rgba(255, 255, 255, 0.22);
    font-size: 13px;
    font-variant-numeric: tabular-nums;
}

.table-basket-label { flex: 1; text-align: start; }
.table-basket-total { font-variant-numeric: tabular-nums; }

.table-basket-line {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: start;
    column-gap: 8px;
    padding: 10px 0;
    border-bottom: 1px solid var(--color-border-soft);
}

.table-basket-line-text { grid-column: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.table-basket-line-name { font-size: 14px; font-weight: 600; }
.table-basket-line-extras { font-size: 12px; color: var(--color-text-muted); }

.table-basket-line-price {
    grid-column: 1;
    margin-top: 3px;
    font-size: 13px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--color-primary);
}

.table-basket-line-remove { grid-column: 2; grid-row: 1 / span 2; align-self: center; }

.table-basket-line-remove {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border: none;
    border-radius: var(--radius-full);
    background: transparent;
    color: var(--color-text-muted);
    cursor: pointer;
}

.table-basket-line-remove:hover { background: var(--color-danger-soft); color: var(--color-danger); }
.table-basket-line-remove i { width: 15px; height: 15px; }

#tableBasketModalOverlay .modal-actions {
    flex-direction: column-reverse;
    align-items: stretch;
    gap: 8px;
}

#tableBasketModalOverlay .modal-actions button {
    width: 100%;
    justify-content: center;
}

/* ---- What the table has already ordered ---------------------------------- */

/* Sits below the menu, and clears the fixed basket bar so the last line
   is never hidden behind it. */
.table-tab-so-far {
    margin-top: 20px;
    margin-bottom: 96px;
}

.table-tab-so-far h2 {
    display: flex;
    align-items: center;
    gap: 7px;
    margin: 0 0 10px;
    font-size: 15px;
}

.table-tab-so-far h2 i { width: 16px; height: 16px; }

.table-sofar-line {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 2px 10px;
    padding: 7px 0;
    font-size: 14px;
    border-bottom: 1px solid var(--color-border-soft);
}

.table-sofar-name { flex: 1 1 100%; }

.table-sofar-price {
    flex: 0 0 auto;
    font-size: 13px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    color: var(--color-primary);
}

/* A line the waiter hasn't sent on yet. Says where it actually is,
   rather than implying the kitchen already has it. */
.table-sofar-pending {
    display: inline-block;
    margin-inline-start: 6px;
    padding: 1px 7px;
    border-radius: var(--radius-full);
    background: var(--color-warning-soft);
    color: var(--color-warning);
    font-size: 11px;
    font-weight: 600;
}

@media (prefers-reduced-motion: reduce) {
    .table-basket-bar,
    .table-menu-item,
    .table-menu-item:active,
    .table-basket-open:active,
    .table-item-qty-btn:active {
        transition: none;
        transform: none;
    }
}

/* The end of the meal. Warm rather than triumphant — a green tick here
   would read as "task complete" when what actually happened is that
   someone finished dinner and paid for it. */
.table-call-farewell-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    margin-bottom: 12px;
    border-radius: var(--radius-full);
    background: var(--color-primary);
    color: #FFFFFF;
}

.table-call-farewell-mark i {
    width: 26px;
    height: 26px;
}

.table-call-farewell-hint {
    margin: 0;
    color: var(--color-text-muted);
    font-size: 13px;
}

/* ---- The waiter's side --------------------------------------------------- */

/* Pinned above the table grid rather than folded into it. A waiter
   glances at this screen from across a room, and a call has to be
   readable before the tables are — so it gets the accent colour, its
   own block, and the biggest type on the page. */
.waiter-calls-banner {
    display: grid;
    gap: 10px;
    margin-bottom: 16px;
}

.waiter-call-card {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 14px 16px;
    border: 1px solid var(--color-primary);
    border-inline-start-width: 5px;
    border-radius: var(--radius);
    background: var(--color-surface);
    box-shadow: var(--shadow-card);
}

.waiter-call-emoji {
    font-size: 26px;
    line-height: 1;
}

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

.waiter-call-table {
    font-size: 17px;
    font-weight: 700;
    line-height: 1.2;
}

.waiter-call-reason {
    font-size: 14px;
    color: var(--color-text-muted);
}

.waiter-call-age {
    font-size: 12px;
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* A call that has gone unanswered long enough to be embarrassing.
   Warning, not danger — the table is waiting, nothing is broken. */
.waiter-call-card.is-stale {
    border-color: var(--color-warning);
    background: var(--color-warning-soft);
}

.waiter-call-card.is-stale .waiter-call-age {
    color: var(--color-warning);
    font-weight: 600;
}

/* The dot on a table card in the grid, for a waiter looking at the
   tables rather than the banner. Same information, second place. */
.waiter-table-call-flag {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-top: 4px;
    padding: 2px 8px;
    border-radius: var(--radius-full);
    background: var(--color-primary);
    color: #FFFFFF;
    font-size: 11px;
    font-weight: 600;
}

/* A table cell: the card that opens the tab, plus its own QR button.
   They are siblings rather than nested (a button inside a button is
   invalid markup), stacked so neither can ever cover the other's tap
   target — which matters more here than saving the 28px, because both
   get pressed by a thumb in a moving room. */
.waiter-table-cell {
    display: flex;
    flex-direction: column;
}

.waiter-table-cell .waiter-table-card {
    flex: 1;
    align-items: center;
    text-align: center;
    justify-content: center;
    min-height: 90px;
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}

.waiter-table-qr-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 5px;
    padding: 7px 8px;
    border: 1px solid var(--color-border-soft);
    border-top: none;
    border-radius: 0 0 var(--radius) var(--radius);
    background: var(--color-bg);
    color: var(--color-text-muted);
    font: inherit;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    transition: background var(--transition-fast), color var(--transition-fast);
}

.waiter-table-qr-btn i {
    width: 14px;
    height: 14px;
}

.waiter-table-qr-btn:hover {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: #FFFFFF;
}

.waiter-table-qr-btn:active {
    transform: scale(0.97);
}

/* The QR shown to a guest. Sized for the worst case it will actually
   meet: a phone held out across a table, at an angle, under restaurant
   lighting. */
.waiter-qr-modal {
    max-width: 380px;
    text-align: center;
}

.waiter-qr-instruction {
    margin: 0 0 14px;
    color: var(--color-text-muted);
    font-size: 13px;
    line-height: 1.5;
}

/* Always white with a wide margin, in BOTH themes. A QR scanner needs
   dark modules on a light ground and a clear quiet zone around them to
   find the code's edges at all — matching the dashboard's dark theme
   here would look tidier and scan worse, and scanning is the only
   thing this element exists to do. */
.waiter-qr-plate {
    display: inline-block;
    padding: 16px;
    border-radius: var(--radius);
    background: #FFFFFF;
}

.waiter-qr-plate img {
    display: block;
    width: 100%;
    max-width: 300px;
    height: auto;
    image-rendering: pixelated; /* keep the modules crisp when scaled */
}

/* A line a diner put on the tab themselves, not yet vouched for by
   anyone. Amber rather than red: it needs looking at, nothing is wrong. */
.waiter-guest-line-flag {
    display: inline-block;
    margin-inline-start: 6px;
    padding: 1px 7px;
    border-radius: var(--radius-full);
    background: var(--color-warning-soft);
    color: var(--color-warning);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.02em;
    vertical-align: 1px;
}

/* A table with items nobody has sent to the kitchen yet. Sits on the
   table card so the grid itself shows where the work is. */
.waiter-table-unsent-flag {
    display: inline-flex;
    align-items: center;
    margin-top: 4px;
    padding: 2px 8px;
    border-radius: var(--radius-full);
    background: var(--color-warning);
    color: #FFFFFF;
    font-size: 11px;
    font-weight: 600;
}

.dine-in-guest-tab {
    color: var(--color-text-muted);
    font-style: italic;
}

/* One pulse per newly-arrived call, on the banner only. Deliberately
   not a loop: a permanently pulsing screen becomes wallpaper within an
   hour, and then a real call is invisible. */
@keyframes waiterCallIn {
    from { opacity: 0; transform: translateY(-6px); }
    to   { opacity: 1; transform: none; }
}

.waiter-call-card.is-new {
    animation: waiterCallIn 240ms ease-out;
}

@media (prefers-reduced-motion: reduce) {
    .table-call-choice,
    .table-call-choice:active,
    .waiter-table-qr-btn,
    .waiter-table-qr-btn:active,
    .waiter-call-card.is-new {
        animation: none;
        transition: none;
        transform: none;
    }
}

/* ============================================================
   RIGHT-TO-LEFT
   ============================================================

   Almost all of the mirroring for dir="rtl" happens automatically,
   because this stylesheet uses CSS logical properties throughout:
   margin-inline-start rather than margin-left, text-align: start
   rather than left, inset-inline-start rather than left. The browser
   resolves those against the document direction, so one set of rules
   serves both.

   What follows is only the handful of things logical properties
   cannot express — cases where something must be flipped by hand
   because it is drawn, not laid out. */

/* Directional icons. An arrow means "back" or "next" by pointing, and
   pointing is relative to reading order — a left-pointing back arrow
   in a right-to-left page points forwards. Lucide renders inline SVG,
   so mirroring the glyph itself is the fix.

   Deliberately scoped to arrows and chevrons ONLY. Flipping every icon
   would mirror logos, clock faces, checkmarks and the search
   magnifier, all of which are direction-neutral and would look wrong
   reversed. */
[dir="rtl"] [data-lucide="arrow-left"],
[dir="rtl"] [data-lucide="arrow-right"],
[dir="rtl"] [data-lucide="chevron-left"],
[dir="rtl"] [data-lucide="chevron-right"],
[dir="rtl"] [data-lucide="arrow-left-right"],
[dir="rtl"] [data-lucide="corner-down-left"],
[dir="rtl"] [data-lucide="corner-up-left"],
[dir="rtl"] [data-lucide="log-in"],
[dir="rtl"] [data-lucide="log-out"],
[dir="rtl"] [data-lucide="send"],
[dir="rtl"] [data-lucide="reply"],
[dir="rtl"] [data-lucide="undo"],
[dir="rtl"] [data-lucide="redo"],
[dir="rtl"] [data-lucide="trending-up"],
[dir="rtl"] [data-lucide="trending-down"] {
    transform: scaleX(-1);
}

/* Up and down arrows are NOT flipped: vertical direction has nothing
   to do with reading order, and a mirrored up-arrow is just a wrong
   up-arrow. Listed explicitly so the omission reads as a decision
   rather than an oversight.
   [data-lucide="arrow-up"], [data-lucide="arrow-down"] — left alone. */

/* The typing/loading nudge animation slides content leftwards, which
   in a right-to-left page should nudge the other way. */
[dir="rtl"] .dots-pulse,
[dir="rtl"] [class*="nudge"] {
    animation-direction: reverse;
}

/* Prices, phone numbers, times and dates stay left-to-right even inside
   Arabic text — that is how they are written and read in Arabic.

   "unicode-bidi: isolate" rather than "plaintext": this app renders a
   dual-currency price as "$12.50 (1,125,000 L.L.)", which is a mix of
   digits, a currency symbol and parentheses — all bidi-NEUTRAL
   characters, with no strong direction of their own. Dropped into a
   right-to-left paragraph, the bidi algorithm reorders them and the
   price renders as "1,125,000) $12.50 (.L.L" — every character
   correct, in an order that is not a price. Isolating the run stops
   the surrounding direction from reaching inside it.

   Selectors match this app's actual class naming (store-product-price,
   dual-lbp, cart-total, ...) by substring, so a newly added price
   element does not silently render scrambled just because nobody
   remembered to list its class here. */
[dir="rtl"] [class*="price"],
[dir="rtl"] [class*="-lbp"],
[dir="rtl"] [class*="total"],
[dir="rtl"] [class*="amount"],
[dir="rtl"] [class*="money"],
[dir="rtl"] [class*="phone"],
[dir="rtl"] .stat-value,
[dir="rtl"] time,
[dir="rtl"] code,
[dir="rtl"] input[type="tel"],
[dir="rtl"] input[type="number"],
[dir="rtl"] input[type="email"],
[dir="rtl"] input[type="url"] {
    direction: ltr;
    unicode-bidi: isolate;
    text-align: start;
}

/* Arabic script has no capital letters, so uppercasing does nothing to
   Arabic text — but letter-spacing actively breaks it, because Arabic
   letters join. Spacing them apart severs the joins and renders words
   as disconnected glyphs, which is close to unreadable. */
[dir="rtl"] {
    letter-spacing: normal;
}

[dir="rtl"] .section-title,
[dir="rtl"] .stat-label,
[dir="rtl"] .badge,
[dir="rtl"] .eyebrow,
[dir="rtl"] th {
    letter-spacing: normal;
    text-transform: none;
}

/* Arabic renders with taller ascenders and descenders than Latin at
   the same nominal size, and this app's UI type is set tight. A small
   line-height increase keeps diacritics from clipping against the row
   above without changing any layout dimensions. */
[dir="rtl"] body {
    line-height: 1.7;
}

/* An action with an icon is a flex box: inline, so several sit side by side on a line and wrap together. */
.orders-table td.actions-cell > * { display: inline-flex; vertical-align: middle; }

/* ---- A list with row actions, as cards on a phone (public/js/table-tools.js adds .cards-on-phone and labels the cells) ---- */
@media (max-width: 640px) {
    /* !important: a page may give its list a height of its own inline (the admin's orders: 600px). */
    .table-scroll:has(.cards-on-phone) {
        height: auto !important; max-height: var(--cards-two, none) !important; overflow-x: hidden !important; overflow-y: auto !important;
        border: 0; -webkit-overflow-scrolling: touch; scrollbar-width: thin; padding-bottom: 2px;
    }
    .cards-on-phone thead { display: none; }
    .cards-on-phone, .cards-on-phone tbody, .cards-on-phone tr, .cards-on-phone td { display: block; width: 100%; box-sizing: border-box; }
    .cards-on-phone tbody tr {
        padding: 6px 14px; margin-bottom: 12px; background: var(--color-surface);
        border: 1px solid var(--color-border); border-radius: 12px;
    }
    .cards-on-phone tbody tr:hover { background: var(--color-surface); }
    .cards-on-phone tbody tr:last-child { margin-bottom: 0; }
    .cards-on-phone td {
        display: flex; align-items: center; justify-content: space-between; gap: 14px;
        padding: 8px 0; border: 0; border-bottom: 1px solid var(--color-border-soft);
        white-space: normal; text-align: end; overflow-wrap: anywhere;
    }
    .cards-on-phone td::before {
        content: attr(data-label); flex: none; max-width: 42%; text-align: start;
        font-size: 12px; font-weight: 600; letter-spacing: 0.03em; text-transform: uppercase; color: var(--color-ink-soft);
    }
    .cards-on-phone td[data-label=""]::before { content: none; }
    .cards-on-phone td:empty { display: none; }
    /* The actions: along the bottom of the card, wrapping, not pinned over anything. */
    .cards-on-phone tbody td.actions-cell {
        position: static; box-shadow: none; background: transparent; width: 100%; min-width: 0; max-width: none;
        display: flex; flex-wrap: wrap; justify-content: flex-start; gap: 4px 16px; padding: 10px 0 6px; border-bottom: 0; text-align: start;
    }
    .cards-on-phone tbody td.actions-cell > * { margin: 0; min-height: 36px; align-items: center; }
    .cards-on-phone tbody tr:hover td.actions-cell { background: transparent; }
    .cards-on-phone tr.table-tools-hidden { display: none !important; }
    .cards-on-phone td .status-badge { margin-inline-start: auto; }
}

/* The admin's header on a phone: its dozen buttons in two even columns instead of a wall that fills the first screen. */
@media (max-width: 640px) {
    body[data-i18n-scope="admin-dashboard"] .dashboard-header { align-items: stretch; }
    body[data-i18n-scope="admin-dashboard"] .dashboard-header > .actions-cell {
        display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; width: 100%;
    }
    body[data-i18n-scope="admin-dashboard"] .dashboard-header .actions-cell > a.secondary-button,
    body[data-i18n-scope="admin-dashboard"] .dashboard-header .actions-cell > button.secondary-button {
        width: 100%; min-height: 46px; justify-content: center; padding-inline: 10px; font-size: 14px; box-sizing: border-box;
    }
    body[data-i18n-scope="admin-dashboard"] .dashboard-header .actions-cell > .notification-bell-wrap .secondary-button { width: 100%; min-height: 46px; justify-content: center; }
    body[data-i18n-scope="admin-dashboard"] .dashboard-header .actions-cell > .connection-indicator { justify-content: center; }
    body[data-i18n-scope="admin-dashboard"] .dashboard-header .actions-cell > [data-i18n-toggle] { justify-self: stretch; display: flex; }
    body[data-i18n-scope="admin-dashboard"] .dashboard-header .actions-cell > [data-i18n-toggle] > * { flex: 1; justify-content: center; }
}
