
/* ============================================================================
   ODS v4 (October 2026): Apple HIG + Material 3 + what Kenyan counters need.
   Appended after ods.css so it can drop into the codebase the same way.
     * every touch target 48px (Android 48dp, covers iOS 44pt); till/kiosk 56-72
     * type in rem, 16px body, 14px floor for anything people must read
     * solid fills carry --o-on-solid; nothing white on a mid-tone
     * selected = the active pill (orange tint, deep orange text), not a shadow
     * content is flat; only the floating layer lifts
     * lines appear only in the bright (increased-contrast) themes, via --o-outline
   ============================================================================ */

/* ---- targets ---- */
.o-btn { min-height: 48px; font-size: 1rem; }
.o-btn--sm { min-height: 40px; font-size: .9375rem; }
.o-icon-btn { width: 44px; height: 44px; }
.o-qty__btn { width: 40px; height: 40px; }
.o-seg__btn { min-height: 40px; font-size: .9375rem; }
.o-tab { min-height: 44px; font-size: .9375rem; }
.o-pager__page, .o-pager__nav .o-icon-btn { min-width: 40px; height: 40px; }
.o-add__btn { width: 44px; height: 44px; }
a.o-chip, button.o-chip { min-height: 36px; padding: 0 14px; }
@media (pointer: coarse) {
  .o-btn--sm, .o-seg__btn, .o-tab, a.o-chip, button.o-chip { min-height: 48px; }
  .o-icon-btn, .o-qty__btn, .o-add__btn, .o-pager__page, .o-pager__nav .o-icon-btn, .o-search__clear, .o-search__mic { width: 48px !important; height: 48px !important; min-width: 48px; }
}

/* ---- type: rem, so the phone's own text size reaches it ---- */
.o-chip { font-size: .875rem; min-height: 32px; }
.o-help, .o-row__sub, .o-row__meta, .o-stat__note, .o-tl__meta, .o-dish__meta, .o-fcard__meta, .o-pager__info, .o-empty, .o-alert, .o-banner__text, .o-search__opt-meta, .o-steps li, .o-legend { font-size: .875rem; }
.o-label, .o-kv__k, .o-kv__v, .o-search__opt-name { font-size: .9375rem; }
.o-row__title, .o-dish__name, .o-pcard__name, .o-switch__text, .o-choice__title, .o-tl__title, .o-banner__title { font-size: 1rem; }
.o-switch__text small, .o-choice__text, .o-dish__desc { font-size: .875rem; }
.o-dish__price, .o-fcard__price { font-size: 1rem; font-weight: 700; }
.o-card__title { font-size: 1.0625rem; }
.o-head__title { font-size: 1.5rem !important; }
.o-section__title, .o-cat__title { font-size: 1.375rem !important; }
.o-sheet__title, .o-confirm__title { font-size: 1.25rem; }
.o-stat__value { font-size: 1.75rem; }
.o-stat__label { font-size: .875rem; }
.o-table table { font-size: .9375rem; }
.o-table th { font-size: .75rem; font-weight: 700; letter-spacing: .06em; }
.o-table .num, .o-kv__v, .o-row__num, .o-stat__value, .o-tl__time { font-variant-numeric: tabular-nums; }
.o-bar__val, .o-bar__day { font-size: .75rem; }

/* ---- solid fills carry their own text colour ---- */
.o-btn--primary { background: var(--o-accent-solid); color: var(--o-on-solid) !important; }
.o-btn--primary:hover { background: var(--o-accent-solid-hover); }
.o-btn--go { background: var(--o-go-solid); color: var(--o-on-solid) !important; }
.o-btn--go:hover, .o-btn--danger-solid:hover { box-shadow: inset 0 0 0 100vmax rgb(0 0 0 / .12); }
.o-btn--danger-solid { background: var(--o-danger-solid); color: var(--o-on-solid) !important; }
.o-add__step, .o-choice__check { background: var(--o-accent-solid); color: var(--o-on-solid); }
.o-add__btn:hover:not(:disabled), .o-search__ai:hover { background: var(--o-accent-solid); color: var(--o-on-solid); }
.o-carousel__dot[aria-current="true"]::before { background: var(--o-accent-solid); }
.o-dish.is-in { box-shadow: 0 0 0 2px var(--o-accent-solid); }

