/*
 * XPos base stylesheet.
 *
 * Design tokens plus the primitives every page needs (layout, cards, tables, forms, buttons,
 * flash messages). Page-specific styling is appended by later phases rather than living here.
 *
 * Written for the environment this actually runs in: a shop counter. Controls are sized for
 * touch (44px minimum target), text is large enough to read at arm's length under fluorescent
 * light, and nothing depends on hover, since a touchscreen has none.
 *
 * No build step and no framework — this file is served exactly as written, cache-busted by
 * mtime via View::asset().
 */

:root {
    /* Type scale */
    --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

    --text-xs: 0.75rem;
    --text-sm: 0.875rem;
    --text-base: 1rem;
    --text-lg: 1.125rem;
    --text-xl: 1.375rem;
    --text-2xl: 1.75rem;

    /* Spacing */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.5rem;
    --space-6: 2rem;

    --radius: 8px;
    --radius-sm: 5px;

    /* Colour. Slate neutrals with a single indigo accent; semantic colours for money and
       stock states, which carry meaning in a POS and must not be decorative. */
    --bg: #f1f5f9;
    --surface: #ffffff;
    --surface-alt: #f8fafc;
    --border: #d8dee9;
    --border-strong: #b9c2d0;

    --ink: #17212f;
    --ink-soft: #4a5768;
    --ink-muted: #6f7d90;

    --accent: #4338ca;
    --accent-ink: #ffffff;
    --accent-soft: #eef0fd;

    --success: #15803d;
    --success-soft: #e8f6ec;
    --danger: #b91c1c;
    --danger-soft: #fdecec;
    --warning: #a16207;
    --warning-soft: #fdf5e3;

    --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.07);
    --shadow: 0 2px 8px rgba(15, 23, 42, 0.09);

    /* Minimum comfortable touch target. */
    --touch: 44px;
}

*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    font-family: var(--font-sans);
    font-size: var(--text-base);
    line-height: 1.5;
    color: var(--ink);
    background: var(--bg);
    /* Prevent the page bouncing when a POS list is shorter than the viewport. */
    min-height: 100vh;
}

h1, h2, h3 {
    margin: 0 0 var(--space-3);
    line-height: 1.25;
    font-weight: 600;
}

h1 { font-size: var(--text-2xl); }
h2 { font-size: var(--text-xl); }
h3 { font-size: var(--text-lg); }

p { margin: 0 0 var(--space-3); }

a {
    color: var(--accent);
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
}

/* Visible focus everywhere. A cashier working by keyboard needs to see where they are, and
   removing outlines is the most common accessibility regression in admin UIs. */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

.muted { color: var(--ink-muted); }
.mono { font-family: var(--font-mono); }

/* Money and quantities are compared down a column, so they align on the decimal point and
   never use a proportional font. */
.num {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    text-align: right;
    white-space: nowrap;
}

/* ------------------------------------------------------------------ topbar */

.topbar {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    padding: 0 var(--space-4);
    min-height: 56px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    box-shadow: var(--shadow-sm);
    flex-wrap: wrap;
}

.brand {
    font-weight: 700;
    font-size: var(--text-lg);
    color: var(--ink);
    text-decoration: none;
    letter-spacing: -0.01em;
}

.topnav {
    display: flex;
    gap: var(--space-1);
    flex: 1 1 auto;
    flex-wrap: wrap;
    /* Horizontal scroll rather than reflow on a narrow terminal screen. */
    overflow-x: auto;
}

.topnav a {
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    color: var(--ink-soft);
    text-decoration: none;
    white-space: nowrap;
    font-size: var(--text-sm);
    font-weight: 500;
}

.topnav a:hover { background: var(--surface-alt); color: var(--ink); }
.topnav a.active { background: var(--accent-soft); color: var(--accent); }

.topbar-right {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-left: auto;
}

.role-chip {
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--ink-muted);
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: var(--space-1) var(--space-3);
}

/* A root admin scoped into one company must never mistake tenant data for platform data,
   so this banner is deliberately loud and always present while acting. */
.acting-banner {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--warning);
    background: var(--warning-soft);
    border: 1px solid currentColor;
    border-radius: var(--radius-sm);
    padding: var(--space-1) var(--space-3);
}

/* ------------------------------------------------------------------- layout */

.page {
    max-width: 1200px;
    margin: 0 auto;
    padding: var(--space-5) var(--space-4) var(--space-6);
}

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

.kv {
    display: grid;
    grid-template-columns: minmax(9rem, max-content) 1fr;
    gap: var(--space-2) var(--space-4);
    margin: 0;
}

.kv dt { color: var(--ink-muted); font-size: var(--text-sm); }
.kv dd { margin: 0; }

/* ------------------------------------------------------------------- tables */

.table-scroll {
    /* Wide tables scroll inside their own box; the page body must never scroll sideways. */
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm);
}

th, td {
    padding: var(--space-3);
    text-align: left;
    border-bottom: 1px solid var(--border);
}

th {
    font-weight: 600;
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--ink-muted);
    background: var(--surface-alt);
    position: sticky;
    top: 0;
    /* Long product lists are scrolled constantly; a floating header saves scrolling back up. */
    z-index: 1;
}

tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--surface-alt); }

/* ------------------------------------------------------------------- forms */

