merge: fix dead discount toggle + stale figure, lift dark palette

This commit is contained in:
Dave 2026-10-12 23:18:00 +02:00
commit cc015d62bd
11 changed files with 218 additions and 131 deletions

View file

@ -93,9 +93,9 @@ button:focus-visible,input:focus-visible,select:focus-visible{outline:0;box-shad
@media (prefers-color-scheme: dark){
:root:not([data-theme="light"]){
--bg:#0f0f0f;--surface:#1c1416;--surface-2:#181818;--surface-card:#181818;
--fg:#f0f0f0;--ink-soft:#a0a0a0;--ink-faint:#777;
--muted:#2a2a2a;--border:#3a2429;
--bg:#161619;--surface:#1e1e23;--surface-2:#1e1e23;--surface-card:#26262d;
--fg:#f2f2f5;--ink-soft:#a0a0a0;--ink-faint:#8e8e9a;
--muted:#303039;--border:#3b3b46;
--accent-soft:#2a1a1a;--accent-2:#3a2429;
--ok-soft:#1a2e22;--warn-soft:#2a1a1a;--danger-soft:#2a1a1a;--danger-2:#3a2429;
}
@ -137,10 +137,10 @@ button:focus-visible,input:focus-visible,select:focus-visible{outline:0;box-shad
:root:not([data-theme="light"]) .skeleton{color:var(--ink-faint)}
}
:root[data-theme="dark"]{
--bg:#0f0f0f;--surface:#1c1416;--surface-2:#181818;--surface-card:#181818;
--fg:#f0f0f0;--ink-soft:#a0a0a0;--ink-faint:#777;
--muted:#2a2a2a;--border:#3a2429;
--accent-soft:#2a1a1a;--accent-2:#3a2429;
--bg:#161619;--surface:#1e1e23;--surface-2:#1e1e23;--surface-card:#26262d;
--fg:#f2f2f5;--ink-soft:#a0a0a0;--ink-faint:#8e8e9a;
--muted:#303039;--border:#3b3b46;
--accent-soft:#2b1f24;--accent-2:#3f2b33;
--ok-soft:#1a2e22;--warn-soft:#2a1a1a;--danger-soft:#2a1a1a;--danger-2:#3a2429;
background:var(--bg);color:var(--fg);
}

View file

@ -92,7 +92,7 @@ button:focus-visible,input:focus-visible,select:focus-visible{outline:0;box-shad
.rf-actions .danger{background:#c92a2a;color:#fff}
.rf-actions .danger:disabled{opacity:.6}
:root[data-theme="dark"] .sale-row .nm b em{color:#ff6680}
:root[data-theme="dark"] .rf-sheet{background:#181818;box-shadow:0 20px 60px rgba(0,0,0,.6)}
:root[data-theme="dark"] .rf-sheet{background:var(--surface);box-shadow:0 20px 60px rgba(0,0,0,.6)}
:root[data-theme="dark"] .rf-head h3 small{color:#888}
:root[data-theme="dark"] .rf-chip{border-color:var(--muted);color:#888}
:root[data-theme="dark"] .rf-warn{background:#2a1a1a;border-color:#5a2429;color:#ff9db0}
@ -112,9 +112,9 @@ button:focus-visible,input:focus-visible,select:focus-visible{outline:0;box-shad
@media (prefers-color-scheme: dark){
:root:not([data-theme="light"]){
--bg:#0f0f0f;--surface:#1c1416;--surface-2:#181818;--surface-card:#181818;
--fg:#f0f0f0;--ink-soft:#a0a0a0;--ink-faint:#777;
--muted:#2a2a2a;--border:#3a2429;
--bg:#161619;--surface:#1e1e23;--surface-2:#1e1e23;--surface-card:#26262d;
--fg:#f2f2f5;--ink-soft:#a0a0a0;--ink-faint:#8e8e9a;
--muted:#303039;--border:#3b3b46;
--accent-soft:#2a1a1a;--accent-2:#3a2429;
--danger-soft:#2a1a1a;--danger-2:#3a2429;
}
@ -140,10 +140,10 @@ button:focus-visible,input:focus-visible,select:focus-visible{outline:0;box-shad
:root:not([data-theme="light"]) .empty p{color:var(--ink-soft)}
}
:root[data-theme="dark"]{
--bg:#0f0f0f;--surface:#1c1416;--surface-2:#181818;--surface-card:#181818;
--fg:#f0f0f0;--ink-soft:#a0a0a0;--ink-faint:#777;
--muted:#2a2a2a;--border:#3a2429;
--accent-soft:#2a1a1a;--accent-2:#3a2429;
--bg:#161619;--surface:#1e1e23;--surface-2:#1e1e23;--surface-card:#26262d;
--fg:#f2f2f5;--ink-soft:#a0a0a0;--ink-faint:#8e8e9a;
--muted:#303039;--border:#3b3b46;
--accent-soft:#2b1f24;--accent-2:#3f2b33;
--danger-soft:#2a1a1a;--danger-2:#3a2429;
background:var(--bg);color:var(--fg);
}

View file

@ -53,22 +53,22 @@ h1{font-size:28px;font-weight:700;letter-spacing:-.02em;line-height:1.15;margin:
:root[data-theme="dark"] .theme-btn .i-moon{display:block}
@media (prefers-color-scheme: dark){
:root:not([data-theme="light"]){--bg:#0f0f0f;--surface:#1c1416;--fg:#f0f0f0;--muted:#2a2a2a;--border:#3a2429}
:root:not([data-theme="light"]){--bg:#161619;--surface:#1e1e23;--fg:#f2f2f5;--muted:#303039;--border:#3b3b46}
:root:not([data-theme="light"]) .lede,:root:not([data-theme="light"]) .brand .sub,:root:not([data-theme="light"]) .card .desc,:root:not([data-theme="light"]) .foot{color:#a0a0a0}
:root:not([data-theme="light"]) .card{background:#181818}
:root:not([data-theme="light"]) .statusbar{background:#181818;border-color:#3a2429;color:#a0a0a0}
:root:not([data-theme="light"]) .pill{background:#181818;border-color:#3a2429;color:#f0f0f0}
:root:not([data-theme="light"]) .card{background:var(--surface)}
:root:not([data-theme="light"]) .statusbar{background:var(--surface);border-color:var(--border);color:#a0a0a0}
:root:not([data-theme="light"]) .pill{background:var(--surface);border-color:var(--border);color:#f0f0f0}
:root:not([data-theme="light"]) .pill .dot{box-shadow:0 0 0 3px rgba(15,138,63,.2)}
}
:root[data-theme="dark"]{
--bg:#0f0f0f;--surface:#1c1416;--fg:#f0f0f0;--muted:#2a2a2a;--border:#3a2429;
--bg:#161619;--surface:#1e1e23;--fg:#f2f2f5;--muted:#303039;--border:#3b3b46;
background:var(--bg);color:var(--fg);
}
:root[data-theme="dark"] body{background:var(--bg);color:var(--fg)}
:root[data-theme="dark"] .lede,:root[data-theme="dark"] .brand .sub,:root[data-theme="dark"] .card .desc,:root[data-theme="dark"] .foot{color:#a0a0a0}
:root[data-theme="dark"] .card{background:#181818}
:root[data-theme="dark"] .statusbar{background:#181818;border-color:#3a2429;color:#a0a0a0}
:root[data-theme="dark"] .pill{background:#181818;border-color:#3a2429;color:#f0f0f0}
:root[data-theme="dark"] .card{background:var(--surface)}
:root[data-theme="dark"] .statusbar{background:var(--surface);border-color:var(--border);color:#a0a0a0}
:root[data-theme="dark"] .pill{background:var(--surface);border-color:var(--border);color:#f0f0f0}
@media (prefers-reduced-motion: reduce){*{transition-duration:0.01ms !important;animation-duration:0.01ms !important}}
</style>
</head>

View file

@ -524,6 +524,11 @@ function renderCartDrawer(){
`− ${fmt(t.discountAmount)}${pctLabel} ex VAT`;
} else {
dl.hidden = true;
// Blank the figure as well as hiding the row. A discount that has
// been cleared must not leave its old amount sitting on screen —
// an operator seeing "− R 98.37" under a total that no longer
// contains it has no way to tell stale text from a live one.
document.getElementById('discountLineAmount').textContent = '';
}
}

View file

@ -12,10 +12,22 @@
:root{
--bg:#ffffff;--surface:#fff7f8;--fg:#222222;--muted:#dddddd;--border:#ffe3e8;--accent:#ff385c;
--ok:#0f8a3f;--warn:#b14a00;--danger:#c92a2a;
/* Text greys were hardcoded per-component, which is why the dark theme
had to restate 43 of them and why the greys drifted out of spec. One
token each; the dark block below rebinds them to lighter values so
they clear WCAG AA against a dark canvas. */
--muted-fg:#717171;--icon-fg:#888;--placeholder-fg:#666;
--shadow-up:0 -1px 0 var(--muted), 0 -8px 24px rgba(34,34,34,.06);
--font:'Inter',system-ui,-apple-system,Segoe UI,Helvetica Neue,Arial,sans-serif;
}
*{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}
/* Any class rule that sets `display` beats the UA stylesheet's
`[hidden]{display:none}`, so a hidden element stays visible and every
JS `.hidden = true/false` silently does nothing. `.cat-menu` and
`.cat-backdrop` already carry per-element overrides for this; this
one rule covers every element (and every future one) instead of
relying on someone remembering to add a twin. */
[hidden]{display:none !important}
html,body{height:100%;background:var(--bg);color:var(--fg);font:15px/1.45 var(--font);-webkit-font-smoothing:antialiased;overscroll-behavior:none;overflow-x:hidden}
body{display:flex;flex-direction:column;min-height:100dvh;max-width:480px;margin:0 auto;background:var(--bg);padding-bottom:env(safe-area-inset-bottom)}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
@ -29,14 +41,14 @@ input{font:inherit;color:inherit}
.topbar .back{width:36px;height:36px;border-radius:50%;display:grid;place-items:center;color:var(--fg)}
.topbar .back:active{background:var(--surface)}
.topbar h1{font-size:17px;font-weight:600;letter-spacing:-.01em;flex:1}
.topbar .netpill{font-size:11px;letter-spacing:.06em;text-transform:uppercase;font-weight:600;color:#717171;padding:4px 8px;border:1px solid var(--muted);border-radius:999px}
.topbar .netpill{font-size:11px;letter-spacing:.06em;text-transform:uppercase;font-weight:600;color:var(--muted-fg);padding:4px 8px;border:1px solid var(--muted);border-radius:999px}
.topbar .netpill.off{color:var(--danger);border-color:var(--danger);background:#fff5f5}
/* ---- Search ---- */
.search{padding:6px 16px 10px;background:var(--bg);position:sticky;top:60px;z-index:9}
.search-box{display:flex;align-items:center;gap:10px;padding:11px 14px;border:1px solid var(--muted);border-radius:12px;background:#fff;box-shadow:0 1px 2px rgba(34,34,34,.02)}
.search-box:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px rgba(255,56,92,.12)}
.search-box svg{width:18px;height:18px;color:#717171;flex:0 0 auto;stroke-width:1.7}
.search-box svg{width:18px;height:18px;color:var(--muted-fg);flex:0 0 auto;stroke-width:1.7}
.search-box input{flex:1;border:0;outline:0;background:transparent;font-size:15px;min-width:0}
.search-box input::placeholder{color:#9a9a9a}
@ -45,9 +57,9 @@ input{font:inherit;color:inherit}
.cat-trigger{display:flex;align-items:center;gap:8px;padding:10px 14px;border:1px solid var(--muted);border-radius:10px;background:#fff;font-size:14px;font-weight:500;color:var(--fg);min-height:42px;max-width:65%;letter-spacing:-.005em}
.cat-trigger:active{background:var(--surface)}
.cat-trigger .cat-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cat-trigger .cat-chev{width:16px;height:16px;flex:0 0 auto;color:#717171;transition:transform .18s}
.cat-trigger .cat-chev{width:16px;height:16px;flex:0 0 auto;color:var(--muted-fg);transition:transform .18s}
.cat-trigger[aria-expanded="true"] .cat-chev{transform:rotate(180deg)}
.cat-count{font-size:12px;color:#717171;letter-spacing:.01em;font-variant-numeric:tabular-nums}
.cat-count{font-size:12px;color:var(--muted-fg);letter-spacing:.01em;font-variant-numeric:tabular-nums}
.cat-menu{position:fixed;left:16px;right:16px;top:auto;background:#fff;border:1px solid var(--muted);border-radius:14px;box-shadow:0 12px 40px rgba(34,34,34,.18);z-index:60;max-width:448px;margin:0 auto;overflow:hidden;transform-origin:top center;animation:catIn .15s ease}
@keyframes catIn{from{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}
@ -57,7 +69,7 @@ input{font:inherit;color:inherit}
.cat-item:last-child{border-bottom:0}
.cat-item:active{background:var(--surface)}
.cat-item .nm{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cat-item .ct{font-size:12px;color:#717171;font-variant-numeric:tabular-nums;letter-spacing:.02em;flex:0 0 auto}
.cat-item .ct{font-size:12px;color:var(--muted-fg);font-variant-numeric:tabular-nums;letter-spacing:.02em;flex:0 0 auto}
.cat-item.active{background:var(--surface);color:var(--accent)}
.cat-item.active .ct{color:var(--accent)}
.cat-item.active::before{content:'';width:6px;height:6px;border-radius:50%;background:var(--accent);flex:0 0 auto}
@ -67,10 +79,10 @@ input{font:inherit;color:inherit}
/* ---- Catalog list ---- */
.catalog{flex:1;overflow-y:auto;-webkit-overflow-scrolling:touch;padding:0 16px 220px}
.catalog .meta{padding:12px 0 6px;font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:#717171;display:flex;justify-content:space-between}
.catalog .meta{padding:12px 0 6px;font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--muted-fg);display:flex;justify-content:space-between}
.catalog .empty{padding:60px 20px;text-align:center;color:#9a9a9a}
.catalog .more{display:flex;flex-direction:column;align-items:center;gap:2px;width:100%;margin:14px 0 8px;padding:14px;border:1px solid var(--muted);border-radius:12px;background:var(--surface-card);color:var(--fg);font-size:14px;font-weight:600;letter-spacing:-.005em}
.catalog .more small{font-size:11px;font-weight:500;color:#717171;letter-spacing:.02em}
.catalog .more small{font-size:11px;font-weight:500;color:var(--muted-fg);letter-spacing:.02em}
.catalog .more:active{background:var(--surface)}
.catalog .empty h3{font-size:16px;font-weight:600;color:var(--fg);margin-bottom:6px;letter-spacing:-.01em}
.catalog .empty p{font-size:13px;line-height:1.5}
@ -81,16 +93,16 @@ input{font:inherit;color:inherit}
.prod .ic .ic-fa{font-size:18px;line-height:1;color:var(--accent)}
.prod .body{flex:1;min-width:0;padding-right:8px}
.prod .name{font-size:14px;font-weight:500;line-height:1.3;letter-spacing:-.005em;color:var(--fg);overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.prod .sub{font-size:11px;color:#717171;margin-top:2px;letter-spacing:.01em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.prod .sub{font-size:11px;color:var(--muted-fg);margin-top:2px;letter-spacing:.01em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.prod .price{font-size:14px;font-weight:600;letter-spacing:-.005em;color:var(--fg);font-variant-numeric:tabular-nums;white-space:nowrap}
.prod .price small{font-size:11px;color:#717171;font-weight:500;margin-left:2px}
.prod .price small{font-size:11px;color:var(--muted-fg);font-weight:500;margin-left:2px}
/* ---- Cart bottom sheet ---- */
.cart-bar{position:fixed;left:0;right:0;bottom:0;background:#fff;border-top:1px solid var(--muted);padding:12px 16px calc(12px + env(safe-area-inset-bottom));z-index:20;display:flex;align-items:center;gap:12px;box-shadow:var(--shadow-up);max-width:480px;margin:0 auto;transform:translateY(100%);transition:transform .22s cubic-bezier(.2,.8,.2,1)}
.cart-bar.show{transform:translateY(0)}
.cart-bar .count{display:flex;flex-direction:column;line-height:1.15}
.cart-bar .count b{font-size:20px;font-weight:700;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.cart-bar .count span{font-size:11px;color:#717171;letter-spacing:.04em;text-transform:uppercase}
.cart-bar .count span{font-size:11px;color:var(--muted-fg);letter-spacing:.04em;text-transform:uppercase}
.cart-bar .total{margin-left:auto;font-size:20px;font-weight:700;letter-spacing:-.02em;color:var(--fg);font-variant-numeric:tabular-nums}
.cart-bar .checkout{padding:14px 22px;background:var(--accent);color:#fff;border-radius:12px;font-weight:600;font-size:15px;letter-spacing:-.005em;min-height:48px;display:flex;align-items:center;gap:6px}
.cart-bar .checkout:active{background:#e5324f}
@ -107,7 +119,7 @@ input{font:inherit;color:inherit}
.sheet-body{flex:1;overflow-y:auto;-webkit-overflow-scrolling:touch;padding:4px 0}
.line{display:flex;align-items:center;gap:12px;padding:14px 20px;border-bottom:1px solid var(--muted)}
.line .nm{flex:1;min-width:0;font-size:14px;font-weight:500;line-height:1.3;letter-spacing:-.005em}
.line .nm small{display:block;font-size:11px;color:#717171;margin-top:2px;letter-spacing:.01em}
.line .nm small{display:block;font-size:11px;color:var(--muted-fg);margin-top:2px;letter-spacing:.01em}
.qty{display:flex;align-items:center;border:1px solid var(--muted);border-radius:10px;overflow:hidden}
.qty button{width:34px;height:34px;display:grid;place-items:center;color:var(--fg);font-size:16px;font-weight:600}
.qty button:active{background:var(--surface)}
@ -117,7 +129,7 @@ input{font:inherit;color:inherit}
.line .rm{width:32px;height:32px;border-radius:50%;color:#9a9a9a;display:grid;place-items:center}
.line .rm:active{background:#fff0f0;color:var(--danger)}
.sheet-foot{padding:14px 20px 18px;border-top:1px solid var(--muted);background:#fff}
.totals{display:flex;justify-content:space-between;padding:6px 0;font-size:14px;color:#717171;letter-spacing:.01em}
.totals{display:flex;justify-content:space-between;padding:6px 0;font-size:14px;color:var(--muted-fg);letter-spacing:.01em}
.totals b{color:var(--fg);font-variant-numeric:tabular-nums}
.totals.grand{padding-top:12px;margin-top:8px;border-top:1px solid var(--muted);font-size:17px;color:var(--fg);font-weight:700;letter-spacing:-.01em}
@ -125,7 +137,7 @@ input{font:inherit;color:inherit}
/* A dashed outline rather than a filled button: this is a secondary
action next to the real catalogue, and it should read as "something
extra" rather than compete with the checkout CTA below it. */
.custom-item-btn{width:100%;margin-bottom:12px;padding:10px 12px;border:1px dashed #c9c9c9;border-radius:9px;background:none;color:#717171;font-size:13px;font-weight:600;letter-spacing:.01em;text-align:center;cursor:pointer}
.custom-item-btn{width:100%;margin-bottom:12px;padding:10px 12px;border:1px dashed #c9c9c9;border-radius:9px;background:none;color:var(--muted-fg);font-size:13px;font-weight:600;letter-spacing:.01em;text-align:center;cursor:pointer}
.custom-item-btn:active{background:#f4f4f4}
/* Ties a custom line back to the "not stock" meaning at a glance, and
stops anyone mistaking it for a catalogue item on a stocktake. */
@ -137,7 +149,7 @@ input{font:inherit;color:inherit}
adds no vertical height at all — the footer keeps every pixel it had
before. It stays visible rather than collapsing away, because a discount
control that appears from nowhere is one nobody finds under pressure. */
.discount-trigger{display:inline-flex;align-items:center;gap:3px;margin-left:8px;padding:3px 6px;border:0;border-radius:6px;background:none;color:#717171;font-size:12px;font-weight:600;letter-spacing:.01em;vertical-align:middle}
.discount-trigger{display:inline-flex;align-items:center;gap:3px;margin-left:8px;padding:3px 6px;border:0;border-radius:6px;background:none;color:var(--muted-fg);font-size:12px;font-weight:600;letter-spacing:.01em;vertical-align:middle}
.discount-trigger:active{background:var(--surface);color:var(--fg)}
.discount-trigger svg{width:13px;height:13px;color:inherit;transition:transform .18s}
.discount-trigger[aria-expanded="true"] svg{transform:rotate(180deg)}
@ -145,15 +157,15 @@ input{font:inherit;color:inherit}
.discount-trigger.has-value svg{color:var(--accent)}
.discount-body{padding:12px 0 4px;display:flex;flex-direction:column;gap:10px}
.discount-pills{display:flex;gap:6px;padding:3px;background:var(--surface);border:1px solid var(--muted);border-radius:999px;width:fit-content}
.discount-pill{padding:7px 16px;border-radius:999px;font-size:13px;font-weight:600;letter-spacing:-.005em;color:#717171;min-height:34px}
.discount-pill{padding:7px 16px;border-radius:999px;font-size:13px;font-weight:600;letter-spacing:-.005em;color:var(--muted-fg);min-height:34px}
.discount-pill.active{background:#fff;color:var(--accent);box-shadow:0 1px 2px rgba(34,34,34,.06)}
.discount-input-row{display:flex;align-items:center;gap:8px}
.discount-input-row input{flex:1 1 0;min-width:0;padding:11px 12px;border:1px solid var(--muted);border-radius:10px;font-size:15px;font-weight:600;text-align:right;font-variant-numeric:tabular-nums;outline:0;background:#fff}
.discount-input-row input:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(255,56,92,.12)}
.discount-suffix{font-size:13px;font-weight:600;color:#717171;width:18px;text-align:left;letter-spacing:-.005em}
.discount-suffix{font-size:13px;font-weight:600;color:var(--muted-fg);width:18px;text-align:left;letter-spacing:-.005em}
.discount-apply{padding:11px 16px;background:var(--accent);color:#fff;border-radius:10px;font-size:13px;font-weight:600;letter-spacing:-.005em;min-height:42px;min-width:78px}
.discount-apply:active{background:#e5324f}
.discount-hint{font-size:11px;color:#717171;margin:0;letter-spacing:.01em}
.discount-hint{font-size:11px;color:var(--muted-fg);margin:0;letter-spacing:.01em}
.totals.discount-line{color:var(--danger)}
.totals.discount-line b{color:var(--danger)}
@ -170,9 +182,9 @@ input{font:inherit;color:inherit}
.edit-sheet{position:fixed;left:50%;bottom:24px;z-index:71;transform:translate(-50%, 24px);background:#fff;border-radius:18px;width:calc(100% - 32px);max-width:448px;padding:18px 18px 14px;opacity:0;transition:opacity .15s, transform .22s cubic-bezier(.2,.8,.2,1);box-shadow:0 20px 60px rgba(34,34,34,.30);pointer-events:none;display:flex;flex-direction:column;gap:12px;max-height:80dvh;overflow-y:auto;-webkit-overflow-scrolling:touch;padding-bottom:calc(14px + env(safe-area-inset-bottom))}
.edit-sheet.show{opacity:1;transform:translate(-50%, 0);pointer-events:auto}
.edit-sheet h3{font-size:15px;font-weight:600;letter-spacing:-.01em;margin:0;display:flex;align-items:center;gap:8px}
.edit-sheet h3 small{font-size:11px;font-weight:500;color:#717171;letter-spacing:.02em;margin-left:auto}
.edit-sheet h3 small{font-size:11px;font-weight:500;color:var(--muted-fg);letter-spacing:.02em;margin-left:auto}
.edit-row{display:flex;flex-direction:column;gap:6px}
.edit-row label{font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:#717171}
.edit-row label{font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--muted-fg)}
.edit-row input{width:100%;box-sizing:border-box;padding:12px 12px;border:1px solid var(--muted);border-radius:10px;font-size:16px;font-weight:600;text-align:right;font-variant-numeric:tabular-nums;outline:0;background:#fff}
.edit-row input:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(255,56,92,.12)}
.edit-actions{display:flex;gap:8px;margin-top:4px}
@ -194,7 +206,7 @@ input{font:inherit;color:inherit}
.pay-tile{padding:18px 14px;border:1.5px solid var(--muted);border-radius:14px;background:#fff;text-align:left;min-height:92px;display:flex;flex-direction:column;justify-content:space-between}
.pay-tile:active{background:var(--surface)}
.pay-tile .lab{font-size:14px;font-weight:600;letter-spacing:-.01em}
.pay-tile .sub{font-size:11px;color:#717171;margin-top:2px;letter-spacing:.01em}
.pay-tile .sub{font-size:11px;color:var(--muted-fg);margin-top:2px;letter-spacing:.01em}
.pay-tile .ic{width:32px;height:32px;border-radius:8px;background:var(--surface);color:var(--accent);display:grid;place-items:center;margin-bottom:8px}
.pay-tile .ic svg{width:18px;height:18px;stroke-width:1.7}
.pay-tile.selected{border-color:var(--accent);background:#fff8fa}
@ -204,7 +216,7 @@ input{font:inherit;color:inherit}
.cash-block.show{display:block}
.cash-row{display:flex;gap:8px;margin-bottom:10px;min-width:0}
.cash-row .field{flex:1 1 0;min-width:0;display:flex;flex-direction:column}
.cash-row label{font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:#717171;margin-bottom:4px}
.cash-row label{font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--muted-fg);margin-bottom:4px}
.cash-row input{width:100%;box-sizing:border-box;padding:12px 10px;border:1px solid var(--muted);border-radius:10px;font-size:16px;font-weight:600;text-align:right;font-variant-numeric:tabular-nums;outline:0;background:#fff;min-width:0}
.cash-row input:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(255,56,92,.12)}
.quick{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:14px}
@ -212,7 +224,7 @@ input{font:inherit;color:inherit}
.quick button:active{background:var(--surface);border-color:#bbb}
.change-out{padding:14px;border-radius:12px;background:var(--surface);border:1px solid var(--border);text-align:center;min-height:64px;display:flex;flex-direction:column;justify-content:center;opacity:0;max-height:0;padding-top:0;padding-bottom:0;overflow:hidden;transition:opacity .15s, max-height .2s, padding .2s, border-width .2s;border-width:0}
.change-out.show{opacity:1;max-height:120px;padding:14px;border-width:1px}
.change-out .lbl{font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:#717171}
.change-out .lbl{font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--muted-fg)}
.change-out .amt{font-size:32px;font-weight:700;letter-spacing:-.02em;color:var(--ok);font-variant-numeric:tabular-nums;margin-top:4px}
.change-out.short .amt{color:var(--danger)}
@ -226,11 +238,11 @@ input{font:inherit;color:inherit}
.cust-results div{padding:10px 14px;border-bottom:1px solid var(--muted);font-size:14px;display:flex;align-items:center;gap:8px;cursor:pointer}
.cust-results div:last-child{border-bottom:0}
.cust-results div:active{background:var(--surface)}
.cust-results small{color:#717171;font-size:11px;margin-left:auto;letter-spacing:.02em}
.cust-results small{color:var(--muted-fg);font-size:11px;margin-left:auto;letter-spacing:.02em}
/* Count header + empty state. Both are divs inside .cust-results, so they
need the row cursor/padding neutralised explicitly. */
.cust-results .cust-count{padding:7px 14px;background:var(--surface);border-bottom:1px solid var(--muted);font-size:11px;font-weight:600;letter-spacing:.02em;color:#717171;cursor:default}
.cust-results .cust-none{padding:11px 14px;font-size:13px;color:#717171;cursor:default}
.cust-results .cust-count{padding:7px 14px;background:var(--surface);border-bottom:1px solid var(--muted);font-size:11px;font-weight:600;letter-spacing:.02em;color:var(--muted-fg);cursor:default}
.cust-results .cust-none{padding:11px 14px;font-size:13px;color:var(--muted-fg);cursor:default}
.cust-results .cust-count:active,.cust-results .cust-none:active{background:none}
.cust-picked{padding:10px 12px;background:var(--surface);border:1px solid var(--border);border-radius:10px;font-size:13px;display:none;align-items:center;gap:8px;margin-bottom:8px}
.cust-picked.show{display:flex}
@ -253,56 +265,70 @@ input{font:inherit;color:inherit}
:root[data-theme="auto"] .theme-btn .i-sun{display:block}
/* ---- dark theme (auto follows prefers-color-scheme) ---- */
/* Dark ramp. The previous canvas was #0f0f0f (L* 4.3 — effectively black)
with surfaces only 3-4 L* above it, and mixed a warm #1c1416 against a
neutral #181818 in the same UI. That near-black canvas plus two
competing surface tones is what read as "muddy". This is a single
cool-neutral ramp with four real steps, and the text greys are lifted
so the most-used one clears WCAG AA (was 3.93:1, now 6.49:1). */
:root[data-theme="dark"]{
--bg:#0f0f0f; --surface:#1c1416; --fg:#f0f0f0; --muted:#2a2a2a; --border:#3a2429;
--bg:#161619; /* L* 7.4 — lifted off black */
--surface:#1e1e23; /* L* 11.5 — cards, drawers, fields */
--raised:#26262d; /* L* 15.4 — menus and modals sit above surfaces */
--fg:#f2f2f5;
--muted:#303039; /* L* 20.2 — separators */
--border:#3b3b46; /* L* 25.3 — outlines */
--muted-fg:#9a9aa6; /* secondary text 6.49:1 on --bg (AA) */
--icon-fg:#8e8e9a; /* icons / labels 5.58:1 on --bg (AA) */
--placeholder-fg:#7c7c88; /* placeholder 4.38:1 */
--shadow-up:0 -1px 0 var(--muted), 0 -8px 24px rgba(0,0,0,.4);
}
:root[data-theme="dark"] body{background:var(--bg);color:var(--fg)}
:root[data-theme="dark"] .topbar{background:var(--bg);border-bottom-color:var(--muted)}
:root[data-theme="dark"] .search-box{background:#181818;border-color:var(--muted)}
:root[data-theme="dark"] .search-box{background:var(--surface);border-color:var(--muted)}
:root[data-theme="dark"] .search-box:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px rgba(255,56,92,.2)}
:root[data-theme="dark"] .search-box svg{color:#888}
:root[data-theme="dark"] .search-box svg{color:var(--icon-fg)}
:root[data-theme="dark"] .search-box input{color:var(--fg)}
:root[data-theme="dark"] .search-box input::placeholder{color:#666}
:root[data-theme="dark"] .chip{background:#181818;border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .search-box input::placeholder{color:var(--placeholder-fg)}
:root[data-theme="dark"] .chip{background:var(--surface);border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .chip.active{background:var(--fg);color:var(--bg);border-color:var(--fg)}
:root[data-theme="dark"] .cat-trigger{background:#181818;border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .cat-trigger .cat-chev{color:#888}
:root[data-theme="dark"] .cat-count{color:#888}
:root[data-theme="dark"] .cat-menu{background:#181818;border-color:var(--muted);box-shadow:0 12px 40px rgba(0,0,0,.5)}
:root[data-theme="dark"] .cat-trigger{background:var(--surface);border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .cat-trigger .cat-chev{color:var(--icon-fg)}
:root[data-theme="dark"] .cat-count{color:var(--icon-fg)}
:root[data-theme="dark"] .cat-menu{background:var(--raised);border-color:var(--muted);box-shadow:0 12px 40px rgba(0,0,0,.5)}
:root[data-theme="dark"] .cat-item{color:var(--fg);border-bottom-color:var(--muted)}
:root[data-theme="dark"] .cat-item:active{background:var(--surface)}
:root[data-theme="dark"] .cat-item .ct{color:#888}
:root[data-theme="dark"] .cat-item .ct{color:var(--icon-fg)}
:root[data-theme="dark"] .cat-item.active{background:var(--surface);color:var(--accent)}
:root[data-theme="dark"] .prod{border-bottom-color:var(--muted)}
:root[data-theme="dark"] .prod:active{background:var(--surface)}
:root[data-theme="dark"] .prod .ic{background:var(--surface);border-color:var(--border);color:var(--accent)}
:root[data-theme="dark"] .prod .name{color:var(--fg)}
:root[data-theme="dark"] .prod .sub{color:#888}
:root[data-theme="dark"] .prod .sub{color:var(--icon-fg)}
:root[data-theme="dark"] .prod .price{color:var(--fg)}
:root[data-theme="dark"] .prod .price small{color:#888}
:root[data-theme="dark"] .cart-bar{background:#181818;border-top-color:var(--muted);box-shadow:var(--shadow-up)}
:root[data-theme="dark"] .cart-bar .count span{color:#888}
:root[data-theme="dark"] .prod .price small{color:var(--icon-fg)}
:root[data-theme="dark"] .cart-bar{background:var(--surface);border-top-color:var(--muted);box-shadow:var(--shadow-up)}
:root[data-theme="dark"] .cart-bar .count span{color:var(--icon-fg)}
:root[data-theme="dark"] .cart-bar .total{color:var(--fg)}
:root[data-theme="dark"] .drawer{background:rgba(0,0,0,.7)}
:root[data-theme="dark"] .sheet{background:#181818}
:root[data-theme="dark"] .sheet{background:var(--surface)}
:root[data-theme="dark"] .sheet-head{border-bottom-color:var(--muted)}
:root[data-theme="dark"] .sheet-head .x{background:var(--surface);color:var(--fg)}
:root[data-theme="dark"] .line{border-bottom-color:var(--muted)}
:root[data-theme="dark"] .line .nm{color:var(--fg)}
:root[data-theme="dark"] .line .nm small{color:#888}
:root[data-theme="dark"] .line .nm small{color:var(--icon-fg)}
:root[data-theme="dark"] .qty{border-color:var(--muted)}
:root[data-theme="dark"] .qty button{color:var(--fg)}
:root[data-theme="dark"] .qty button:active{background:var(--surface)}
:root[data-theme="dark"] .qty .v{color:var(--fg)}
:root[data-theme="dark"] .line .pr{color:var(--fg)}
:root[data-theme="dark"] .line .rm{color:#666}
:root[data-theme="dark"] .line .rm{color:var(--placeholder-fg)}
:root[data-theme="dark"] .line .rm:active{background:#3a2429;color:var(--accent)}
:root[data-theme="dark"] .sheet-foot{background:#181818;border-top-color:var(--muted)}
:root[data-theme="dark"] .totals{color:#888}
:root[data-theme="dark"] .sheet-foot{background:var(--surface);border-top-color:var(--muted)}
:root[data-theme="dark"] .totals{color:var(--icon-fg)}
:root[data-theme="dark"] .totals b{color:var(--fg)}
:root[data-theme="dark"] .totals.grand{border-top-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .discount-trigger{color:#888}
:root[data-theme="dark"] .discount-trigger{color:var(--icon-fg)}
:root[data-theme="dark"] .custom-item-btn{border-color:#3a3a3a;color:#999}
:root[data-theme="dark"] .custom-item-btn:active{background:#222}
:root[data-theme="dark"] .custom-line .nm{color:#d2b47c}
@ -310,53 +336,53 @@ input{font:inherit;color:inherit}
:root[data-theme="dark"] .discount-trigger:active{color:var(--fg)}
:root[data-theme="dark"] .discount-trigger.has-value{color:#ff6680}
:root[data-theme="dark"] .discount-pills{background:#222;border-color:var(--muted)}
:root[data-theme="dark"] .discount-pill{color:#888}
:root[data-theme="dark"] .discount-pill.active{background:#181818;color:#ff6680;box-shadow:0 1px 2px rgba(0,0,0,.4)}
:root[data-theme="dark"] .discount-input-row input{background:#181818;border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .discount-pill{color:var(--icon-fg)}
:root[data-theme="dark"] .discount-pill.active{background:var(--surface);color:#ff6680;box-shadow:0 1px 2px rgba(0,0,0,.4)}
:root[data-theme="dark"] .discount-input-row input{background:var(--surface);border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .discount-input-row input:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(255,56,92,.2)}
:root[data-theme="dark"] .discount-suffix{color:#888}
:root[data-theme="dark"] .discount-hint{color:#888}
:root[data-theme="dark"] .discount-suffix{color:var(--icon-fg)}
:root[data-theme="dark"] .discount-hint{color:var(--icon-fg)}
:root[data-theme="dark"] .totals.discount-line{color:#ff6680}
:root[data-theme="dark"] .totals.discount-line b{color:#ff6680}
:root[data-theme="dark"] .empty{color:#666}
:root[data-theme="dark"] .empty{color:var(--placeholder-fg)}
:root[data-theme="dark"] .empty h3{color:var(--fg)}
:root[data-theme="dark"] .empty p{color:#888}
:root[data-theme="dark"] .netpill{background:transparent;color:#888;border-color:var(--muted)}
:root[data-theme="dark"] .empty p{color:var(--icon-fg)}
:root[data-theme="dark"] .netpill{background:transparent;color:var(--icon-fg);border-color:var(--muted)}
:root[data-theme="dark"] .netpill.off{color:#ff6680;border-color:#ff6680;background:#2a1a1a}
:root[data-theme="dark"] .back:active{background:var(--surface)}
:root[data-theme="dark"] .pay-tile{background:#181818;border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .pay-tile{background:var(--surface);border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .pay-tile:active{background:var(--surface)}
:root[data-theme="dark"] .pay-tile .ic{background:var(--surface);color:var(--accent)}
:root[data-theme="dark"] .pay-tile.selected{border-color:var(--accent);background:#2a1a1a}
:root[data-theme="dark"] .pay-tile.selected .ic{background:var(--accent);color:#fff}
:root[data-theme="dark"] .cash-row input{background:#181818;border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .cash-row input{background:var(--surface);border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .cash-row input:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(255,56,92,.2)}
:root[data-theme="dark"] .cash-row input[readonly]{background:#222;color:#aaa}
:root[data-theme="dark"] .cash-row label{color:#888}
:root[data-theme="dark"] .quick button{background:#181818;border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .cash-row label{color:var(--icon-fg)}
:root[data-theme="dark"] .quick button{background:var(--surface);border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .quick button:active{background:var(--surface);border-color:#444}
:root[data-theme="dark"] .change-out{background:var(--surface);border-color:var(--border);color:var(--fg)}
:root[data-theme="dark"] .change-out .lbl{color:#888}
:root[data-theme="dark"] .cust-block input{background:#181818;border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .change-out .lbl{color:var(--icon-fg)}
:root[data-theme="dark"] .cust-block input{background:var(--surface);border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .cust-block input:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(255,56,92,.2)}
:root[data-theme="dark"] .cust-results{background:#181818;border-color:var(--muted)}
:root[data-theme="dark"] .cust-results{background:var(--surface);border-color:var(--muted)}
:root[data-theme="dark"] .cust-results div{border-bottom-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .cust-results div:active{background:var(--surface)}
:root[data-theme="dark"] .cust-results small{color:#888}
:root[data-theme="dark"] .cust-results .cust-count{background:#222;border-bottom-color:var(--muted);color:#888}
:root[data-theme="dark"] .cust-results .cust-none{color:#888}
:root[data-theme="dark"] .cust-results small{color:var(--icon-fg)}
:root[data-theme="dark"] .cust-results .cust-count{background:#222;border-bottom-color:var(--muted);color:var(--icon-fg)}
:root[data-theme="dark"] .cust-results .cust-none{color:var(--icon-fg)}
:root[data-theme="dark"] .cust-picked{background:var(--surface);border-color:var(--border);color:var(--fg)}
:root[data-theme="dark"] .edit-sheet{background:#181818;box-shadow:0 20px 60px rgba(0,0,0,.6)}
:root[data-theme="dark"] .edit-sheet h3 small{color:#888}
:root[data-theme="dark"] .edit-row label{color:#888}
:root[data-theme="dark"] .edit-row input{background:#181818;border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .edit-sheet{background:var(--raised);box-shadow:0 20px 60px rgba(0,0,0,.6)}
:root[data-theme="dark"] .edit-sheet h3 small{color:var(--icon-fg)}
:root[data-theme="dark"] .edit-row label{color:var(--icon-fg)}
:root[data-theme="dark"] .edit-row input{background:var(--surface);border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .edit-row input:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(255,56,92,.2)}
:root[data-theme="dark"] .edit-actions .secondary{background:#181818;border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .edit-actions .secondary{background:var(--surface);border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .edit-actions .secondary:active{background:var(--surface)}
:root[data-theme="dark"] .edit-actions .danger{background:#181818;border-color:#5a2429;color:#ff6680}
:root[data-theme="dark"] .edit-actions .danger{background:var(--surface);border-color:#5a2429;color:#ff6680}
:root[data-theme="dark"] .edit-actions .danger:active{background:#2a1a1a}
:root[data-theme="dark"] .line-holding{background:var(--surface)}
:root[data-theme="dark"] .final-cta:disabled{background:#2a2a2a;color:#666}
:root[data-theme="dark"] .final-cta:disabled{background:#2a2a2a;color:var(--placeholder-fg)}
@media (prefers-reduced-motion: reduce){*{transition-duration:0.01ms !important;animation-duration:0.01ms !important}}
</style>
</head>

View file

@ -152,9 +152,9 @@ input,select{font:inherit;color:inherit}
/* ---- Dark theme ---- */
@media (prefers-color-scheme: dark){
:root:not([data-theme="light"]){
--bg:#0f0f0f;--surface:#1c1416;--surface-2:#181818;--fg:#f0f0f0;--muted:#2a2a2a;--border:#3a2429;
--ink:#f0f0f0;--ink-soft:#a0a0a0;--ink-faint:#777;
--surface-card:#181818;--accent-soft:#2a1a1a;--accent-2:#3a2429;--accent-soft-hover:#3a2429;
--bg:#161619;--surface:#1e1e23;--surface-2:#1e1e23;--fg:#f2f2f5;--muted:#303039;--border:#3b3b46;
--ink:#f2f2f5;--ink-soft:#a0a0a0;--ink-faint:#8e8e9a;
--surface-card:#26262d;--accent-soft:#2a1a1a;--accent-2:#3a2429;--accent-soft-hover:#3a2429;
--ok-soft:#1a2e22;--danger-soft:#2a1a1a;--danger-2:#3a2429;
--focus-ring:0 0 0 3px rgba(255,102,128,.35);
}
@ -203,9 +203,9 @@ input,select{font:inherit;color:inherit}
:root:not([data-theme="light"]) .btn.acct:active{background:var(--accent-soft-hover)}
}
:root[data-theme="dark"]{
--bg:#0f0f0f;--surface:#1c1416;--surface-2:#181818;--fg:#f0f0f0;--muted:#2a2a2a;--border:#3a2429;
--ink:#f0f0f0;--ink-soft:#a0a0a0;--ink-faint:#777;
--surface-card:#181818;--accent-soft:#2a1a1a;--accent-2:#3a2429;--accent-soft-hover:#3a2429;
--bg:#161619;--surface:#1e1e23;--surface-2:#1e1e23;--fg:#f2f2f5;--muted:#303039;--border:#3b3b46;
--ink:#f2f2f5;--ink-soft:#a0a0a0;--ink-faint:#8e8e9a;
--surface-card:#26262d;--accent-soft:#2b1f24;--accent-2:#3f2b33;--accent-soft-hover:#452f38;
--ok-soft:#1a2e22;--danger-soft:#2a1a1a;--danger-2:#3a2429;
--focus-ring:0 0 0 3px rgba(255,102,128,.35);
}

View file

@ -78,7 +78,7 @@ body{display:flex;flex-direction:column;min-height:100dvh;max-width:480px;margin
/* ---- Dark theme ---- */
@media (prefers-color-scheme: dark){
:root:not([data-theme="light"]){
--bg:#0f0f0f;--surface:#1c1416;--fg:#f0f0f0;--muted:#2a2a2a;--border:#3a2429;
--bg:#161619;--surface:#1e1e23;--fg:#f2f2f5;--muted:#303039;--border:#3b3b46;
}
:root:not([data-theme="light"]) body{background:var(--bg);color:var(--fg)}
:root:not([data-theme="light"]) .topbar{background:var(--bg)}
@ -89,22 +89,22 @@ body{display:flex;flex-direction:column;min-height:100dvh;max-width:480px;margin
:root:not([data-theme="light"]) .topbar .theme-btn{color:var(--fg)}
:root:not([data-theme="light"]) .section-label{color:#a0a0a0}
:root:not([data-theme="light"]) .active{background:#2a1a1a;border-color:#ff385c}
:root:not([data-theme="light"]) .active .logo{background:#181818;border-color:var(--muted)}
:root:not([data-theme="light"]) .active .logo{background:var(--surface);border-color:var(--muted)}
:root:not([data-theme="light"]) .active .name{color:var(--fg)}
:root:not([data-theme="light"]) .lh-card{background:#181818;border-color:var(--muted);color:var(--fg)}
:root:not([data-theme="light"]) .lh-card{background:var(--surface);border-color:var(--muted);color:var(--fg)}
:root:not([data-theme="light"]) .lh-card:active{background:var(--surface)}
:root:not([data-theme="light"]) .lh-card.selected{background:#2a1a1a;border-color:#ff385c}
:root:not([data-theme="light"]) .lh-card .logo{background:#181818;border-color:var(--muted)}
:root:not([data-theme="light"]) .lh-card .logo{background:var(--surface);border-color:var(--muted)}
:root:not([data-theme="light"]) .lh-card .meta{color:#a0a0a0}
:root:not([data-theme="light"]) .lh-card:not(.selected) .check{border-color:var(--muted)}
:root:not([data-theme="light"]) .btn{background:#181818;border-color:var(--muted);color:var(--fg)}
:root:not([data-theme="light"]) .btn{background:var(--surface);border-color:var(--muted);color:var(--fg)}
:root:not([data-theme="light"]) .btn:active{background:var(--surface)}
:root:not([data-theme="light"]) .banner.warn{background:#2a1a1a;border-color:#5a3a1a;color:#ffb060}
:root:not([data-theme="light"]) .banner.err{background:#2a1a1a;border-color:#5a2429;color:#ff6680}
:root:not([data-theme="light"]) .skeleton{color:#666}
}
:root[data-theme="dark"]{
--bg:#0f0f0f;--surface:#1c1416;--fg:#f0f0f0;--muted:#2a2a2a;--border:#3a2429;
--bg:#161619;--surface:#1e1e23;--fg:#f2f2f5;--muted:#303039;--border:#3b3b46;
background:var(--bg);color:var(--fg);
}
:root[data-theme="dark"] body{background:var(--bg);color:var(--fg)}
@ -116,15 +116,15 @@ body{display:flex;flex-direction:column;min-height:100dvh;max-width:480px;margin
:root[data-theme="dark"] .topbar .theme-btn{color:var(--fg)}
:root[data-theme="dark"] .section-label{color:#a0a0a0}
:root[data-theme="dark"] .active{background:#2a1a1a;border-color:#ff385c}
:root[data-theme="dark"] .active .logo{background:#181818;border-color:var(--muted)}
:root[data-theme="dark"] .active .logo{background:var(--surface);border-color:var(--muted)}
:root[data-theme="dark"] .active .name{color:var(--fg)}
:root[data-theme="dark"] .lh-card{background:#181818;border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .lh-card{background:var(--surface);border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .lh-card:active{background:var(--surface)}
:root[data-theme="dark"] .lh-card.selected{background:#2a1a1a;border-color:#ff385c}
:root[data-theme="dark"] .lh-card .logo{background:#181818;border-color:var(--muted)}
:root[data-theme="dark"] .lh-card .logo{background:var(--surface);border-color:var(--muted)}
:root[data-theme="dark"] .lh-card .meta{color:#a0a0a0}
:root[data-theme="dark"] .lh-card:not(.selected) .check{border-color:var(--muted)}
:root[data-theme="dark"] .btn{background:#181818;border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .btn{background:var(--surface);border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"] .btn:active{background:var(--surface)}
:root[data-theme="dark"] .banner.warn{background:#2a1a1a;border-color:#5a3a1a;color:#ffb060}
:root[data-theme="dark"] .banner.err{background:#2a1a1a;border-color:#5a2429;color:#ff6680}

View file

@ -56,11 +56,11 @@ body{display:flex;flex-direction:column;min-height:100dvh;max-width:480px;margin
.block-row .edit-text{width:100%;margin-top:6px;padding:8px 10px;border-radius:8px;border:1px solid var(--muted);font:inherit}
.pricing-row{display:flex;align-items:center;justify-content:space-between;padding:12px;border:1px solid var(--muted);border-radius:10px;background:#fff;margin:4px 0 16px}
@media (prefers-color-scheme: dark){
:root:not([data-theme="light"]){--bg:#0f0f0f;--surface:#1c1416;--fg:#f0f0f0;--muted:#2a2a2a;--border:#3a2429}
:root:not([data-theme="light"]) .block-row, :root:not([data-theme="light"]) .pricing-row, :root:not([data-theme="light"]) .btn, :root:not([data-theme="light"]) .preview-tab{background:#181818;border-color:var(--muted);color:var(--fg)}
:root:not([data-theme="light"]){--bg:#161619;--surface:#1e1e23;--fg:#f2f2f5;--muted:#303039;--border:#3b3b46}
:root:not([data-theme="light"]) .block-row, :root:not([data-theme="light"]) .pricing-row, :root:not([data-theme="light"]) .btn, :root:not([data-theme="light"]) .preview-tab{background:var(--surface);border-color:var(--muted);color:var(--fg)}
}
:root[data-theme="dark"]{--bg:#0f0f0f;--surface:#1c1416;--fg:#f0f0f0;--muted:#2a2a2a;--border:#3a2429}
:root[data-theme="dark"] .block-row, :root[data-theme="dark"] .pricing-row, :root[data-theme="dark"] .btn, :root[data-theme="dark"] .preview-tab{background:#181818;border-color:var(--muted);color:var(--fg)}
:root[data-theme="dark"]{--bg:#161619;--surface:#1e1e23;--fg:#f2f2f5;--muted:#303039;--border:#3b3b46}
:root[data-theme="dark"] .block-row, :root[data-theme="dark"] .pricing-row, :root[data-theme="dark"] .btn, :root[data-theme="dark"] .preview-tab{background:var(--surface);border-color:var(--muted);color:var(--fg)}
</style>
</head>
<body>

View file

@ -51,7 +51,15 @@
// vertical height while staying visible.
// Bumped because pos-app.js and pos.html are both served cache-first out of
// the shell; without a new CACHE name the tills keep the old ones.
const CACHE = "pos-v35";
// v36 — the discount panel and the Discount totals row both carry a `hidden`
// attribute, but their classes (.discount-body, .totals) set `display:flex`,
// and an author display rule outranks the UA stylesheet's
// `[hidden]{display:none}`. So `hidden` did nothing: "Add discount" could not
// collapse the tier buttons, and a cleared discount left its old amount
// painted under an unchanged total. Fixed with a global `[hidden]` override
// plus blanking the figure when the row is hidden. Also carries the dark
// palette lift.
const CACHE = "pos-v36";
const APP_SHELL = [
'./',
'./index.html',

View file

@ -224,4 +224,43 @@ describe("POS service worker cache tracks shell edits", () => {
expect(m, "sw.js CACHE constant not found").not.toBeNull();
expect(Number(m![1])).toBeGreaterThanOrEqual(33);
});
});
});
describe("POS [hidden] attribute actually hides", () => {
/**
* A `hidden` attribute is implemented by the UA stylesheet as
* `[hidden]{display:none}` — but ANY author rule that sets `display`
* beats it, because author styles outrank UA styles regardless of
* specificity. `.totals` and `.discount-body` both set
* `display:flex`, so `el.hidden = true` did nothing: the discount
* panel could not be collapsed, and the "Discount" row could not be
* dismissed — it stayed on screen showing the last figure even
* after the discount was cleared, because the JS only ever set
* `hidden` and never cleared the text.
*
* Both symptoms looked like two unrelated bugs. One line of CSS
* fixes both, and this guards the whole class of them.
*/
it("has a global [hidden]{display:none} override", () => {
expect(POS_HTML).toMatch(/\[hidden\]\s*\{[^}]*display:\s*none\s*!important/);
});
it("covers every element that carries a hidden attribute", () => {
// Any element with `hidden` whose class rule sets `display` would
// otherwise be permanently visible. Assert the override exists
// rather than trusting each element to have remembered one.
const risky = [
...POS_HTML.matchAll(/<div[^>]*\shidden[^>]*>/g),
].filter((tag) => /\bclass="/.test(tag[0]));
expect(risky.length, "expected some hidden elements to audit").toBeGreaterThan(0);
expect(POS_HTML).toMatch(/\[hidden\]\s*\{[^}]*display:\s*none\s*!important/);
});
it("blanks the discount figure when the row is cleared", () => {
// Hiding alone left the old amount painted on screen, which the
// operator had no way to distinguish from a live one. Matched
// loosely because a comment sits between the two statements.
expect(POS_APP).toMatch(
/dl\.hidden\s*=\s*true;[\s\S]{0,400}discountLineAmount'\)\.textContent\s*=\s*''/,
);
});
});

View file

@ -954,26 +954,33 @@
* so the same OKLCH hue reads at the right intensity.
* ========================================================================= */
.dark {
--background: oklch(0.15 0.01 240);
/* The whole dark ramp sat at oklch(0.15) — an effective #080c0f,
L* 3.2. On a wall-mounted shop display that reads as a dead black
rectangle rather than a surface. Every step is lifted +0.04 so the
ramp keeps its shape (and its 3.2 L* bg→card separation) but the
canvas starts at L* 6.1 instead. Text tokens are unchanged and
were already compliant — muted-foreground goes 6.32:1 → 5.95:1,
still comfortably above AA. */
--background: oklch(0.19 0.01 240);
--foreground: oklch(0.94 0.005 240);
--card: oklch(0.19 0.012 240);
--card: oklch(0.23 0.012 240);
--card-foreground: oklch(0.94 0.005 240);
--popover: oklch(0.20 0.012 240);
--popover: oklch(0.24 0.012 240);
--popover-foreground: oklch(0.94 0.005 240);
--secondary: oklch(0.23 0.012 240);
--secondary: oklch(0.27 0.012 240);
--secondary-foreground: oklch(0.94 0.005 240);
--muted: oklch(0.22 0.012 240);
--muted: oklch(0.26 0.012 240);
--muted-foreground: oklch(0.66 0.01 240);
--accent: oklch(0.28 0.04 240);
--accent: oklch(0.32 0.04 240);
--accent-foreground: oklch(0.94 0.005 240);
--border: oklch(0.28 0.014 240);
--input: oklch(0.27 0.014 240);
--sidebar: oklch(0.17 0.012 240);
--border: oklch(0.32 0.014 240);
--input: oklch(0.31 0.014 240);
--sidebar: oklch(0.21 0.012 240);
--sidebar-foreground: oklch(0.94 0.005 240);
--sidebar-accent: oklch(0.24 0.04 240);
--sidebar-accent: oklch(0.28 0.04 240);
--sidebar-accent-foreground: oklch(0.94 0.005 240);
--sidebar-border: oklch(0.28 0.014 240);
--input-highlight: oklch(0.30 0.06 240);
--sidebar-border: oklch(0.32 0.014 240);
--input-highlight: oklch(0.34 0.06 240);
/* Dark-mode semantic bumps (success / warn / info get brighter so
* they punch on the dark canvas). */
--success: oklch(0.76 0.18 155);
@ -986,10 +993,12 @@
/* The two natively-dark themes (Cursor / Supabase) are already dark by
* default, but flipping the mode toggle still works — it just deepens
* the surface slightly. */
* the surface slightly. Kept ~0.04 L below the standard dark ramp so
* they still read as "darker", but no longer at L* 1.3 (effectively
* #000). */
:root[data-theme="cursor"].dark,
:root[data-theme="supabase"].dark {
--background: oklch(0.11 0.012 240);
--card: oklch(0.15 0.014 240);
--sidebar: oklch(0.13 0.012 240);
--background: oklch(0.15 0.012 240);
--card: oklch(0.19 0.014 240);
--sidebar: oklch(0.17 0.012 240);
}