/* ---- selected = the active pill ---- */
.o-tab.is-on, .o-seg__btn.is-on, .o-seg__btn[aria-pressed="true"] { background: var(--o-accent-tint); color: var(--o-accent-deep) !important; box-shadow: none; }
.o-choice.is-selected { box-shadow: none; }
.o-pager__page.is-on { background: var(--o-accent-tint); color: var(--o-accent-deep); }

/* ---- switch: off is a small dark handle, on a large one on green (M3) ---- */
.o-switch__track, :root:is([data-orderly-theme], [data-theme], .o-v4) .o-switch__track { width: 52px; height: 32px; background: var(--o-surface-3); }
.o-switch__track::after { top: 8px; left: 8px; width: 16px; height: 16px; background: var(--o-control-off); box-shadow: none;
  transition: transform var(--o-ease), width var(--o-ease), height var(--o-ease), top var(--o-ease), left var(--o-ease), background var(--o-ease); }
.o-switch input:checked + .o-switch__track, :root:is([data-orderly-theme], [data-theme], .o-v4) .o-switch input:checked + .o-switch__track { background: var(--o-go); }
.o-switch input:checked + .o-switch__track::after { top: 4px; left: 4px; width: 24px; height: 24px; background: var(--o-surface); transform: translateX(20px); }
.o-switch:active .o-switch__track::after { width: 28px; height: 28px; top: 2px; left: 2px; }
.o-switch:active input:checked + .o-switch__track::after { transform: translateX(18px); }
.o-pcard .o-switch__track, :root:is([data-orderly-theme], [data-theme], .o-v4) .o-pcard .o-switch__track { width: 44px; height: 28px; }
.o-pcard .o-switch__track::after { top: 7px; left: 7px; width: 14px; height: 14px; }
.o-pcard .o-switch input:checked + .o-switch__track::after { top: 4px; left: 4px; width: 20px; height: 20px; transform: translateX(16px); }