label {
    display: block;
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--ink-soft);
    margin-bottom: var(--space-1);
}

input[type="text"],
input[type="number"],
input[type="email"],
input[type="password"],
input[type="date"],
input[type="search"],
select,
textarea {
    width: 100%;
    min-height: var(--touch);
    padding: var(--space-2) var(--space-3);
    font: inherit;
    color: var(--ink);
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
}

input:focus, select:focus, textarea:focus {
    border-color: var(--accent);
    outline: 2px solid var(--accent-soft);
    outline-offset: 0;
}

input[type="number"] {
    /* Prices and quantities read as columns of digits. */
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    text-align: right;
}

.field { margin-bottom: var(--space-4); }

.field-error {
    color: var(--danger);
    font-size: var(--text-sm);
    margin-top: var(--space-1);
}

.form-row {
    display: grid;
    gap: var(--space-4);
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}

/* ----------------------------------------------------------------- buttons */

button,
.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: var(--touch);
    padding: var(--space-2) var(--space-5);
    font: inherit;
    font-weight: 600;
    color: var(--accent-ink);
    background: var(--accent);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    cursor: pointer;
    text-decoration: none;
    /* Stops a double-tap on a touchscreen selecting the label text. */
    user-select: none;
}

button:hover, .button:hover { filter: brightness(1.08); }
button:active, .button:active { filter: brightness(0.94); }

button:disabled {
    opacity: 0.55;
    cursor: not-allowed;
    filter: none;
}

.button-secondary {
    color: var(--ink);
    background: var(--surface);
    border-color: var(--border-strong);
}

.button-danger { background: var(--danger); }

.link-button {
    min-height: auto;
    padding: 0;
    color: var(--accent);
    background: none;
    border: 0;
    font-weight: 500;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.inline { display: inline; }

.actions {
    display: flex;
    gap: var(--space-3);
    flex-wrap: wrap;
    align-items: center;
}

/* -------------------------------------------------------------------- flash */

.flash-stack {
    max-width: 1200px;
    margin: var(--space-4) auto 0;
    padding: 0 var(--space-4);
    display: grid;
    gap: var(--space-2);
}

.flash {
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    font-size: var(--text-sm);
    font-weight: 500;
}

.flash-success { color: var(--success); background: var(--success-soft); border-color: currentColor; }
.flash-error   { color: var(--danger);  background: var(--danger-soft);  border-color: currentColor; }
.flash-warning { color: var(--warning); background: var(--warning-soft); border-color: currentColor; }
.flash-info    { color: var(--accent);  background: var(--accent-soft);  border-color: currentColor; }

/* -------------------------------------------------------------- error page */

.error-page {
    display: grid;
    place-items: center;
    min-height: 100vh;
    padding: var(--space-4);
}

.error-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: var(--space-6);
    max-width: 40rem;
    width: 100%;
    text-align: center;
}

.error-status {
    font-family: var(--font-mono);
    font-size: var(--text-2xl);
    font-weight: 700;
    color: var(--ink-muted);
    margin: 0 0 var(--space-2);
}

.error-message { color: var(--ink-soft); }

.error-trace {
    margin-top: var(--space-5);
    padding: var(--space-4);
    background: #0f172a;
    color: #cbd5e1;
    border-radius: var(--radius-sm);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    line-height: 1.55;
    text-align: left;
    overflow-x: auto;
    white-space: pre;
}

/* ------------------------------------------------------------------- print */

@media print {
    /* Receipts and invoices print through the browser rather than a PDF library, because
       Sinhala requires complex-script shaping that pure-PHP PDF writers cannot perform. The
       browser already does it correctly, so the print stylesheet is the real output path —
       see php/README.md. */
    .topbar,
    .flash-stack,
    .actions,
    .no-print {
        display: none !important;
    }

    body { background: #fff; }

    .page { max-width: none; padding: 0; }

    .card {
        border: 0;
        box-shadow: none;
        padding: 0;
        margin: 0;
    }
}

/* ------------------------------------------------------- auth pages (Phase 3) */

/*
 * Sign-in, two-factor, password reset, email verification. These render inside layout_guest,
 * which has no nav bar, so the card is centred in the viewport rather than sitting under a header.
 */

.auth-shell {
    display: grid;
    place-items: center;
    min-height: 100vh;
    padding: var(--space-4);
}

.auth-card {
    width: 100%;
    max-width: 27rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: var(--space-6);
}

.auth-brand {
    margin: 0 0 var(--space-5);
    font-size: var(--text-lg);
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--ink);
}

.auth-card h1 { font-size: var(--text-xl); }

.auth-links {
    margin: var(--space-5) 0 0;
    font-size: var(--text-sm);
}

.auth-links .sep {
    color: var(--ink-muted);
    margin: 0 var(--space-2);
}

.field-hint {
    margin: var(--space-1) 0 0;
    font-size: var(--text-xs);
    color: var(--ink-muted);
}

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

/*
 * One-time codes are read digit by digit off a phone screen, so they get a monospace font, wide
 * tracking, and a large size — the single most mistyped field in the app.
 */
.code-input {
    font-family: var(--font-mono);
    font-size: var(--text-xl);
    letter-spacing: 0.28em;
    text-align: center;
}

/* -------------------------------------------------------- two-factor setup */

.setup-steps {
    margin: var(--space-4) 0 0;
    padding-left: var(--space-5);
    display: grid;
    gap: var(--space-5);
}

.setup-steps > li { padding-left: var(--space-2); }

.setup-link { margin: var(--space-3) 0; }

/*
 * The setup key is transcribed by hand into an authenticator, so it must be unambiguous and
 * selectable in one gesture. user-select:all makes a single click select the whole key.
 */
.setup-secret {
    margin: var(--space-2) 0;
    padding: var(--space-3);
    background: var(--surface-alt);
    border: 1px dashed var(--border-strong);
    border-radius: var(--radius-sm);
    font-size: var(--text-base);
    letter-spacing: 0.1em;
    word-break: break-all;
    user-select: all;
}

/* ------------------------------------------------------------ backup codes */

.backup-codes {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
    gap: var(--space-2);
    margin: var(--space-4) 0;
    padding: 0;
    list-style: none;
}

.backup-codes li {
    padding: var(--space-2) var(--space-3);
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-size: var(--text-base);
    letter-spacing: 0.06em;
    text-align: center;
    user-select: all;
}

/* --------------------------------------------------------------- security */

.status-on,
.status-off {
    display: inline-block;
    font-size: var(--text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: var(--space-1) var(--space-3);
    border-radius: 999px;
}

.status-on  { color: var(--success); background: var(--success-soft); }
.status-off { color: var(--ink-muted); background: var(--surface-alt); border: 1px solid var(--border); }

.resend-form { margin-top: var(--space-3); }

hr {
    border: 0;
    border-top: 1px solid var(--border);
    margin: var(--space-5) 0;
}

/* ------------------------------------------------- tenancy & signup (Phase 4) */

/* Signup has three sections and a plan grid; the login-sized card would force it into one
   unreadable column. */
.auth-card-wide { max-width: 46rem; }

.auth-card-wide h2 {
    margin-top: var(--space-6);
    padding-top: var(--space-4);
    border-top: 1px solid var(--border);
    font-size: var(--text-lg);
}

.auth-card-wide h2:first-of-type {
    margin-top: var(--space-5);
}

code {
    font-family: var(--font-mono);
    font-size: 0.9em;
    padding: 0.1em 0.35em;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: 3px;
}

/* ---------------------------------------------------------------- plan picker */

.plan-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: var(--space-3);
    margin-bottom: var(--space-4);
}

/*
 * The whole tile is the label, so the entire card is a tap target rather than just the radio dot —
 * this is chosen on a phone as often as a desktop.
 */
.plan-option {
    display: grid;
    gap: var(--space-2);
    padding: var(--space-4);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    cursor: pointer;
    font-weight: 400;
    color: var(--ink);
    margin: 0;
}

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

/* :has() lets the selected state follow the radio without JS. Browsers without it simply show the
   radio dot, which is still perfectly usable — hence the server-side plan-selected class too. */
.plan-option:has(input:checked),
.plan-selected {
    border-color: var(--accent);
    background: var(--accent-soft);
    box-shadow: inset 0 0 0 1px var(--accent);
}

.plan-option input { width: auto; min-height: 0; }

.plan-name {
    font-weight: 600;
    font-size: var(--text-base);
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.plan-badge {
    font-size: var(--text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--accent);
    background: var(--surface);
    border: 1px solid currentColor;
    border-radius: 999px;
    padding: 0.1rem var(--space-2);
}

.plan-price {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-size: var(--text-lg);
    font-weight: 600;
}

.plan-description,
.plan-trial {
    font-size: var(--text-sm);
    color: var(--ink-muted);
}

.plan-trial { color: var(--success); font-weight: 500; }

/* ------------------------------------------------------- subscription block */

/*
 * The 402 screen. Deliberately calm rather than alarming: the tenant has not lost data and has not
 * done anything wrong, they owe money — so it reads as an invoice reminder, not an error page.
 */
.block-screen {
    max-width: 34rem;
    margin: var(--space-6) auto;
    text-align: center;
}

.block-badge {
    display: inline-block;
    font-size: var(--text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--warning);
    background: var(--warning-soft);
    border-radius: 999px;
    padding: var(--space-1) var(--space-3);
    margin: 0 0 var(--space-4);
}

.block-message {
    color: var(--ink-soft);
    font-size: var(--text-lg);
    line-height: 1.5;
}

.block-screen .actions { justify-content: center; }
.block-screen .auth-links { display: flex; gap: var(--space-2); justify-content: center; align-items: center; }

/* The current plan in the comparison table. */
.row-current { background: var(--accent-soft); }
.row-current td { font-weight: 500; }

/* --------------------------------------------------------- catalog (Phase 5) */

.page-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
}

.page-head h1 { margin-bottom: var(--space-3); }

.count {
    font-size: var(--text-base);
    font-weight: 400;
}

/* Filter bar sits above the table and wraps on a narrow terminal screen. */
.filter-bar {
    display: flex;
    gap: var(--space-3);
    align-items: flex-end;
    flex-wrap: wrap;
    padding: var(--space-4) 0;
    border-top: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
    margin-bottom: var(--space-4);
}

.filter-bar .field { margin-bottom: 0; flex: 1 1 10rem; }
.filter-bar .actions { flex: 0 0 auto; }