/* ---- bright themes: the only place lines appear ---- */
.o-card, .o-dish, .o-pcard, .o-choice, .o-btn--ghost, .o-btn--soft, .o-btn--danger, .o-icon-btn, .o-chip, .o-search, .o-phone, .o-seg, .o-tabs,
.o-table, .o-board__col, .o-empty, .o-alert, .o-banner, .o-more, .o-qty, .o-switch__track, .o-sheet, dialog.o-confirm, .o-toast,
.o-sync, .o-nav, .o-receipt, .o-ob-key, .o-ob-staff__tile, .o-ob-paytile { outline: 1.5px solid var(--o-outline); outline-offset: -1.5px; }
.o-input:not(#o):not(#v4), .o-select:not(#o):not(#v4), .o-textarea:not(#o):not(#v4) { outline: 1.5px solid var(--o-outline) !important; outline-offset: -1.5px !important; }

/* ---- content is flat; only the floating layer lifts ---- */
.o-dish { border-radius: var(--o-r); box-shadow: none; }
.o-dish:hover { box-shadow: var(--o-elev-2); }
.o-dish__main:focus-visible { border-radius: var(--o-r); }
.o-chip--raised { box-shadow: none; background: var(--o-surface); }
.o-search--hero { box-shadow: var(--o-elev-2); }

/* ---- dark-hc and other dark variants reach the dark component rules ---- */
:root[data-theme^="dark"] .o-qty__btn { color: var(--o-accent); }

/* ---- low-end phones and data saver: food art stands still ---- */
html[data-lite] .oc__img, html[data-lite] .oc__sh, html[data-lite] .oc::after, html[data-lite] .oc__fx i { animation: none !important; }
html[data-lite] .oc__fx { display: none; }
@media (prefers-reduced-data: reduce) { .oc__img, .oc__sh, .oc::after, .oc__fx i { animation: none !important; } .oc__fx { display: none; } }

/* ============================================================== new in v4 */

/* sync status: the till works offline; staff must see what is waiting */
.o-sync { display: inline-flex; align-items: center; gap: 8px; min-height: 36px; padding: 0 14px 0 12px; border-radius: var(--o-r-pill); background: var(--o-surface-2); color: var(--o-text-2); font: 600 .875rem/1.2 var(--o-font); font-variant-numeric: tabular-nums; }
.o-sync svg { width: 18px; height: 18px; flex: 0 0 auto; }
.o-sync--offline { background: var(--o-warn-tint); color: var(--o-warn); }
.o-sync--sending { background: var(--o-accent-tint); color: var(--o-accent-deep); }
.o-sync--sent { background: var(--o-go-tint); color: var(--o-go-deep); }
.o-sync--failed { background: var(--o-danger-tint); color: var(--o-danger); }
.o-sync .o-spinner { width: 14px; height: 14px; }

/* navigation: one set of destinations; a bar on phones, a rail from 600px */
.o-nav { display: flex; justify-content: space-around; align-items: stretch; min-height: 64px; padding: 6px 4px calc(6px + env(safe-area-inset-bottom)); background: var(--o-surface); font-family: var(--o-font); }
.o-nav__item { position: relative; flex: 1 1 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; min-width: 48px; min-height: 48px; padding: 0; border: 0; background: transparent; color: var(--o-text-2); font: 600 .8125rem/1.2 var(--o-font); text-decoration: none !important; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.o-nav__pill { display: grid; place-items: center; width: 56px; height: 32px; border-radius: var(--o-r-pill); transition: background var(--o-ease); }
.o-nav__pill svg { width: 24px; height: 24px; stroke-width: 2; }
.o-nav__item:hover .o-nav__pill { background: var(--o-surface-2); }
.o-nav__item.is-on { color: var(--o-accent-deep); }
.o-nav__item.is-on .o-nav__pill { background: var(--o-accent-tint); }
.o-nav__item.is-on .o-nav__pill svg { stroke-width: 2.5; }
.o-nav__item:focus-visible { outline: none; }
.o-nav__item:focus-visible .o-nav__pill { box-shadow: var(--o-ring); }
.o-nav__badge { position: absolute; top: 2px; left: calc(50% + 6px); min-width: 18px; height: 18px; padding: 0 5px; border-radius: var(--o-r-pill); background: var(--o-danger-solid); color: var(--o-on-solid); font: 700 .6875rem/18px var(--o-font); text-align: center; font-variant-numeric: tabular-nums; }
.o-nav--rail { flex-direction: column; justify-content: flex-start; gap: 12px; width: 96px; min-height: 0; padding: 16px 0; }
.o-nav--rail .o-nav__item { flex: 0 0 auto; min-height: 64px; }

/* Skeleton (owner, 12 Nov: "smooth loading skeletons that resemble our app, for all sorts of things loading").
   The shape of what is coming, in the real part's own box, so nothing jumps when the data lands: tickets for orders,
   rows for lists, tiles for dishes and tables, stat tiles, label/value pairs, fields, chat bubbles. Blocks sit on
   surface-3 and breathe slowly (never a shine sweep); a list breathes as one soft wave from the top (--o-skel-i);
   the whole skeleton waits 250 ms before it shows, so a fast answer never flashes one; nothing moves under reduced
   motion. Never a centred spinner for a screen, never "Nothing here" while it is still loading. */
.o-skel { display: block; height: 14px; border-radius: var(--o-r-xs); background: var(--o-surface-3); animation: o-skel-breathe 1.8s ease-in-out infinite; animation-delay: calc(var(--o-skel-i, 0) * 110ms); }
.o-skel--line { height: 14px; } .o-skel--sm { height: 12px; } .o-skel--title { height: 20px; width: 60%; } .o-skel--big { height: 32px; width: 45%; }
.o-skel--media { aspect-ratio: 4 / 3; height: auto; border-radius: 0; }
.o-skel--circle { flex: 0 0 auto; width: 44px; height: 44px; border-radius: 50%; }
.o-skel--pill { flex: 0 0 auto; width: 72px; height: 28px; border-radius: var(--o-r-pill); }
.o-skel--btn { width: 100%; height: 48px; border-radius: var(--o-r-pill); }
.o-skel--field { width: 100%; height: 48px; border-radius: var(--o-r); }
@keyframes o-skel-breathe { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }
@keyframes o-skel-in { from { opacity: 0; } to { opacity: 1; } }
/* the wrapper: a column of shapes that waits, then fades in; each item breathes a beat after the one above */
.o-skeleton { display: grid; gap: 12px; animation: o-skel-in .24s ease .25s both; }
.o-skeleton > :nth-child(2) { --o-skel-i: 1; } .o-skeleton > :nth-child(3) { --o-skel-i: 2; } .o-skeleton > :nth-child(4) { --o-skel-i: 3; }
.o-skeleton > :nth-child(5) { --o-skel-i: 4; } .o-skeleton > :nth-child(6) { --o-skel-i: 5; } .o-skeleton > :nth-child(n+7) { --o-skel-i: 6; }
.o-skeleton--grid { grid-template-columns: repeat(auto-fill, minmax(min(150px, 100%), 1fr)); }
.o-skeleton--wide { grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); }
/* a card: the surface every Orderly card sits on */
.o-skel-card { display: grid; align-content: start; gap: 10px; padding: 16px; border-radius: var(--o-r); background: var(--o-surface); }
.o-skel-card__top { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
/* the order ticket: number and time, the type, who, the money and its chip, the next step */
.o-skel-ticket .o-skel-card__top .o-skel:first-child { width: 88px; height: 26px; }
.o-skel-ticket .o-skel--btn { margin-top: 6px; }
/* a list row: icon or avatar, two lines, something at the end */
.o-skel-row { display: grid; grid-template-columns: 40px minmax(0, 1fr) auto; align-items: center; gap: 12px; min-height: 64px; padding: 8px 0; }
.o-skel-row > .o-skel--circle { width: 40px; height: 40px; }
.o-skel-row__text { display: grid; gap: 8px; min-width: 0; }
.o-skel-rows { display: grid; padding: 4px 16px; border-radius: var(--o-r); background: var(--o-surface); }
/* tiles: a dish on the till and the menu, a stat, a table */
.o-skel-dish { min-height: 76px; }
.o-skel-tile { aspect-ratio: 1; padding: 14px; }
.o-skel-stat { min-height: 92px; }
/* an order's page: label/value pairs */
.o-skel-kv { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.o-skel-kv > div { display: grid; gap: 8px; }
/* a form: a label over a field */
.o-skel-fieldset { display: grid; gap: 8px; }
/* a conversation: bubbles from both sides */
.o-skel-bubble { width: 62%; height: 44px; border-radius: var(--o-r-lg); }
.o-skel-bubble--me { justify-self: end; width: 46%; }
@media (prefers-reduced-motion: reduce) { .o-skel, .o-skeleton { animation: none; } }

/* M-Pesa wait (the till's): a visible countdown, and a code that is checked, never trusted */
.o-ob-pay__timer { margin: 0; font: 700 var(--ob-fs-lg, 20px)/1.2 var(--o-font); color: var(--o-text); font-variant-numeric: tabular-nums; }
.o-ob-input--code:not(#o) { font: 500 1.25rem/1.2 var(--font-mono, ui-monospace, monospace) !important; letter-spacing: .08em; text-transform: uppercase; }
.o-ob-check { display: flex; align-items: flex-start; gap: 8px; margin: 8px 0 0; font: 500 .875rem/1.45 var(--o-font); color: var(--o-warn); }
.o-ob-check svg { width: 18px; height: 18px; flex: 0 0 auto; margin-top: 1px; }
.o-ob-check.is-ok { color: var(--o-go-deep); }

/* receipt: what a Kenyan receipt must carry (eTIMS), at 80mm or 58mm */
.o-receipt { width: 302px; max-width: 100%; padding: 18px 16px 22px; background: #ffffff; color: #000000; font: 400 13px/1.45 var(--font-mono, ui-monospace, monospace); font-variant-numeric: tabular-nums; }
.o-receipt--58 { width: 219px; font-size: 11.5px; }
.o-receipt__head { text-align: center; margin-bottom: 10px; }
.o-receipt__name { font: 700 16px/1.25 var(--o-font); }
.o-receipt__rule { margin: 8px 0; border-top: 1.5px dashed #000000; }
.o-receipt__row { display: flex; justify-content: space-between; gap: 10px; }
.o-receipt__row--total { font-weight: 700; font-size: 1.15em; }
.o-receipt__qr { display: grid; place-items: center; width: 112px; height: 112px; margin: 10px auto 6px; border: 1.5px solid #000000; font: 600 11px/1.3 var(--o-font); text-align: center; }
.o-receipt__foot { text-align: center; font-size: .92em; }

/* ticket: lives in backend/assets/ods/ods.css (the base every surface loads) */


/* the ticket sheet's paid mark in every dark theme (ods.css matched only data-orderly-theme="dark") */
:root:is([data-orderly-theme="dark"], [data-theme^="dark"]) .o-tsheet__pay.is-on .o-tsheet__tick, :root:is([data-orderly-theme="dark"], [data-theme^="dark"]) .o-tsheet__mark { color: var(--o-go); }

/* chat (design system: Chat): staff, guests and riders talking about an order.
   Inbox rows: avatar, name, the last message, time; a thread of bubbles (mine on the
   right in the accent, theirs on the left in grey), date chips, one composer. */
.o-chatlist { display: flex; flex-direction: column; gap: 4px; margin: 0; padding: 0; list-style: none; }
.o-chatrow { display: grid; grid-template-columns: 48px minmax(0, 1fr) auto; grid-template-rows: auto auto; column-gap: 12px; row-gap: 2px; align-items: center; min-height: 72px; padding: 12px; border-radius: var(--o-r); background: var(--o-surface); color: var(--o-text); font-family: var(--o-font); text-decoration: none; cursor: pointer; transition: background var(--o-ease); }
.o-chatrow:hover { background: var(--o-surface-2); }
.o-chatrow:focus-visible { outline: none; box-shadow: var(--o-ring); }
.o-chatrow__avatar { grid-row: 1 / span 2; display: grid; place-items: center; width: 48px; height: 48px; border-radius: 50%; overflow: hidden; background: var(--o-surface-3); color: var(--o-text-2); font: 700 1rem/1 var(--o-font); }
.o-chatrow__avatar img { width: 100%; height: 100%; object-fit: cover; }
.o-chatrow__name { display: flex; align-items: baseline; gap: 6px; min-width: 0; font: 600 1rem/1.3 var(--o-font); }
.o-chatrow__name b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.o-chatrow__name small { flex: 0 0 auto; font: 500 .8125rem/1.3 var(--o-font); color: var(--o-text-3); }
.o-chatrow__time { justify-self: end; font: 500 .8125rem/1.3 var(--o-font); color: var(--o-text-3); font-variant-numeric: tabular-nums; white-space: nowrap; }
.o-chatrow__last { grid-column: 2; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: 500 .9375rem/1.35 var(--o-font); color: var(--o-text-2); }
.o-chatrow__last.is-typing { color: var(--o-accent-deep); font-weight: 600; }
.o-chatrow__end { grid-column: 3; justify-self: end; display: flex; align-items: center; gap: 6px; }
.o-chatrow__end .o-chip { min-height: 24px; padding: 0 8px; font-size: .75rem; }
.o-chatrow__dot { width: 10px; height: 10px; border-radius: 50%; background: var(--o-accent); }
.o-chatrow__badge { min-width: 22px; height: 22px; padding: 0 7px; border-radius: var(--o-r-pill); background: var(--o-accent-solid); color: var(--o-on-solid); font: 700 .75rem/22px var(--o-font); text-align: center; font-variant-numeric: tabular-nums; } /* ui-lint: allow - 22px is the line height of a 22px badge, the size is rem */
.o-chatrow.is-unread .o-chatrow__name b, .o-chatrow.is-unread .o-chatrow__last { color: var(--o-text); font-weight: 700; }

.o-chathead { display: flex; align-items: center; gap: 12px; min-height: 64px; padding: 8px 12px; background: var(--o-surface); font-family: var(--o-font); }
.o-chathead__who { flex: 1 1 auto; min-width: 0; }
.o-chathead__who b { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: 600 1.0625rem/1.25 var(--o-font); color: var(--o-text); }
.o-chathead__who small { display: block; font: 500 .8125rem/1.3 var(--o-font); color: var(--o-text-3); }
.o-chathead .o-chatrow__avatar { grid-row: auto; width: 40px; height: 40px; flex: 0 0 auto; font-size: .875rem; }
.o-chatorder { display: flex; align-items: center; gap: 12px; width: min(720px, calc(100% - 24px)); margin: 8px auto 0; padding: 10px 14px; border-radius: var(--o-r); background: var(--o-surface); color: var(--o-text); font: 600 .9375rem/1.3 var(--o-font); text-decoration: none; }
.o-chatorder > svg, .o-chatorder > .o-ico { width: 20px; height: 20px; flex: 0 0 auto; color: var(--o-text-2); }
.o-chatorder__no { font-variant-numeric: tabular-nums; }
.o-chatorder__sum { margin-inline-start: auto; font-variant-numeric: tabular-nums; }

.o-chat { display: flex; flex-direction: column; width: min(720px, 100%); margin: 0 auto; padding: 8px 12px 16px; font-family: var(--o-font); }
.o-chat__day { align-self: center; margin: 16px 0 8px; padding: 4px 12px; border-radius: var(--o-r-pill); background: var(--o-surface-2); color: var(--o-text-2); font: 600 .8125rem/1.3 var(--o-font); }
.o-chat__run { display: flex; flex-direction: column; gap: 2px; max-width: min(75%, 30rem); margin-top: 8px; }
.o-chat__run--theirs { align-self: flex-start; align-items: flex-start; }
.o-chat__run--mine { align-self: flex-end; align-items: flex-end; }
.o-chat__who { margin: 0 12px 2px; font: 600 .8125rem/1.3 var(--o-font); color: var(--o-text-3); }
.o-bubble { padding: 10px 14px; border-radius: 18px; font: 500 1rem/1.4 var(--o-font); overflow-wrap: anywhere; white-space: pre-wrap; }
.o-chat__run--theirs .o-bubble { background: var(--o-surface-2); color: var(--o-text); }
.o-chat__run--mine .o-bubble { background: var(--o-accent-tint); color: var(--o-text); }
.o-chat__run--theirs .o-bubble:last-of-type { border-bottom-left-radius: 6px; }
.o-chat__run--mine .o-bubble:last-of-type { border-bottom-right-radius: 6px; }
.o-bubble--media { padding: 0; overflow: hidden; background: transparent !important; } /* ui-lint: allow - a photo bubble is the photo */
.o-bubble--media img { display: block; width: min(240px, 60vw); height: auto; border-radius: 18px; cursor: zoom-in; }
.o-chat__stamp { margin: 2px 8px 0; font: 500 .75rem/1.3 var(--o-font); color: var(--o-text-3); font-variant-numeric: tabular-nums; }
.o-chat__typing .o-bubble { display: inline-flex; align-items: center; gap: 4px; min-height: 40px; }
.o-chat__typing .o-bubble i { width: 7px; height: 7px; border-radius: 50%; background: var(--o-text-3); animation: o-typing 1.2s ease-in-out infinite; }
.o-chat__typing .o-bubble i:nth-child(2) { animation-delay: .15s; }
.o-chat__typing .o-bubble i:nth-child(3) { animation-delay: .3s; }
@keyframes o-typing { 0%, 60%, 100% { opacity: .35; transform: none; } 30% { opacity: 1; transform: translateY(-3px); } }
@media (prefers-reduced-motion: reduce) { .o-chat__typing .o-bubble i { animation: none; } }
/* dark: an orange tint reads brown on a dark ground, so my bubbles take the solid accent */
:root:is([data-orderly-theme="dark"], [data-theme^="dark"]) .o-chat__run--mine .o-bubble, html.dark .o-chat__run--mine .o-bubble { background: var(--o-accent-solid); color: var(--o-on-solid); }

.o-quick { display: flex; gap: 8px; padding: 8px 12px 0; overflow-x: auto; scrollbar-width: none; }
.o-quick::-webkit-scrollbar { display: none; }
.o-quick .o-chipbtn { flex: 0 0 auto; }
.o-composer { display: flex; align-items: flex-end; gap: 8px; padding: 8px 12px calc(8px + env(safe-area-inset-bottom)); background: var(--o-surface); font-family: var(--o-font); }
.o-composer__field { flex: 1 1 auto; min-width: 0; min-height: 48px; max-height: 140px; margin: 0; padding: 13px 18px; border: 0; border-radius: 24px; outline: none; resize: none; background: var(--o-surface-2); color: var(--o-text); font: 500 1rem/1.4 var(--o-font); }
.o-composer__field:focus { box-shadow: var(--o-ring); }
.o-composer__btn { display: grid; place-items: center; flex: 0 0 auto; width: 48px; height: 48px; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--o-text-2); cursor: pointer; }
.o-composer__btn:hover { background: var(--o-surface-2); color: var(--o-text); }
.o-composer__btn:focus-visible { outline: none; box-shadow: var(--o-ring); }
.o-composer__btn svg, .o-composer__btn .o-ico { width: 24px; height: 24px; }
.o-composer__send { background: var(--o-accent-solid); color: var(--o-on-solid); }
.o-composer__send:hover { background: var(--o-accent-solid-hover); color: var(--o-on-solid); }

/* ---- fixes found while checking v4 ---- */
/* components size themselves: no dependence on the host's box-sizing reset */
[class^="o-"], [class*=" o-"], [class^="o-"]::before, [class*=" o-"]::before, [class^="o-"]::after, [class*=" o-"]::after { box-sizing: border-box; }
/* a table's second line is a second line (a later page rule had made it inline everywhere) */
.o-table .o-table__sub { display: block; margin-top: 2px; font-size: .875rem; }
/* labels arrive in sentence case; capitalising every word made "On The Way" */
.o-chip[data-status] { text-transform: none; }
/* the add stepper gets its own row instead of covering the price */
.o-dish.is-in .o-dish__main { padding-bottom: 60px; }
/* one boundary around the phone field, not one per part */
.o-phone .o-input:not(#o):not(#v4), .o-phone .o-select:not(#o):not(#v4) { outline: none !important; }

/* ---- Toast v4 (8 Nov 2026): one calm dark card on every surface (ODS.toast, the ordering block, Quasar Notify
   through the bridge). Ink by day, a raised grey by night: never a white slab on a dark screen, never a whole
   green or red block. The tone is the icon; one action in orange words; a close icon only when it stays. ---- */
.o-toast { display: flex; align-items: center; gap: 12px; min-height: 56px; max-width: min(560px, calc(100vw - 32px)); padding: 8px 8px 8px 16px; border: 0; border-radius: var(--o-r); background: var(--o-toast-bg); color: var(--o-toast-text); box-shadow: var(--o-elev-3); font: 600 .9375rem/1.35 var(--o-font); text-align: left; }
.o-toast--go, .o-toast--danger, .o-toast--warn { background: var(--o-toast-bg); color: var(--o-toast-text); }
.o-toast--go::before, .o-toast--danger::before, .o-toast--warn::before { content: ""; flex: 0 0 auto; width: 20px; height: 20px; background: currentColor; -webkit-mask: var(--o-toast-ico) center / contain no-repeat; mask: var(--o-toast-ico) center / contain no-repeat; }
.o-toast--go::before { color: var(--o-toast-go); --o-toast-ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E"); }
.o-toast--danger::before { color: var(--o-toast-danger); --o-toast-ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7.5v5'/%3E%3Cpath d='M12 16.5h.01'/%3E%3C/svg%3E"); }
.o-toast--warn::before { color: var(--o-toast-warn); --o-toast-ico: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7v5l3 2'/%3E%3C/svg%3E"); }
.o-toast__text { flex: 1 1 auto; min-width: 0; padding: 6px 4px 6px 0; color: inherit; }
.o-toast__action { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; min-height: 40px; padding: 0 12px; border: 0; border-radius: var(--o-r-pill); background: transparent; color: var(--o-toast-action); font: 700 .9375rem/1 var(--o-font); cursor: pointer; }
.o-toast__action:hover { background: color-mix(in oklch, var(--o-toast-text) 10%, transparent); }
.o-toast__action svg { width: 18px; height: 18px; }
.o-toast__close { flex: 0 0 auto; display: grid; place-items: center; width: 40px; height: 40px; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--o-toast-text); opacity: .72; cursor: pointer; }
.o-toast__close:hover { opacity: 1; background: color-mix(in oklch, var(--o-toast-text) 10%, transparent); }
.o-toast :focus-visible { outline: none; box-shadow: 0 0 0 2px var(--o-toast-action); }

/* ---- Scroll bars (8 Nov 2026): the page says which theme it is, so the browser's own bars follow it at night,
   and every bar is thin, rounded and quiet (no chunky grey system bar), the same on every surface. ---- */
:root, :root[data-theme="light"], :root[data-theme="light-hc"], :root[data-orderly-theme="light"] { color-scheme: light; }
:root[data-theme="dark"], :root[data-theme="dark-hc"], :root[data-orderly-theme="dark"], html.dark { color-scheme: dark; }
@media (prefers-color-scheme: dark) { :root:not([data-theme]):not([data-orderly-theme]):not(.light) { color-scheme: dark; } }
html { scrollbar-color: rgb(128 128 128 / .45) transparent; scrollbar-color: color-mix(in oklch, var(--o-text) 26%, transparent) transparent; } /* ui-lint: allow - old WebViews without color-mix get a mid grey */
* { scrollbar-width: thin; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track, ::-webkit-scrollbar-corner { background: transparent; }
::-webkit-scrollbar-thumb { border: 3px solid transparent; border-radius: 999px; background: rgb(128 128 128 / .45) padding-box; background: color-mix(in oklch, var(--o-text) 26%, transparent) padding-box; } /* ui-lint: allow - as above */
::-webkit-scrollbar-thumb:hover { background-color: color-mix(in oklch, var(--o-text) 42%, transparent); }

/* ---- Live: the connection dot (design verdict 9 Nov: quiet, text-3, never green; green means accept or paid).
   "Live" with a 6px dot; a small spinner and "Reconnecting" after 5 s down. Markup: shared/orderly-realtime/LiveDot.vue
   (and its plain-HTML twin in the back office pages). The status text for screen readers sits in .o-live__sr. ---- */
.o-live { display: inline-flex; align-items: center; gap: 6px; font-size: .8125rem; font-weight: 600; line-height: 20px; color: var(--o-text-3); white-space: nowrap; }
.o-live__row { display: inline-flex; align-items: center; gap: 6px; }
.o-live__dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.o-live__spin { width: 10px; height: 10px; border-radius: 50%; border: 2px solid currentColor; border-right-color: transparent; animation: o-live-spin .9s linear infinite; } /* ui-lint: allow - a spinner ring, not a divider line */
.o-live__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
@keyframes o-live-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .o-live__spin { animation-duration: 3s; } }

/* ---- a focused field: its ring sits inside its own edge (owner, 12 Nov: "padding??? fix it") ----
   The token ring (--o-ring) reaches 5px outside, so on a field it ran into the label 8px above, overlapped a
   neighbour in tight rows and was clipped by a sheet's scroll edge. A field draws a 2px accent outline inset by
   its own width instead: it hugs the field, follows its radius, and touches nothing around it. Buttons, links and
   chips keep the token ring. A wrapper field (phone, affix) rings once; the input inside it does not. */
.o-input:not(#o):not(#v4):focus, .o-select:not(#o):not(#v4):focus, .o-textarea:not(#o):not(#v4):focus {
  box-shadow: none !important; outline: 2px solid var(--o-focus, var(--o-accent-solid)) !important; outline-offset: -2px !important; /* ui-lint: allow - over ods.css's own !important field focus */
}
.o-phone:not(#v4):focus-within, .o-affix:not(#v4):focus-within, .o-fld:not(#v4):focus {
  box-shadow: none; outline: 2px solid var(--o-focus, var(--o-accent-solid)); outline-offset: -2px;
}
.o-phone .o-input:not(#o):not(#v4):focus, .o-phone .o-select:not(#o):not(#v4):focus,
.o-affix .o-input:not(#o):not(#v4):focus { outline: none !important; box-shadow: none !important; } /* ui-lint: allow - over ods.css's own !important field focus */