.inline-add {
    display: flex;
    gap: var(--space-3);
    align-items: flex-start;
    flex-wrap: wrap;
    padding: var(--space-4) 0;
    border-top: 1px solid var(--border);
    margin: var(--space-4) 0;
}

.inline-add .field { margin-bottom: 0; flex: 1 1 14rem; }
.field-narrow { flex: 0 0 7rem !important; }
.input-narrow { max-width: 5rem; }
.inline-add .actions { padding-top: 1.55rem; }

.empty-state {
    padding: var(--space-6) var(--space-4);
    text-align: center;
    color: var(--ink-muted);
}

/* -------------------------------------------------------------- thumbnails */

.col-thumb { width: 52px; }

.thumb {
    display: block;
    width: 40px;
    height: 40px;
    object-fit: cover;
    border-radius: var(--radius-sm);
    background: var(--surface-alt);
}

.thumb-empty,
.product-image-empty {
    border: 1px dashed var(--border-strong);
}

.product-image {
    display: block;
    width: 160px;
    height: 160px;
    object-fit: cover;
    border-radius: var(--radius);
    background: var(--surface-alt);
}

.image-panel {
    display: flex;
    gap: var(--space-5);
    align-items: flex-start;
    flex-wrap: wrap;
}

.image-panel > div { flex: 1 1 18rem; }

/* ------------------------------------------------------------ table states */

.col-actions {
    white-space: nowrap;
    text-align: right;
}

.col-actions .link-button + form,
.col-actions form + form { margin-left: var(--space-3); }

/* An inactive or non-applying row is dimmed rather than hidden — it still needs to be findable. */
.row-muted { opacity: 0.6; }

/*
 * Low stock is a semantic state, not decoration: it is the cue to reorder. Colour alone would fail for
 * a colour-blind user, so the figure is also bolded.
 */
.stock-low {
    color: var(--danger);
    font-weight: 700;
}

.discounted { color: var(--success); }

.check-inline {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-weight: 400;
    margin: 0;
}

.check-inline input,
.field-check input {
    width: auto;
    min-height: 0;
    margin: 0;
}

.field-check label {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-weight: 400;
    color: var(--ink);
    margin: 0;
}

input[type="file"] {
    width: 100%;
    padding: var(--space-2);
    font: inherit;
    background: var(--surface-alt);
    border: 1px dashed var(--border-strong);
    border-radius: var(--radius-sm);
}

/* ------------------------------------------------------------- pagination */

.pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-4);
    padding-top: var(--space-4);
    margin-top: var(--space-4);
    border-top: 1px solid var(--border);
    font-size: var(--text-sm);
}

/* Visually hidden but read by screen readers — used to announce the low-stock state. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ------------------------------------------------------------- POS (Phase 6) */

/*
 * The till. A two-column layout: catalog on the left, cart on the right, both scrolling
 * independently so a long product list never pushes the total off screen.
 *
 * Everything here is sized for a touchscreen at a counter — tiles are large targets, the total is
 * readable at arm's length, and nothing depends on hover.
 */
.pos {
    display: grid;
    grid-template-columns: 1fr minmax(20rem, 26rem);
    gap: var(--space-4);
    align-items: start;
}

@media (max-width: 60rem) {
    /* Cart first on a narrow screen: the running total matters more than browsing. */
    .pos { grid-template-columns: 1fr; }
    .pos-cart { order: -1; }
}

.pos-catalog,
.pos-cart {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    padding: var(--space-4);
}

.pos-cart {
    position: sticky;
    top: var(--space-4);
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 6rem);
}

.pos-search-row {
    display: flex;
    gap: var(--space-3);
    align-items: flex-end;
}

.pos-search-row .field { flex: 1 1 auto; margin-bottom: 0; }
.pos-search-row select { width: auto; min-width: 9rem; }

.pos-hint { margin: var(--space-2) 0 var(--space-3); }

.pos-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
    gap: var(--space-2);
    max-height: 60vh;
    overflow-y: auto;
}

.pos-tile {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-1);
    /* Generous target: this is tapped hundreds of times a day, often at speed. */
    min-height: 5.5rem;
    padding: var(--space-3);
    text-align: left;
    color: var(--ink);
    background: var(--surface-alt);
    border: 1px solid var(--border);
    font-weight: 400;
}

.pos-tile:hover { border-color: var(--accent); filter: none; }

/* Out of stock at this branch: dimmed but still tappable, because a manager may be selling from a
   delivery that has not been booked in yet — the server refuses if it really cannot be covered. */
.pos-tile-out { opacity: 0.45; }

.pos-tile-name {
    font-weight: 600;
    font-size: var(--text-sm);
    line-height: 1.3;
}

.pos-tile-price {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-size: var(--text-sm);
}

.pos-tile-price s { color: var(--ink-muted); }
.pos-tile-price strong { color: var(--success); }
.pos-tile-unit { font-size: var(--text-xs); color: var(--ink-muted); }

/* D-31. Decorative — the name is still the label, this just speeds recognition. Fixed box so a
   grid of mixed portrait and landscape thumbnails does not jitter as images load in. */
.pos-tile-image {
    width: 100%;
    height: 3.5rem;
    object-fit: contain;
    align-self: center;
    background: var(--surface);
    border-radius: var(--radius-sm, 3px);
}

.pos-empty { padding: var(--space-5); text-align: center; }

/* ------------------------------------------------------------------ the cart */

.pos-cart-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: var(--space-3);
    padding-bottom: var(--space-3);
    border-bottom: 1px solid var(--border);
}

.pos-cart-head span { display: block; }

.pos-lines {
    flex: 1 1 auto;
    overflow-y: auto;
    min-height: 6rem;
    margin: var(--space-2) 0;
}

.pos-line {
    display: grid;
    grid-template-columns: 1fr auto auto auto;
    gap: var(--space-2);
    align-items: center;
    padding: var(--space-2) 0;
    border-bottom: 1px solid var(--border);
}

.pos-line-name { font-size: var(--text-sm); }

.pos-line-qty {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

.pos-qty-btn {
    min-height: 32px;
    min-width: 32px;
    padding: 0;
    font-size: var(--text-base);
    font-weight: 700;
    color: var(--ink);
    background: var(--surface-alt);
    border: 1px solid var(--border-strong);
}

.pos-line-amount {
    min-width: 4rem;
    text-align: center;
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-size: var(--text-sm);
}

.pos-line-total { font-size: var(--text-sm); min-width: 5rem; }

.pos-line-remove {
    min-height: 32px;
    min-width: 32px;
    padding: 0;
    color: var(--danger);
    background: none;
    border: 0;
    font-size: var(--text-lg);
}

.pos-totals {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: var(--space-1) var(--space-3);
    padding-top: var(--space-3);
    border-top: 1px solid var(--border);
    margin: 0 0 var(--space-2);
}

.pos-totals dt { color: var(--ink-muted); font-size: var(--text-sm); }
.pos-totals dd { margin: 0; }

.pos-total-label { color: var(--ink) !important; font-weight: 600; font-size: var(--text-base) !important; }

/* The number the cashier reads aloud. Deliberately the largest thing on the page. */
.pos-total {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-size: var(--text-2xl);
    font-weight: 700;
}

.pos-actions { margin-top: var(--space-3); }

/* ------------------------------------------------- customer, discount, points */

/* Attached to the cart rather than hidden in a dialog: whether a sale has a customer changes what
   the sale IS — it enables loyalty and allows credit — so it must be visible without opening
   anything. */
.pos-customer {
    padding: var(--space-2) var(--space-3);
    background: var(--surface-alt);
    border: 1px solid var(--border);
    margin-bottom: var(--space-2);
}

.pos-customer-none,
.pos-customer-chosen {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
}

.pos-customer-chosen strong { display: block; font-size: var(--text-sm); }
.pos-customer-chosen .muted { display: block; }

.pos-adjustments {
    display: flex;
    gap: var(--space-3);
    padding: var(--space-2) 0;
    font-size: var(--text-sm);
}

.pos-customer-results {
    list-style: none;
    margin: 0 0 var(--space-2);
    padding: 0;
    max-height: 16rem;
    overflow-y: auto;
}

.pos-customer-result {
    display: block;
    width: 100%;
    text-align: left;
    padding: var(--space-2) var(--space-3);
    background: transparent;
    border: 0;
    border-bottom: 1px solid var(--border);
    color: var(--ink);
    font-weight: 400;
}

.pos-customer-result:hover { background: var(--surface-alt); filter: none; }
.pos-customer-result strong { display: block; font-size: var(--text-sm); }
.pos-actions button { flex: 1 1 auto; }

/* -------------------------------------------------------------------- dialogs */

.pos-dialog {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: var(--space-5);
    max-width: 26rem;
    width: 100%;
    color: var(--ink);
    background: var(--surface);
}

.pos-dialog::backdrop { background: rgba(15, 23, 42, 0.55); }

.pos-change {
    padding: var(--space-3);
    background: var(--success-soft);
    color: var(--success);
    border-radius: var(--radius-sm);
    font-weight: 600;
    font-family: var(--font-mono);
}

/* ------------------------------------------------------------------ receipts */

/*
 * Receipts print through the browser (decision A-09): no pure-PHP PDF writer can shape Sinhala, and a
 * browser already does. Sized for 80mm roll paper when printed, readable on screen for a reprint.
 */
.receipt-page {
    background: var(--bg);
    padding: var(--space-5) var(--space-4);
}

.receipt {
    max-width: 80mm;
    margin: 0 auto;
    padding: var(--space-4);
    background: #fff;
    border: 1px solid var(--border);
    font-family: var(--font-mono);
    font-size: 12px;
    line-height: 1.45;
    color: #000;
}

.receipt-head { text-align: center; margin-bottom: var(--space-3); }
.receipt-head h1 { font-size: 15px; margin: 0 0 var(--space-1); }
.receipt-logo { max-width: 40mm; max-height: 20mm; margin: 0 auto var(--space-2); display: block; }
.receipt-meta, .receipt-note { margin: 0; font-size: 11px; }
.receipt-note { margin-top: var(--space-2); text-align: center; }

.receipt-info,
.receipt-totals {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 1px var(--space-2);
    margin: var(--space-3) 0;
    padding: var(--space-2) 0;
    border-top: 1px dashed #000;
    border-bottom: 1px dashed #000;
    font-size: 11px;
}

.receipt-info dt, .receipt-totals dt { color: #000; font-size: 11px; }
.receipt-info dd, .receipt-totals dd { margin: 0; text-align: right; }

.receipt-grand { font-size: 14px !important; font-weight: 700; padding-top: var(--space-1); }

.receipt-lines { width: 100%; font-size: 11px; border-collapse: collapse; }
.receipt-lines td { padding: 0; border: 0; font-size: 11px; }
.receipt-item-name { padding-top: var(--space-2) !important; font-weight: 700; }
.receipt-item-qty { color: #333; }
.receipt-item-total { text-align: right; }
.receipt-item-retail, .receipt-item-discount { padding-left: var(--space-3) !important; color: #333; font-size: 10px; }

.receipt-actions { max-width: 80mm; margin: var(--space-4) auto 0; text-align: center; }
.receipt-summary { margin-top: var(--space-4); max-width: 22rem; margin-left: auto; }

@media print {
    /* Nothing but the receipt. The browser's own margins are removed so an 80mm roll is not wasted. */
    .receipt-page { background: #fff; padding: 0; }
    .receipt { max-width: none; border: 0; padding: 0; }
    @page { margin: 4mm; }
}

/* ---------------------------------------------------------------- accounting */

/* Sub-navigation for the seven accounting pages. They are read in sequence — trial balance, then
   P&L, then drill into a ledger — so they need to be one click apart without each taking a slot in
   the main nav. */
.subnav {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    margin-bottom: var(--space-4);
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--border);
}

.subnav a {
    padding: var(--space-1) var(--space-3);
    font-size: var(--text-sm);
    color: var(--ink-muted);
    text-decoration: none;
    border-radius: var(--radius-sm, 3px);
}

.subnav a:hover { background: var(--surface-alt); color: var(--ink); }
.subnav a.active { background: var(--surface-alt); color: var(--ink); font-weight: 600; }

/* ------------------------------------------------------------------- reports */

/* The hub: a link plus what the report answers. The description is the point — twelve bare titles
   is a maze. */
.report-list { list-style: none; margin: 0 0 var(--space-5); padding: 0; }

.report-list li {
    padding: var(--space-2) 0;
    border-bottom: 1px solid var(--border);
}

.report-list a { font-weight: 600; }
.report-list .muted { display: block; font-size: var(--text-sm); margin-top: 2px; }

/* A period-on-period change. Colour carries the meaning, so it is set by the caller rather than by
   the sign — a fall in expenses is good news. */
.delta {
    font-size: var(--text-sm);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ------------------------------------------------------------------ invoices */

/* A4 rather than the receipt's 80mm roll: this goes to a customer, not a till printer. */
.invoice-page {
    background: var(--surface-alt);
    padding: var(--space-5) var(--space-3);
}

.invoice {
    max-width: 190mm;
    margin: 0 auto;
    padding: var(--space-6);
    background: var(--surface);
    border: 1px solid var(--border);
    position: relative;
}

.invoice-head {
    display: flex;
    justify-content: space-between;
    gap: var(--space-5);
    padding-bottom: var(--space-4);
    border-bottom: 2px solid var(--ink);
    margin-bottom: var(--space-4);
}

.invoice-issuer h1 { font-size: var(--text-xl); margin: 0 0 var(--space-2); }
.invoice-issuer p { margin: 0 0 var(--space-1); font-size: var(--text-sm); }
.invoice-logo { max-width: 45mm; max-height: 22mm; margin-bottom: var(--space-2); display: block; }

.invoice-meta { text-align: right; flex-shrink: 0; }
.invoice-meta h2 { font-size: var(--text-xl); margin: 0; text-transform: uppercase; letter-spacing: 0.08em; }
.invoice-number { font-size: var(--text-base); margin: 0 0 var(--space-3); }
.invoice-meta dl { display: grid; grid-template-columns: auto auto; gap: 0 var(--space-2); justify-content: end; }
.invoice-meta dt { color: var(--ink-muted); font-size: var(--text-sm); }
.invoice-meta dd { margin: 0; font-size: var(--text-sm); }

.invoice-bill-to { margin-bottom: var(--space-4); }
.invoice-bill-to h3 {
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-muted);
    margin: 0 0 var(--space-1);
}
.invoice-bill-to p { margin: 0; }

.invoice-lines { width: 100%; border-collapse: collapse; margin-bottom: var(--space-4); }
.invoice-lines th {
    text-align: left;
    border-bottom: 1px solid var(--ink);
    padding: var(--space-2) var(--space-2) var(--space-2) 0;
    font-size: var(--text-sm);
}
.invoice-lines td {
    padding: var(--space-2) var(--space-2) var(--space-2) 0;
    border-bottom: 1px solid var(--border);
    vertical-align: top;
}
.invoice-lines .num { text-align: right; font-variant-numeric: tabular-nums; padding-right: 0; }

.invoice-totals {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: var(--space-1) var(--space-4);
    max-width: 80mm;
    margin-left: auto;
}
.invoice-totals dt { color: var(--ink-muted); font-size: var(--text-sm); }
.invoice-totals dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }
.invoice-total-label { color: var(--ink) !important; font-weight: 600; font-size: var(--text-base) !important; }
.invoice-total { font-weight: 700; font-size: var(--text-lg); }

.invoice-notes { margin-top: var(--space-5); padding-top: var(--space-3); border-top: 1px solid var(--border); }
.invoice-paid { margin-top: var(--space-4); font-weight: 600; color: var(--success); }
.invoice-foot { margin-top: var(--space-6); padding-top: var(--space-2); border-top: 1px solid var(--border); }

/* A cancelled invoice printed by accident must not read as a live one. */
.invoice-void {
    position: absolute;
    top: 40%;
    left: 0;
    right: 0;
    text-align: center;
    font-size: 64px;
    font-weight: 700;
    letter-spacing: 0.15em;
    color: var(--danger, #c0392b);
    opacity: 0.18;
    transform: rotate(-18deg);
    pointer-events: none;
    margin: 0;
}

.invoice-actions { max-width: 190mm; margin: var(--space-4) auto 0; }

@media print {
    /* Nothing but the invoice, on A4. */
    .invoice-page { background: #fff; padding: 0; }
    .invoice { max-width: none; border: 0; padding: 0; }
    /* The watermark must survive printing — browsers drop backgrounds by default, but this is
       real text, so it prints as long as colour adjustment is not stripped. */
    .invoice-void { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
    @page { margin: 14mm; }
}

/* ======================================================================== */
/* Primitives the views had been using without styling                       */
/* ======================================================================== */

/*
 * Written in Phase 14, after an audit found 46 class names used across the view layer with no rule
 * behind them — accumulated from phase 5 onward, because a missing style makes a page look plain
 * rather than broken, so nothing ever failed.
 *
 * The worst of them was `.visually-hidden`, which is an accessibility contract: text marked with it
 * is *meant* to be read by a screen reader and not seen. With no rule, every such label was simply
 * visible, so the markup was doing the opposite of what it said.
 */

/* Screen-reader-only. The clip-path pair keeps it in the accessibility tree while removing it
   visually — `display: none` would hide it from screen readers too, defeating the point. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* ---------------------------------------------------------- semantic text */

/* Money and counts that carry a judgement. Never colour alone — every use in the views pairs these
   with a word, an arrow or a sign, because roughly 1 in 12 men cannot separate the two hues. */
.text-positive { color: var(--success); }
.text-warn     { color: var(--danger); }

.hint {
    margin: var(--space-3) 0;
    font-size: var(--text-sm);
    color: var(--ink-soft);
}

.note, .notes {
    margin: var(--space-2) 0;
    padding: var(--space-3);
    background: var(--surface-alt);
    border-left: 3px solid var(--border-strong);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
    white-space: pre-wrap;
}

/* ------------------------------------------------------------- alerts */

.alert {
    padding: var(--space-3) var(--space-4);
    margin-bottom: var(--space-4);
    border: 1px solid var(--border);
    border-left-width: 4px;
    border-radius: var(--radius-sm);
    background: var(--surface-alt);
    font-size: var(--text-sm);
}

.alert-info { border-left-color: var(--accent); background: var(--accent-soft); }
.alert-warn { border-left-color: var(--warning); background: var(--warning-soft); }

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

/* A short status word inside a heading or a table cell. `.chip` and `.badge` are the same object
   under two names the views already use; kept as one rule rather than renaming 40 call sites. */
.chip, .badge {
    display: inline-block;
    padding: 2px var(--space-2);
    border-radius: 999px;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    font-size: var(--text-xs);
    font-weight: 600;
    line-height: 1.6;
    color: var(--ink-soft);
    vertical-align: middle;
    white-space: nowrap;
}

.chip-open   { background: var(--success-soft); border-color: var(--success); color: var(--success); }
.badge-warn  { background: var(--warning-soft); border-color: var(--warning); color: var(--warning); }
.badge-muted { color: var(--ink-muted); }

/* ------------------------------------------------------------- layout */

/* A row of actions that should sit together and wrap as a unit. */
.action-row, .page-actions, .filter-actions, .error-actions, .statement-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
}

.filter-actions { align-self: flex-end; }
.statement-actions { max-width: 21cm; margin: var(--space-4) auto 0; }

.stack > * + * { margin-top: var(--space-3); }

.field-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
}
.field-row > .field { flex: 1 1 12rem; }

/* A checkbox sits beside its label rather than under it, and the whole label is the target — a
   14px box is not a 44px touch target on its own. */
.field.checkbox { display: flex; align-items: center; }
.field.checkbox label {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-height: var(--touch);
    margin: 0;
    font-weight: 400;
    cursor: pointer;
}
.field.checkbox input { width: 1.15rem; height: 1.15rem; }

.inline-form { display: inline; }

/* Values that are shown but cannot be edited — derived customer totals, a frozen invoice snapshot.
   Deliberately not a disabled input: a greyed-out field invites clicking, and these are facts. */
.readonly-block {
    padding: var(--space-3);
    background: var(--surface-alt);
    border: 1px dashed var(--border-strong);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
}

/* --------------------------------------------------- definition grids */

/* Headline figures across the top of a report, and label/value pairs on a detail page. Both are
   <dl>s of <div> pairs; they differ only in how wide a cell wants to be. */
.summary-grid, .detail-grid {
    display: grid;
    gap: var(--space-3);
    margin: var(--space-4) 0;
    padding: var(--space-4);
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.summary-grid { grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); }
.detail-grid  { grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); }

.summary-grid dt, .detail-grid dt {
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--ink-muted);
}

.summary-grid dd, .detail-grid dd {
    margin: var(--space-1) 0 0;
    font-size: var(--text-lg);
    font-variant-numeric: tabular-nums;
}

.detail-grid dd { font-size: var(--text-base); }

/* ------------------------------------------------------------- tables */

/* Rows that mean something. Tinted rather than outlined, so a long table still scans as a table. */
.row-warn      > td { background: var(--danger-soft); }
.row-highlight > td { background: var(--accent-soft); }

/* Deliberately NOT green: a cash drawer that is over is a counting error too, so it is marked as
   notable rather than as good news. */
tr.row-good > td { background: var(--warning-soft); }

tr.subtotal > th, tr.subtotal > td { border-top: 1px solid var(--border-strong); font-weight: 600; }
tr.total    > th, tr.total    > td { border-top: 2px solid var(--ink); font-weight: 700; }

.indent > th:first-child { padding-left: var(--space-5); }

/* A two-column reconciliation: label on the left, amount on the right, the sum reading downward. */
.reconciliation {
    width: 100%;
    max-width: 26rem;
    border-collapse: collapse;
    margin: var(--space-3) 0;
}
.reconciliation th { text-align: left; font-weight: 400; padding: var(--space-2) 0; }
.reconciliation td { padding: var(--space-2) 0; }

/* -------------------------------------------------- shift Z-report extras */

.receipt-title { font-weight: 700; margin: 0; }
.receipt-lines th {
    text-align: left;
    font-weight: 400;
    padding: 0;
    border: 0;
    font-size: 11px;
}
.receipt-subtotal th, .receipt-subtotal td { border-top: 1px dashed #000; font-weight: 700; }
.receipt-total th, .receipt-total td { border-top: 1px solid #000; font-weight: 700; font-size: 13px; }

/* Two ruled lines. The physical ritual this paper exists for is one person handing a counted bag
   to another, and a printout nobody signs settles no argument about a missing note. */
.signature-row { display: flex; gap: var(--space-4); margin-top: var(--space-5); }
.signature { flex: 1; }
.signature span { display: block; font-size: 10px; color: #333; }
.signature::before {
    content: "";
    display: block;
    border-top: 1px solid #000;
    margin-bottom: var(--space-1);
    padding-top: var(--space-5);
}

/* ------------------------------------------------- printable statements */

/*
 * Trial balance, profit and loss, balance sheet (D-55). A4 rather than the 80mm the receipt and
 * shift summary use — these go to an accountant, not a till printer.
 */
.statement-page {
    background: var(--bg);
    padding: var(--space-5) var(--space-4);
}

.statement {
    max-width: 21cm;
    margin: 0 auto;
    padding: var(--space-6);
    background: #fff;
    border: 1px solid var(--border);
    color: #000;
}

.statement-head { text-align: center; margin-bottom: var(--space-6); }
.statement-head h1 { font-size: var(--text-xl); margin: 0; }
.statement-title { font-size: var(--text-lg); font-weight: 600; margin: var(--space-1) 0; }

.statement-section { margin-bottom: var(--space-5); }
.statement-section h2 {
    font-size: var(--text-base);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    border-bottom: 1px solid #000;
    padding-bottom: var(--space-1);
    margin-bottom: var(--space-2);
}

.statement-lines { width: 100%; border-collapse: collapse; }
.statement-lines th {
    text-align: left;
    font-weight: 400;
    padding: var(--space-1) 0;
}
.statement-lines td { padding: var(--space-1) 0; }
.statement-lines .num { padding-left: var(--space-4); }

.statement-subtotal th, .statement-subtotal td {
    border-top: 1px solid #000;
    padding-top: var(--space-2);
    font-weight: 700;
}

.statement-grand { margin-top: var(--space-5); }
.statement-total th, .statement-total td {
    border-top: 2px solid #000;
    border-bottom: 3px double #000;
    padding: var(--space-2) 0;
    font-size: var(--text-lg);
    font-weight: 700;
}

.statement-note { margin-top: var(--space-4); font-size: var(--text-sm); }
.statement-foot { margin-top: var(--space-6); padding-top: var(--space-3); border-top: 1px solid var(--border); }

@media print {
    /* Nothing but the statement. */
    .statement-page { background: #fff; padding: 0; }
    .statement { max-width: none; border: 0; padding: 0; }
    @page { margin: 15mm; }
}

/* ------------------------------------------------------ notifications (Phase 15) */

/* The unread badge in the top bar. A dot plus a count rather than an icon font — one more asset to
   ship, and a glyph that fails to load leaves a blank space where the count should be. */
.notify-bell {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: var(--touch);
    min-height: var(--touch);
    color: var(--ink-soft);
    text-decoration: none;
    font-size: var(--text-lg);
}

.notify-count {
    position: absolute;
    top: 4px;
    right: 2px;
    min-width: 1.15rem;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--danger);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    line-height: 1.15rem;
    text-align: center;
}

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

.notification {
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
}

/* Unread is marked by a bar and a weight change, not by colour alone. */
.notification-unread {
    border-left: 4px solid var(--accent);
    background: var(--accent-soft);
}

.notification-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
}

.notification-body { margin: 0 0 var(--space-3); white-space: pre-wrap; }
