/* ============================================================================
   Mobile app shell and cards — the layout every module shares on a phone.
   Colour comes from the role palette in menu.php (--w-primary and friends), so
   nothing here hardcodes a brand colour: a picker sees green, an admin gold,
   a super admin red, and this file is the same for all three.
   ============================================================================ */

/* One height, used by everything that has to sit clear of the bar. */
:root { --app-tabbar-h: 61px; }

/* ---- app-level bottom tabs: Chat | WMS | HR | Mine ---- */
.app-tabbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
    display: flex; background: var(--w-surface);
    border-top: 1px solid var(--w-border);
    padding-bottom: env(safe-area-inset-bottom);
}
.app-tab {
    flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 3px;
    padding: 8px 2px 7px; font-size: 11.5px; line-height: 1;
    color: var(--w-muted); text-decoration: none; position: relative;
}
.app-tab > span:not(.app-tab-ico):not(.app-tab-badge) {
    max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.app-tab:hover, .app-tab:focus { color: var(--w-muted); text-decoration: none; }
.app-tab .app-tab-ico {
    width: 100%; max-width: 52px; height: 30px; border-radius: 15px;
    display: flex; align-items: center; justify-content: center;
    font-size: 21px; transition: background .15s ease;
}
.app-tab.active { color: var(--w-primary); font-weight: 600; }
.app-tab.active .app-tab-ico { background: var(--w-primary-soft); }
.app-tab .app-tab-badge {
    position: absolute; top: 2px; left: 50%; margin-left: 4px;
    min-width: 18px; height: 18px; padding: 0 5px;
    border-radius: 9px; background: #e23b3b; color: #fff;
    font-size: 11px; font-weight: 700; line-height: 18px; text-align: center;
}
/* Desktop keeps the bar too. The sidebar is fixed at --sb-w, so the bar starts where the content
   does rather than sliding underneath it — the same treatment .action-bar already gets. */
@media (min-width: 768px) {
    .app-tabbar { left: var(--sb-w, 0px); }
    /* The save bar has to start there too. style.css sets left: var(--sb-w) for it at this width,
       but a later .action-bar rule in the same file resets left: 0 and wins on document order, so
       it had always run under the sidebar on desktop. This file loads last, so it settles it.
       Nothing here may change the bar's height: --app-tabbar-h is what everything else stacks off,
       and a bar 2px taller than the variable leaves the save bar overlapping it. */
    .action-bar { left: var(--sb-w, 0px); }
}

/* pages that show the bar need room for it */
.has-tabbar { padding-bottom: 86px; }

/* ---- greeting ---- */
.m-greet { padding: 18px 2px 14px; }
.m-greet h1 { font-size: 24px; font-weight: 700; margin: 0 0 4px; letter-spacing: -.2px; }
.m-greet p { margin: 0; color: var(--w-muted); font-size: 14.5px; }

/* ---- the one filled card at the top of a hub ---- */
.m-hero {
    background: var(--w-primary-gradient); color: #fff;
    border-radius: 18px; padding: 16px 16px 18px; margin-bottom: 16px;
    position: relative; overflow: hidden;
}
.m-hero::after {                       /* the soft disc in the corner of the reference */
    content: ''; position: absolute; right: -40px; top: -60px;
    width: 180px; height: 180px; border-radius: 50%;
    background: rgba(255, 255, 255, .06);
}
.m-hero h2 { font-size: 16px; font-weight: 700; margin: 0 0 14px; position: relative; }
.m-hero-stats { display: flex; align-items: stretch; position: relative; }
.m-hero-stat { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; }
.m-hero-stat + .m-hero-stat { border-left: 1px solid rgba(255, 255, 255, .22); }
.m-hero-stat i {
    width: 42px; height: 42px; border-radius: 12px; background: rgba(255, 255, 255, .16);
    display: flex; align-items: center; justify-content: center; font-size: 21px;
}
.m-hero-stat small { font-size: 12.5px; opacity: .92; line-height: 1.2; }
.m-hero-stat b { font-size: 24px; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
/* the stat itself is the link, so colour it directly — a descendant rule never matches here and the
   site's link colour wins, which left white-on-white numbers on the card */
.m-hero-stat, .m-hero-stat:hover, .m-hero-stat:focus,
.m-hero-stat b, .m-hero-stat small { color: #fff; text-decoration: none; }
.m-hero-stat:hover i { background: rgba(255, 255, 255, .26); }

/* ---- plain white card with a heading ---- */
.m-card { background: var(--w-surface); border-radius: 16px; padding: 14px 14px 6px; margin-bottom: 14px; box-shadow: 0 1px 2px rgba(16, 24, 40, .05); }
.m-card.tight { padding-bottom: 14px; }
.m-card-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
.m-card-head h2 { font-size: 17px; font-weight: 700; margin: 0; }
.m-card-head a { font-size: 13.5px; font-weight: 600; color: var(--w-primary); text-decoration: none; white-space: nowrap; }
.m-card-head a i { font-size: 12px; }

/* ---- a row that leads somewhere ---- */
.m-row {
    display: flex; align-items: center; gap: 12px;
    padding: 12px 2px; text-decoration: none; color: var(--w-text);
    border-bottom: 1px solid var(--w-border);
}
.m-card > .m-row:last-child, .m-card > form:last-child .m-row { border-bottom: 0; }
.m-row:hover { text-decoration: none; color: var(--w-text); }
.m-row-ico { width: 34px; height: 34px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 19px; flex: none; }
.m-row-ico.warn { background: #fff3e0; color: #ed6c02; }
.m-row-ico.bad  { background: #fdecea; color: #d32f2f; }
.m-row-ico.ok   { background: var(--w-primary-soft); color: var(--w-primary); }
.m-row-body { flex: 1; min-width: 0; }
.m-row-body b { display: block; font-weight: 600; font-size: 15px; line-height: 1.3; }
.m-row-body small { color: var(--w-muted); font-size: 12.5px; }
.m-row-meta { color: var(--w-muted); font-size: 13.5px; white-space: nowrap; }
.m-row > .bi-chevron-right { color: #b9c2be; font-size: 15px; flex: none; }
.m-row-count {
    min-width: 22px; height: 22px; padding: 0 6px; border-radius: 11px;
    background: #e23b3b; color: #fff; font-size: 12px; font-weight: 700;
    line-height: 22px; text-align: center; flex: none;
}
.m-row-count.warn { background: #ed6c02; }

/* ---- the big two-column menu tiles ---- */
.m-tiles { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; padding-bottom: 8px; }
.m-tile {
    background: var(--w-primary-soft); border-radius: 14px;
    padding: 18px 10px 14px; text-align: center; text-decoration: none;
    display: flex; flex-direction: column; align-items: center; gap: 10px;
    color: var(--w-text); min-height: 104px; justify-content: center;
}
.m-tile:hover, .m-tile:focus { text-decoration: none; color: var(--w-text); background: var(--w-primary-soft-active, var(--w-primary-soft)); }
.m-tile i { font-size: 29px; color: var(--w-primary); line-height: 1; }
.m-tile span { font-size: 13.5px; font-weight: 600; line-height: 1.25; }
@media (min-width: 560px) { .m-tiles { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 768px) { .m-tiles { grid-template-columns: repeat(4, 1fr); } }

/* ---- filter chips ---- */
.m-chips { display: flex; gap: 8px; overflow-x: auto; padding: 2px 2px 8px; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.m-chips::-webkit-scrollbar { display: none; }
.m-chip {
    flex: none; display: inline-flex; align-items: center; gap: 6px;
    padding: 9px 16px; border-radius: 999px; border: 1px solid var(--w-border);
    background: var(--w-surface); color: var(--w-text);
    font-size: 14px; font-weight: 600; text-decoration: none; white-space: nowrap;
}
.m-chip:hover { text-decoration: none; color: var(--w-text); }
.m-chip.active { background: var(--w-primary); border-color: var(--w-primary); color: #fff; }
.m-chip.soft { background: var(--w-primary-soft); border-color: transparent; color: var(--w-primary); }
.m-chip .m-chip-n { opacity: .85; font-weight: 700; }

/* ---- full-width primary action ---- */
.m-action { display: block; width: 100%; padding: 15px 16px; border: 0; border-radius: 12px;
    background: var(--w-primary); color: #fff; font-size: 16px; font-weight: 700; text-align: center; text-decoration: none; }
.m-action:hover, .m-action:focus { color: #fff; text-decoration: none; background: var(--w-primary-dark); }
.m-note { text-align: center; color: var(--w-muted); font-size: 12.5px; margin: 8px 0 0; }

/* ---- status pill used inside cards ---- */
.m-state { display: inline-flex; align-items: center; gap: 7px; padding: 8px 14px; border-radius: 999px; font-size: 14px; font-weight: 600; }
.m-state.bad { background: #fdecea; color: #c62828; }
.m-state.ok { background: var(--w-primary-soft); color: var(--w-primary); }

/* ---- the week strip on the HR hub ---- */
.hr-month { font-size: 17px; font-weight: 600; margin: 2px 0 10px; }
.hr-week { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.hr-day { text-align: center; padding: 6px 0 8px; border-radius: 10px; }
.hr-day small { display: block; font-size: 11.5px; color: var(--w-muted); margin-bottom: 2px; }
.hr-day b { font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums; }
.hr-day + .hr-day { border-left: 1px solid var(--w-border); }
.hr-day.today { background: var(--w-primary); border-left-color: transparent; }
.hr-day.today small, .hr-day.today b { color: #fff; }
.hr-shift { margin: 12px 0 0; padding-top: 10px; border-top: 1px solid var(--w-border); color: var(--w-muted); font-size: 13.5px; }
.m-card .mt-3 { margin-top: 12px; }

/* ---- chat: the search row and the list in card form ---- */
/* the search box, then announce, then a plus to start a chat. No heading: the magnifier and the
   placeholder already say what the box is, and a title above it only pushed the list further down. */
.chat-filter-head { display: flex; align-items: center; gap: 8px; }
.chat-filter-head .chat-search { flex: 1 1 auto; flex-basis: auto; min-width: 0; margin: 0; order: 0; }
/* starting a chat: where the chevron was, and filled, because it is the one thing on this row that
   makes something rather than narrowing something */
.chat-new-btn {
    flex: none; display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; border-radius: 10px;
    background: var(--w-primary); color: #fff; text-decoration: none;
}
.chat-new-btn:hover, .chat-new-btn:focus { background: var(--w-primary-dark); color: #fff; text-decoration: none; }
.chat-new-btn i { font-size: 17px; line-height: 1; }
/* the list's own controls, above the search row (owner 2026-10-05): the order, and marking everything read. One line on
   a phone — the select takes what the button leaves — and the confirmation opens under it rather than in a dialog. */
.chat-list-tools { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.chat-sort { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 6px; margin: 0; color: var(--w-muted); }
.chat-sort i { flex: none; font-size: 16px; }
.chat-sort-select { flex: 1 1 auto; min-width: 0; max-width: 260px; height: 36px; padding: 0 8px; font-size: 14px;
    border: 1px solid var(--w-border); border-radius: 10px; background: var(--w-surface); color: var(--w-text); }
.chat-markall-btn { flex: none; display: inline-flex; align-items: center; gap: 6px; height: 36px; padding: 0 12px;
    border: 1px solid var(--w-border); border-radius: 10px; background: var(--w-surface); color: var(--w-primary); font-size: 14px; white-space: nowrap; }
.chat-markall-btn:hover, .chat-markall-btn:focus, .chat-markall-btn[aria-expanded="true"] { background: var(--w-primary-soft); }
.chat-markall-btn i { font-size: 16px; line-height: 1; }
.chat-markall-panel { margin: 0 0 10px; padding: 10px 12px; border: 1px solid var(--w-border); border-radius: 10px; background: var(--w-surface); }
.chat-markall-panel fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }
.chat-markall-panel legend { font-size: 14.5px; font-weight: 600; margin-bottom: 6px; color: var(--w-text); }
.chat-markall-opt { display: flex; align-items: flex-start; gap: 8px; margin: 0 0 6px; font-size: 14px; overflow-wrap: anywhere; }
.chat-markall-opt input { margin-top: 3px; flex: none; }
.chat-markall-sum { margin: 8px 0 0; font-size: 14px; color: var(--w-text); font-weight: 600; overflow-wrap: anywhere; }
.chat-markall-note { margin: 4px 0 0; font-size: 12.5px; color: var(--w-muted); }
.chat-markall-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; margin-top: 10px; }
.chat-markall-actions .btn { min-height: 36px; }
@media (max-width: 380px) { .chat-markall-btn span { display: none; } }
.chat-list { padding-left: 12px; padding-right: 12px; }
.chat-list .chat-row { border-bottom: 1px solid var(--w-border); }
.chat-list .chat-row:last-child { border-bottom: 0; }

/* Every page carries the bar now, so every page needs the room for it — pages that set
   .has-tabbar themselves keep their own value, this is the floor for the rest. */
/* The bar is on every page at every width now, so every page needs the room for it — pages that set
   .has-tabbar themselves keep their own value, this is the floor for the rest.
   env(safe-area-inset-bottom) resolves to 0 where there is no notch, so these hold on desktop too. */
body { padding-bottom: calc(var(--app-tabbar-h) + env(safe-area-inset-bottom) + 14px); }
body.has-tabbar-none { padding-bottom: 0; }

/* A form's save bar is also fixed to the bottom and sits above the tabs on z-index, which left
   the tabs visible but untappable — a tap there hit the save bar. Stack them instead: the save
   bar rides directly on top of the tab bar, and the page leaves room for both. */
.action-bar { bottom: calc(var(--app-tabbar-h) + env(safe-area-inset-bottom)); }
.has-action-bar, body:has(.action-bar) {
    padding-bottom: calc(var(--app-tabbar-h) + env(safe-area-inset-bottom) + 86px);
}

/* The chat conversation is a full-height flex column that owns the whole viewport, so the bar takes
   its place as the last row instead of floating over the composer someone is typing in. */
body.chat-body .app-tabbar { position: static; flex: none; }
body.chat-body { padding-bottom: 0; }

/* ---- collapsible panel: one row of a settings page, opened by tapping its header ----
   Built on <details> so it works with the keyboard and without JavaScript; the script only
   remembers which panels were left open. */
.m-panel { background: var(--w-surface); border-radius: 16px; margin-bottom: 12px; box-shadow: 0 1px 2px rgba(16, 24, 40, .05); overflow: hidden; }
.m-panel > summary {
    display: flex; align-items: center; gap: 12px;
    padding: 15px 16px; cursor: pointer; list-style: none;
    font-size: 16px; font-weight: 700; color: var(--w-text);
}
.m-panel > summary::-webkit-details-marker { display: none; }
.m-panel > summary:hover { background: var(--w-bg); }
.m-panel-ico {
    width: 34px; height: 34px; border-radius: 10px; flex: none;
    background: var(--w-primary-soft); color: var(--w-primary);
    display: flex; align-items: center; justify-content: center; font-size: 18px;
}
.m-panel-title { flex: 1; min-width: 0; }
.m-panel-title small { display: block; font-size: 12.5px; font-weight: 400; color: var(--w-muted); margin-top: 1px; }
.m-panel-chev { flex: none; color: #b9c2be; font-size: 14px; transition: transform .18s ease; }
.m-panel[open] > summary .m-panel-chev { transform: rotate(180deg); }
.m-panel[open] > summary { border-bottom: 1px solid var(--w-border); }
.m-panel-body { padding: 16px; }
@media (prefers-reduced-motion: reduce) { .m-panel-chev { transition: none; } }

/* ---- a sub-heading and a "see all" link inside a panel ---- */
.m-sub-head { font-size: 13px; font-weight: 700; color: var(--w-muted); text-transform: none; margin: 18px 0 2px; }
.m-panel-body > .m-row:last-of-type { border-bottom: 0; }
.m-see-all { display: inline-flex; align-items: center; gap: 4px; margin-top: 10px; font-size: 13.5px; font-weight: 600; color: var(--w-primary); text-decoration: none; }
.m-see-all:hover { text-decoration: none; color: var(--w-primary-dark); }
.m-panel-body .m-action + .m-sub-head { margin-top: 20px; }

/* HR sets the employee's name, so it reads as text — a box would invite someone to type in it */
.p-name { margin: 2px 0 4px; font-size: 17px; font-weight: 600; color: var(--w-text); line-height: 1.35; }

/* ---- the shared action button ----
   This once lived only in the warehouse stylesheet (removed with the WMS).
   Three screens outside the warehouse use the class — admin/org.php, admin/user_positions.php and
   chat/index.php — so on those the buttons had no rule at all and fell back to the browser's own:
   flat grey, square corners, a 2px outset border, 29px tall beside a 48px input. Defining it here
   puts it on every page, and wms.css no longer carries a second copy to drift from.
   The size matches what the warehouse screens already had, so none of them move. */
.btn-wms {
    display: inline-flex; align-items: center; justify-content: center; gap: 7px;
    min-height: 38px; padding: 0 15px;
    border: 1px solid var(--w-border-strong); border-radius: var(--w-radius-sm);
    background: var(--w-surface); color: var(--w-text);
    font: inherit; font-size: 14px; font-weight: 600; line-height: 1;
    cursor: pointer; white-space: nowrap;
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.btn-wms:hover, .btn-wms:focus { background: var(--w-bg); color: var(--w-text); text-decoration: none; }
.btn-wms i { font-size: 16px; line-height: 1; }

.btn-wms.primary { background: var(--w-primary); border-color: var(--w-primary); color: #fff; }
.btn-wms.primary:hover, .btn-wms.primary:focus { background: var(--w-primary-dark); border-color: var(--w-primary-dark); color: #fff; }

.btn-wms.danger { color: var(--w-danger); border-color: var(--w-danger-border); background: var(--w-surface); }
.btn-wms.danger:hover, .btn-wms.danger:focus { background: var(--w-danger-soft); color: var(--w-danger); }

.btn-wms:disabled, .btn-wms.disabled { opacity: .5; cursor: not-allowed; }

/* Beside a form control the button takes the control's height (.form-control is min-height 48px),
   so the row reads as one line rather than a tall field with a short button floating next to it.
   These four are every container in the codebase that puts a field and a button on the same row. */
.wms-toolbar .btn-wms,
.wms-scan .btn-wms,
.wms-actions .btn-wms,
.org-line .btn-wms,
.m-panel-body form .btn-wms { min-height: 48px; padding: 0 18px; }
.wms-actions { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }

/* STEP 08 receiving workflow (wms/receive.php, wms/receiving.php): controls a thumb can hit at the dock, and the add
   form shown one step at a time once the script runs (.is-stepped); without it every step stays visible */
.wms-rcv .form-control, .wms-rcv select.form-control { min-height: 52px; font-size: 17px; }
.wms-rcv .btn-wms { min-height: 52px; padding: 0 18px; font-size: 16px; }
.wms-rcv .rcv-big { font-size: 26px; font-weight: 600; }
.wms-rcv .rcv-check { font-size: 17px; padding: 8px 0; }
.wms-rcv .rcv-check input { width: 22px; height: 22px; vertical-align: middle; margin-right: 6px; }
.wms-rcv .rcv-steps { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 12px; }
.wms-rcv .rcv-tab { border: 1px solid var(--w-border-strong); background: var(--w-surface); color: var(--w-text); border-radius: 999px; padding: 8px 14px; font: inherit; font-size: 14px; font-weight: 600; }
.wms-rcv .rcv-tab.is-current { background: var(--w-primary); border-color: var(--w-primary); color: #fff; }
.wms-rcv .is-stepped .rcv-step { display: none; }
.wms-rcv .is-stepped .rcv-step.is-current { display: block; }
.wms-rcv .rcv-step { padding: 6px 0 2px; }
.wms-rcv .rcv-step > label.d-block { margin-bottom: 10px; }
.wms-rcv .rcv-units { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.wms-rcv .rcv-unit { flex: 1 1 100px; border: 1px solid var(--w-border-strong); border-radius: var(--w-radius-sm); padding: 12px; text-align: center; font-size: 16px; font-weight: 600; cursor: pointer; }
.wms-rcv .rcv-unit input { margin-right: 6px; }
.wms-rcv .rcv-pack { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; align-items: end; }
.wms-rcv .rcv-pack p { grid-column: 1 / -1; font-size: 18px; }
/* work queues (WMS STEP 12): queue chips, the next-task card with big one-hand buttons, the supervisor board */
.wms-queue .wms-chips { display: flex; gap: 6px; overflow-x: auto; padding-bottom: 4px; margin-bottom: 10px; -webkit-overflow-scrolling: touch; }
.wms-queue .wms-chip { flex: 0 0 auto; border: 1px solid var(--w-border-strong); background: var(--w-surface); color: var(--w-text); border-radius: 999px; padding: 8px 14px; font-size: 14px; font-weight: 600; text-decoration: none; white-space: nowrap; }
.wms-queue .wms-chip b { margin-left: 4px; }
.wms-queue .wms-chip.is-current { background: var(--w-primary); border-color: var(--w-primary); color: #fff; }
.wms-queue .wms-next .rcv-big { font-size: 22px; }
.wms-queue .wms-next-actions { display: flex; flex-direction: column; gap: 10px; }
.wms-queue .wms-next-actions form, .wms-queue .wms-next-actions .wms-big { width: 100%; }
.wms-queue .btn-wms.wms-big { min-height: 64px; width: 100%; font-size: 19px; justify-content: center; }
.wms-queue .wms-board { display: grid; grid-template-columns: 1fr; gap: 12px; }
.wms-queue .wms-board-col { background: var(--w-surface); border: 1px solid var(--w-border); border-radius: var(--w-radius-sm); padding: 10px; }
.wms-queue .wms-card { display: block; border: 1px solid var(--w-border); border-radius: var(--w-radius-sm); padding: 8px 10px; margin-bottom: 8px; color: var(--w-text); text-decoration: none; background: var(--w-bg); }
.wms-queue .wms-card.is-urgent { border-left: 4px solid #dc3545; }
@media (min-width: 768px) { .wms-queue .wms-next-actions { flex-direction: row; } .wms-queue .wms-next-actions form, .wms-queue .wms-next-actions .wms-big { width: auto; flex: 1 1 0; } }
@media (min-width: 992px) { .wms-queue .wms-board { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1400px) { .wms-queue .wms-board { grid-template-columns: repeat(6, minmax(0, 1fr)); } }

/* scanner mode (WMS STEP 13, wms/scan_mode.php): a full-screen worker shell over the page — one prompt at a time, the task
   always in view, a big coloured result, 48px+ controls, one-hand actions at the bottom; never wider than the screen */
.wms-sm-body { overflow-x: hidden; }
.wms-sm { position: fixed; inset: 0; z-index: 1050; background: var(--w-bg); color: var(--w-text); display: flex; flex-direction: column; overflow-y: auto; overflow-x: hidden;
    -webkit-overflow-scrolling: touch; padding-bottom: env(safe-area-inset-bottom); }
.wms-sm * { box-sizing: border-box; max-width: 100%; }
.wms-sm .sm-top { position: sticky; top: 0; z-index: 2; display: flex; align-items: center; gap: 10px; padding: 8px 12px; background: var(--w-primary); color: #fff; min-height: 56px; }
.wms-sm .sm-top a { color: #fff; text-decoration: none; min-width: 48px; min-height: 48px; display: inline-flex; align-items: center; justify-content: center; font-size: 20px; }
.wms-sm .sm-top .sm-lang { font-size: 15px; font-weight: 600; margin-left: auto; }
.wms-sm .sm-title { font-size: 17px; }
.wms-sm .sm-task { position: sticky; top: 56px; z-index: 1; background: var(--w-surface); border-bottom: 1px solid var(--w-border); padding: 10px 14px; font-size: 15px; }
.wms-sm .sm-task-head { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.wms-sm .sm-pos { margin-left: auto; color: var(--w-muted); font-size: 13px; }
.wms-sm .sm-task-sku { margin-top: 4px; font-size: 16px; overflow-wrap: anywhere; }
.wms-sm .sm-task-route { margin-top: 4px; display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; font-size: 16px; font-weight: 600; }
.wms-sm .sm-left { margin-left: auto; font-weight: 400; }
.wms-sm .sm-result { margin: 10px 14px 0; border-radius: 14px; padding: 14px; font-size: 19px; font-weight: 700; display: flex; flex-wrap: wrap; gap: 10px; align-items: center; min-height: 12px; }
.wms-sm .sm-result[data-state="idle"] { display: none; }
.wms-sm .sm-result[data-state="ok"], .wms-sm .sm-result[data-state="sent"] { background: #dcfae6; color: #067647; }
.wms-sm .sm-result[data-state="error"], .wms-sm .sm-result[data-state="stale"] { background: var(--w-danger-soft); color: #b42318; border: 2px solid var(--w-danger); }
.wms-sm .sm-result[data-state="sending"] { background: #eff8ff; color: #175cd3; }
.wms-sm .sm-result[data-state="unsent"] { background: #fffaeb; color: #b54708; border: 2px solid #f79009; }
.wms-sm .sm-main { flex: 1 0 auto; padding: 12px 14px 96px; display: flex; flex-direction: column; gap: 12px; width: 100%; max-width: 640px; margin: 0 auto; }
.wms-sm .sm-ask { font-size: 22px; font-weight: 700; margin: 0 0 8px; overflow-wrap: anywhere; }
.wms-sm .sm-label { font-size: 13px; color: var(--w-muted); margin: 0; }
.wms-sm .sm-inputrow { display: flex; gap: 8px; }
.wms-sm .sm-input { min-height: 60px; font-size: 22px; flex: 1 1 auto; min-width: 0; }
.wms-sm .sm-cam { min-width: 60px; min-height: 60px; font-size: 24px; justify-content: center; flex: 0 0 auto; }
.wms-sm .btn-wms.sm-big { min-height: 60px; width: 100%; font-size: 19px; justify-content: center; margin-top: 8px; }
.wms-sm .sm-scan, .wms-sm .sm-confirm, .wms-sm .sm-next, .wms-sm .sm-list, .wms-sm .sm-note, .wms-sm .sm-open { background: var(--w-surface); border: 1px solid var(--w-border); border-radius: 16px; padding: 14px; }
.wms-sm .sm-note.is-ok { border-color: #75e0a7; background: #f6fef9; }
.wms-sm .sm-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; font-size: 14px; }
.wms-sm .sm-steps li { padding: 6px 10px; border-radius: 10px; color: var(--w-muted); overflow-wrap: anywhere; }
.wms-sm .sm-steps li.is-done { color: #067647; }
.wms-sm .sm-steps li.is-done::before { content: "\2713  "; }
.wms-sm .sm-steps li.is-current { background: var(--w-primary-soft); color: var(--w-text); font-weight: 600; }
.wms-sm .sm-radio { display: flex; align-items: center; gap: 10px; min-height: 48px; font-size: 17px; margin: 0; }
.wms-sm .sm-radio input { width: 24px; height: 24px; }
.wms-sm .sm-sheet > summary, .wms-sm .sm-rest > summary { list-style: none; cursor: pointer; }
.wms-sm .sm-rest > summary { min-height: 48px; display: flex; align-items: center; font-size: 16px; color: var(--w-muted); }
.wms-sm .sm-sheet[open] { background: var(--w-surface); border: 1px solid var(--w-border); border-radius: 16px; padding: 10px 14px 14px; }
.wms-sm .sm-list-row { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 6px 0; border-bottom: 1px solid var(--w-border); font-size: 16px; }
.wms-sm .sm-list-row form .btn-wms { min-height: 48px; min-width: 48px; justify-content: center; }
.wms-sm .sm-bottom { position: fixed; left: 0; right: 0; bottom: 0; z-index: 3; display: flex; gap: 8px; padding: 8px 12px calc(8px + env(safe-area-inset-bottom)); background: var(--w-surface); border-top: 1px solid var(--w-border); }
.wms-sm .sm-bottom > *, .wms-sm .sm-bottom form { flex: 1 1 0; min-width: 0; }
.wms-sm .sm-bottom .btn-wms.sm-big { margin-top: 0; font-size: 16px; }
.wms-sm .sm-camera { position: fixed; inset: 0; z-index: 5; background: #000; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; padding: 16px; color: #fff; }
.wms-sm .sm-camera[hidden] { display: none; }
.wms-sm .sm-camera video { width: 100%; max-height: 70vh; object-fit: cover; border-radius: 12px; }
.wms-sm [hidden] { display: none !important; }

/* /superadmin/rights.php: a position's rights in two columns on a wide screen, one on a phone */
@media (min-width: 768px) {
    [data-rights-position] .org-perm-group { display: grid; grid-template-columns: 1fr 1fr; gap: 0 10px; }
    [data-rights-position] .org-perm-group > .org-perm-group-head { grid-column: 1 / -1; }
}

/* a page's top: the search box on a row of its own, full width, and the page's own links together
   on the row under it — the same two tidy rows whatever the screen and however many links */
.wms-search { display: flex; gap: 8px; flex: 1 1 100%; min-width: 0; }
.wms-search .form-control { flex: 1 1 auto; min-width: 0; }
.wms-search .btn-wms { flex: none; }
.wms-toolbar-top > .btn-wms { white-space: nowrap; }

/* a product attribute: its list of values and the "or type a new one" box, side by side */
.wms-value-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.wms-value-pair.single { grid-template-columns: 1fr; }
/* a select beside a text field takes the field's height (.form-control is 48px) */
.wms-value-pair .custom-select, .wms-select-tall { height: 48px; min-height: 48px; }
/* several values per attribute (wms/products.php): chips, a search box, and its suggestions */
.wms-multi { position: relative; }
.wms-multi select[multiple] { height: auto; min-height: 48px; }
.wms-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 6px; }
.wms-chips:empty { display: none; }
.wms-chip { display: inline-flex; align-items: center; gap: 4px; padding: 3px 4px 3px 10px; border-radius: 999px;
    background: var(--brand-soft, #e8f0fe); color: var(--ink, #1f2937); font-size: .9rem; line-height: 1.4; }
.wms-chip.new { background: #fff4e0; }
.wms-chip small { color: #9a6700; }
.wms-chip button { border: 0; background: transparent; color: inherit; width: 24px; height: 24px; border-radius: 50%; line-height: 1; font-size: 1rem; cursor: pointer; }
.wms-chip button:hover { background: rgba(0, 0, 0, .08); }
.wms-chip-input { height: 44px; }
.wms-chip-list { position: absolute; left: 0; right: 0; z-index: 20; max-height: 240px; overflow-y: auto; box-shadow: 0 6px 18px rgba(0, 0, 0, .12); }
@media (max-width: 480px) { .wms-value-pair { grid-template-columns: 1fr; } }

/* an attribute or value row on /wms/attributes.php: name, order, active and its button on one line */
.wms-attr-row { display: grid; grid-template-columns: minmax(0, 1fr) 76px 92px 150px; gap: 10px; align-items: center; margin-bottom: 10px; }
/* inside an .org-line block a field would otherwise keep its 96px minimum and spill out of its cell */
.wms-attr-row .form-control, .org-line .wms-attr-row .form-control { min-width: 0; width: 100%; flex: none; }
.wms-attr-row .wms-attr-sort { text-align: center; }
.wms-attr-active input { margin: 0; }
.wms-attr-active { display: flex; align-items: center; gap: 6px; white-space: nowrap; }
/* a tax item name row on /wms/tax_names.php: name | code | note | active | button */
.wms-tax-row { display: grid; grid-template-columns: minmax(0, 1fr) 92px 150px;
    grid-template-areas: "name name code" "note active act"; gap: 8px 10px; align-items: center; margin-bottom: 4px; }
.wms-tax-row .form-control, .org-line .wms-tax-row .form-control { min-width: 0; width: 100%; flex: none; }
.org-line.wms-tax-item { display: block; }
.wms-tax-row > [name="name"] { grid-area: name; }
.wms-tax-row > [name="code"] { grid-area: code; }
.wms-tax-row > [name="note"] { grid-area: note; }
.wms-tax-row > .wms-attr-active { grid-area: active; }
.wms-tax-row > .wms-attr-act { grid-area: act; }
@media (max-width: 575px) {
    .wms-tax-row { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "name name" "code code" "note note" "active act"; }
}
.wms-attr-act { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }
.wms-attr-act { align-items: stretch; }
.wms-attr-act .btn-wms { white-space: nowrap; justify-content: center; }
.wms-attr-act small { font-size: 11.5px; white-space: nowrap; }
/* a phone: name and order on the first line, active and the button on the second */
@media (max-width: 575px) {
    .wms-attr-row { grid-template-columns: minmax(0, 1fr) auto; }
    .wms-attr-row .wms-attr-sort, .org-line .wms-attr-row .wms-attr-sort { width: 76px; }
    .wms-attr-active { grid-column: 1; }
    .wms-attr-act { grid-column: 2; }
}

/* the pager is a row of its own and keeps its compact size */
.wms-pager-links .btn-wms { min-height: 36px; padding: 6px 10px; }

/* the withdrawal filter row: branch, person and two dates on one line, stacking when narrow */
.checkin-filter-form .wd-date { flex: 1 1 130px; min-width: 120px; margin: 0; display: flex; flex-direction: column; gap: 3px; }
.checkin-filter-form .wd-date small { color: var(--w-muted); font-size: 12px; }

/* the small cash/box marker beside a withdrawal, in the calendar cell and in the day detail */
.cal-wd-ico { font-size: .95em; opacity: .85; margin-right: 1px; }

/* the month's withdrawals at a glance: cash, goods, and the two together */
.wd-sum { border: 1px solid var(--w-border); border-radius: 12px; padding: 10px 12px 12px; margin-bottom: 12px; }
.wd-sum-head { font-size: 13px; font-weight: 600; color: var(--w-muted); margin-bottom: 8px; }
.wd-sum-parts { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.wd-sum-part { display: flex; flex-direction: column; align-items: center; gap: 2px; text-align: center; }
.wd-sum-part + .wd-sum-part { border-left: 1px solid var(--w-border); }
.wd-sum-part i { font-size: 17px; color: var(--w-muted); line-height: 1; }
.wd-sum-part small { font-size: 11.5px; color: var(--w-muted); line-height: 1.2; }
.wd-sum-part b { font-size: 16px; font-weight: 700; font-variant-numeric: tabular-nums; }
.wd-sum-part em { font-style: normal; font-size: 11px; color: var(--w-muted); }
.wd-sum-total i, .wd-sum-total b { color: var(--w-primary); }

/* The days either side of the month fill the first and last week out to seven. They are context,
   not this month, so they sit back: a darker, flatter ground and dimmed contents. Everything on
   them still works — they carry real dates and real data — they just do not compete for attention.
   This comes first so the holiday and weekly-off rules below still tint the ones that need it. */
.cal-cell.is-other-month { background: #eceef0; }
.cal-cell.is-other-month .cal-day-num { color: #9aa2a8; font-weight: 500; }
.cal-cell.is-other-month > *:not(.cal-day-num) { opacity: .55; }

/* The weekly day off is routine, a named holiday is not — so the routine one is grey and the
   named one red. Both say "closed"; only one is worth noticing. */
.cal-cell.is-weekly-off { background: var(--w-bg); }
.cal-cell.is-weekly-off .cal-day-num { color: var(--w-muted); }
.cal-weekly-off {
    display: block; margin-top: 2px; font-size: 9.5px; line-height: 1.15;
    color: var(--w-muted); font-weight: 600;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* a company holiday: the day is closed, so it reads differently from a day someone chose off */
.cal-cell.is-holiday { background: var(--w-danger-soft); }
.cal-cell.is-holiday .cal-day-num { color: var(--w-danger); font-weight: 700; }
.cal-holiday {
    display: block; margin-top: 2px; font-size: 9.5px; line-height: 1.15;
    color: var(--w-danger); font-weight: 600;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---- clocking in is green, clocking out is red ----
   These two say which direction you are moving, so they are fixed colours rather than the role
   palette. --w-primary is red for a super admin and gold for a company admin, which made the
   "เข้างาน" button red for exactly the people who look at it most — and red next to a red
   "ออกงาน" says nothing at all. One pair of tokens, used by the toggle and the history alike. */
:root {
    --w-in: #1b7f4b;  --w-in-soft: #e8f5ed;  --w-in-strong: #14603a;
    --w-out: #c62828; --w-out-soft: #fdecea; --w-out-strong: #a01d1d;
}
/* later than style.css, so these win over the role-coloured and the blanket locked rules there */
#typeIn:checked + .type-toggle-in,
.type-toggle-in.is-locked  { background: var(--w-in-soft);  border-color: var(--w-in);  color: var(--w-in); }
#typeOut:checked + .type-toggle-out,
.type-toggle-out.is-locked { background: var(--w-out-soft); border-color: var(--w-out); color: var(--w-out); }

/* ---- attendance history: one card per day, check-in on the left, check-out on the right ---- */
.att-day { border: 1px solid var(--w-border); border-radius: 12px; padding: 10px 12px 12px; margin-bottom: 10px; }
.att-date { font-size: 13px; font-weight: 600; color: var(--w-muted); display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }
.att-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.att-pair + .att-pair { margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--w-border); }
.att-side { text-align: center; padding: 8px 6px; border-radius: 10px; }
.att-in  { background: var(--w-in-soft); }
.att-out { background: var(--w-out-soft); }
.att-label { font-size: 12.5px; font-weight: 700; }
.att-in .att-label  { color: var(--w-in); }
.att-out .att-label { color: var(--w-out); }
.att-time { font-size: 19px; font-weight: 700; font-variant-numeric: tabular-nums; margin: 2px 0 8px; }
.att-in .att-time  { color: var(--w-in-strong); }
.att-out .att-time { color: var(--w-out-strong); }
.att-time.att-missing { color: var(--w-muted); font-weight: 500; }
.att-photo { display: block; width: 100%; aspect-ratio: 1 / 1; border-radius: 10px; overflow: hidden; background: rgba(0,0,0,.05); }
.att-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.att-photo-none { display: flex; align-items: center; justify-content: center; color: var(--w-muted); font-size: 20px; }

/* a history row that leads with a photo instead of an icon */
.m-row-media .m-thumb { width: 52px; height: 52px; border-radius: 10px; overflow: hidden; flex: none; display: block; background: rgba(0,0,0,.05); }
.m-row-media .m-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---- organisation editor: a division, the departments under it, and the position list ---- */
.org-division { border: 1px solid var(--w-border); border-radius: 12px; padding: 10px; margin-bottom: 12px; }
.org-division.is-off { opacity: .55; }
.org-departments { padding-left: 14px; border-left: 2px solid var(--w-primary-soft); margin: 8px 0 0 6px; }
/* Each row is its own little form: change a name, tick active, press save. The row has six things
   in it and the panel is ~570px wide, so it wraps — and when it did, the save button landed on a
   line by itself looking like it belonged to nothing. Pushed to the end and kept with the row. */
/* Each row is its own little form: change a name, tick active, press save. The department row
   carries six things — tag, name, which division, order, active, save — in about 530px, and at the
   old widths the save button was pushed onto a line of its own looking like it belonged to nothing.
   These sizes are what let the whole row sit on one line; the dashed rule keeps a wrapped row
   visibly together when the screen really is too narrow. */
.org-line { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-bottom: 8px;
            padding-bottom: 8px; border-bottom: 1px dashed var(--w-border); }
.org-line:last-child { border-bottom: 0; }
/* the save button is the last thing to fit, so it gets the tighter padding of the two */
.org-line .org-save { margin-left: auto; flex: none; padding: 0 12px; }
.org-line.is-off { opacity: .55; }
/* basis equal to min-width: wrapping is decided on the basis, and shrinking only happens after a
   line is already full — a basis wider than the minimum pushed the button onto its own line */
.org-line .form-control { flex: 1 1 96px; min-width: 96px; }
.org-line .org-sort { flex: 0 0 60px; min-width: 60px; }
.org-line .org-scope, .org-line .org-move { flex: 0 0 auto; width: auto; min-width: 104px; }
.org-tag { flex: none; font-size: 11.5px; font-weight: 700; padding: 3px 8px; border-radius: 999px; }
/* what a position lets its holder do — folded away under each position, since most of the time
   you are renaming or reordering, not changing what the job may do */
.org-perms { margin: -4px 0 10px 0; }
/* positions as tabs (admin/org.php): a row of chips that scrolls sideways when there are many */
.org-pos-tabs { display: flex; gap: 6px; overflow-x: auto; padding: 2px 2px 10px; margin-bottom: 10px; border-bottom: 1px solid var(--w-border); scrollbar-width: thin; }
.org-pos-tabs[hidden] { display: none; }
.org-pos-tab {
    flex: none; display: inline-flex; align-items: center; gap: 6px; max-width: 240px;
    border: 1px solid var(--w-border); border-radius: 999px; background: var(--w-surface); color: var(--w-text);
    font: inherit; font-size: 13px; padding: 4px 10px 4px 4px; cursor: pointer;
}
.org-pos-tab .org-rank { width: 22px; height: 22px; font-size: 11px; }
.org-pos-tab-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.org-pos-tab .org-perm-count { margin-left: 0; }
.org-pos-tab:hover { border-color: var(--w-primary-soft-border); }
.org-pos-tab.is-on { background: var(--w-primary-soft); border-color: var(--w-primary-soft-border); color: var(--w-primary); font-weight: 600; }
.org-pos-tab.is-off .org-pos-tab-name { text-decoration: line-through; color: var(--w-muted); }
.org-pos-tab:focus-visible { outline: 2px solid var(--w-primary); outline-offset: 2px; }
.org-pos-tab-add { padding: 4px 12px; color: var(--w-primary); border-style: dashed; }
.org-pos[hidden] { display: none; }
.org-perms > summary {
    display: flex; align-items: center; gap: 7px; cursor: pointer; list-style: none;
    padding: 7px 10px; border-radius: var(--w-radius-sm); background: var(--w-bg);
    font-size: 13px; font-weight: 600; color: var(--w-muted);
}
.org-perms > summary::-webkit-details-marker { display: none; }
.org-perms > summary:hover { color: var(--w-text); }
.org-perm-count {
    margin-left: auto; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px;
    background: var(--w-primary-soft); color: var(--w-primary);
    font-size: 11.5px; font-weight: 700; line-height: 20px; text-align: center;
}
.org-perm-form { padding: 10px 2px 2px; }
.org-perm-group + .org-perm-group { margin-top: 10px; }
.org-perm-group-head { font-size: 12px; font-weight: 700; color: var(--w-muted); margin-bottom: 4px; }

.org-tag-company { background: #fde8ef; color: #9d174d; }
.org-tag-division { background: var(--w-primary-soft); color: var(--w-primary); }
.org-tag-department { background: var(--w-bg); color: var(--w-muted); }
.org-rank { flex: none; width: 26px; height: 26px; border-radius: 50%; background: var(--w-primary-soft); color: var(--w-primary);
    display: inline-flex; align-items: center; justify-content: center; font-size: 12.5px; font-weight: 700; }
.org-active { flex: none; display: inline-flex; align-items: center; gap: 5px; margin: 0; font-size: 13px; color: var(--w-muted); }
.org-active input { width: 16px; height: 16px; }
.org-add { padding-top: 6px; border-top: 1px dashed var(--w-border); }
.org-add-division { margin-top: 10px; }

/* ---- a plain table for the non-WMS pages ------------------------------------------------
   .wms-table lived in the warehouse stylesheet (removed with the WMS). Same look, defined where
   every page can reach it. */
.table-wrap { overflow-x: auto; border: 1px solid var(--w-border); border-radius: 10px; background: var(--w-surface); }
.table-lite { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.table-lite th {
    font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--w-muted);
    text-align: left; padding: 8px 10px; background: var(--w-bg);
    border-bottom: 1px solid var(--w-border); white-space: nowrap;
}
.table-lite td { padding: 8px 10px; border-bottom: 1px solid var(--w-border); vertical-align: top; }
.table-lite tr:last-child td { border-bottom: 0; }

/* a notice that is neither good news nor bad — "waiting on somebody else" */
.alert-info { background: #eef4fb; color: #1d4ed8; border-color: #cfe0f5; }

/* ---- the company's @name ---------------------------------------------------------------- */
.slug-now {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    padding: 12px 14px; margin: 10px 0; border: 1px solid var(--w-border);
    border-radius: 10px; background: var(--w-bg);
}
.slug-now-label { font-size: 12.5px; color: var(--w-muted); }
.slug-now-value {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 18px; font-weight: 700;
    color: var(--w-primary); background: var(--w-surface);
    padding: 4px 10px; border-radius: 7px; border: 1px solid var(--w-border);
}
/* the @ sits inside the field so it is plain that only the part after it is being typed */
.slug-input { position: relative; }
.slug-input-at {
    position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
    font-size: 17px; font-weight: 700; color: var(--w-muted); pointer-events: none;
}
.slug-input .form-control { padding-left: 32px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

.slug-req { padding: 14px; border: 1px solid var(--w-border); border-radius: 12px; background: var(--w-surface); }
.slug-req + .slug-req { margin-top: 12px; }
.slug-req-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.slug-req-move { display: inline-flex; align-items: center; gap: 8px; font-size: 14px; }
.slug-req-move code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.slug-req-want { color: var(--w-primary); font-weight: 700; }
.slug-req-fields { max-width: 320px; }

/* ---- announcements ---------------------------------------------------------------------
   The megaphone sits beside the search box, and only a position carrying chat.announce draws it. */
.chat-announce-btn {
    flex: none; display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; border: 1px solid var(--w-border); border-radius: 10px;
    background: var(--w-surface); color: var(--w-primary); text-decoration: none;
}
.chat-announce-btn:hover, .chat-announce-btn:focus { background: var(--w-primary-soft); color: var(--w-primary); text-decoration: none; }
.chat-announce-btn i { font-size: 15px; line-height: 1; }
.chat-filter-head .chat-announce-btn { order: 1; }
.chat-filter-head .chat-new-btn { order: 2; }

.announce-branch {
    margin: 12px 0 4px; font-size: 12px; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase; color: var(--w-muted);
}
.announce-branch:first-of-type { margin-top: 4px; }

/* An announcement lands in a thread that otherwise looks like any other private chat, so it says
   what it is. The stripe is on the leading edge, where the eye already is. */
.msg-bubble.is-announce { border-left: 3px solid var(--w-primary); }
.msg-announce-tag {
    display: flex; align-items: center; gap: 5px; margin-bottom: 5px;
    font-size: 11px; font-weight: 700; letter-spacing: .04em; color: var(--w-primary);
}
.msg-announce-tag i { font-size: 11px; }
/* the company's face on an announcement, where a person's photo would otherwise be */
.chat-avatar.is-company, .msg-avatar.is-company {
    background: var(--w-primary-soft); color: var(--w-primary);
    display: inline-flex; align-items: center; justify-content: center;
}
.login-name-hint { margin: 6px 0 14px; font-size: 12.5px; line-height: 1.45; }
/* who actually pressed send. The announcement is from the company; this says who signed it. */
.msg-announce-by {
    margin-top: 6px; padding-top: 5px; border-top: 1px solid var(--w-border);
    font-size: 11.5px; color: var(--w-muted);
}

/* ---- Dropbox storage pool ---------------------------------------------------------------- */
.dbx-queue { display: flex; gap: 10px; flex-wrap: wrap; margin: 4px 0 10px; }
.dbx-stat {
    flex: 1 1 80px; min-width: 0; padding: 10px 12px; border: 1px solid var(--w-border);
    border-radius: 10px; background: var(--w-bg);
}
.dbx-stat b { display: block; font-size: 21px; line-height: 1.1; font-variant-numeric: tabular-nums; }
.dbx-stat small { display: block; font-size: 11.5px; color: var(--w-muted); margin-top: 2px; }
.dbx-stat.is-bad b { color: var(--w-danger); }

.dbx-acct { padding: 14px 0; border-bottom: 1px solid var(--w-border); }
.dbx-acct:last-of-type { border-bottom: 0; }
.dbx-acct-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 7px; }
.dbx-acct-num { margin-left: auto; font-size: 12.5px; color: var(--w-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.dbx-bar { height: 9px; border-radius: 5px; background: var(--w-border); overflow: hidden; }
.dbx-bar span { display: block; height: 100%; border-radius: 5px; }
.dbx-bar span.ok { background: var(--w-in, #2e7d32); }
.dbx-bar span.warn { background: #e8710a; }
.dbx-bar span.crit { background: var(--w-danger, #d32f2f); }
.dbx-bar span.off { background: var(--w-muted); }
.dbx-acct-note { font-size: 12px; color: var(--w-muted); margin-top: 6px; }
.dbx-acct-err { font-size: 12.5px; color: var(--w-danger); margin-top: 5px; }
.dbx-acct-actions { display: flex; gap: 8px; align-items: center; margin-top: 10px; max-width: 320px; }
.dbx-acct-actions .form-control { flex: 1 1 auto; min-width: 0; }
.dbx-inline { margin: 0; }

.dbx-tag { display: inline-block; font-size: 10.5px; font-weight: 700; letter-spacing: .06em; padding: 2px 7px; border-radius: 4px; }
.dbx-tag.ok { background: var(--w-primary-soft); color: var(--w-primary); }
.dbx-tag.warn { background: #fdf0e3; color: #9a5b06; }
.dbx-tag.crit { background: var(--w-danger-soft); color: var(--w-danger); }
.dbx-tag.off { background: var(--w-bg); color: var(--w-muted); }
.ret-row {
    display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap;
    padding: 12px 0; border-bottom: 1px solid var(--w-border);
}
.ret-row:last-of-type { border-bottom: 0; }
.ret-co { flex: 1 1 150px; min-width: 0; }
.ret-co b { display: block; font-size: 15px; }
.ret-co small { display: block; font-size: 12px; color: var(--w-muted); }
.ret-field { flex: 0 1 165px; margin: 0; }
.ret-field span { display: block; font-size: 11.5px; color: var(--w-muted); margin-bottom: 3px; }
/* what a bubble shows once retention has taken the bytes */
.msg-expired {
    display: flex; align-items: center; gap: 7px; padding: 8px 10px;
    border: 1px dashed var(--w-border); border-radius: 8px;
    font-size: 13px; color: var(--w-muted);
}
.ret-pair { display: flex; gap: 10px; flex-wrap: wrap; }
.ret-pair .ret-field { flex: 1 1 160px; }
.dbx-bar.mb-2 { margin-bottom: 12px; }

/* ---- chat file storage: what is there, and clearing some out ----------------------------- */
.cs-total { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.cs-total b { font-size: 26px; line-height: 1.1; font-variant-numeric: tabular-nums; }
.cs-total span { font-size: 14px; color: var(--w-muted); }
.cs-total small { margin-left: auto; font-size: 12.5px; color: var(--w-muted); }

.cs-kinds { display: flex; flex-direction: column; gap: 2px; margin-top: 10px; }
.cs-kind { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--w-border); }
.cs-kind:last-child { border-bottom: 0; }
.cs-kind-ico {
    flex: none; width: 32px; height: 32px; border-radius: 9px; background: var(--w-bg);
    display: inline-flex; align-items: center; justify-content: center; color: var(--w-muted);
}
.cs-kind-body { flex: 1 1 auto; min-width: 0; }
.cs-kind-body b { display: block; font-size: 14.5px; }
.cs-kind-body small { display: block; font-size: 12px; color: var(--w-muted); }
.cs-kind-size { flex: none; font-size: 14px; font-variant-numeric: tabular-nums; }

.cs-days, .cs-range { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end; }
.cs-days .ret-field { flex: 1 1 150px; }
.cs-range .ret-field { flex: 0 1 165px; }
.cs-purge-kinds { display: flex; flex-direction: column; gap: 2px; margin-bottom: 12px; }

.cs-preview {
    margin-top: 14px; padding: 14px; border: 1px solid var(--w-danger-border, var(--w-border));
    border-radius: 10px; background: var(--w-danger-soft, var(--w-bg));
}
.cs-preview.is-empty { border-color: var(--w-border); background: var(--w-bg); color: var(--w-muted); font-size: 13.5px; }
.cs-preview-head { margin-bottom: 10px; }
.cs-preview-head b { display: block; font-size: 15.5px; }
.cs-preview-head small { display: block; font-size: 12.5px; color: var(--w-muted); margin-top: 2px; }

/* ---- replying, reacting, finding ---------------------------------------------------------- */
.msg-quote {
    display: block; width: 100%; text-align: left; margin: 0 0 6px; padding: 5px 8px;
    border: 0; border-left: 3px solid var(--w-primary); border-radius: 6px;
    background: rgba(0, 0, 0, .05); cursor: pointer; font: inherit;
}
.msg-quote-name { display: block; font-size: 11.5px; font-weight: 700; color: var(--w-primary); }
.msg-quote-text { display: block; font-size: 12.5px; color: var(--w-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-row.is-flash .msg-bubble { animation: msgFlash 1.4s ease; }
@keyframes msgFlash { 0%, 60% { box-shadow: 0 0 0 3px var(--w-primary-soft); } 100% { box-shadow: none; } }
@media (prefers-reduced-motion: reduce) { .msg-row.is-flash .msg-bubble { animation: none; box-shadow: 0 0 0 3px var(--w-primary-soft); } }

.msg-reacts { display: flex; gap: 4px; flex-wrap: wrap; margin: 4px 0 0; }
.msg-react {
    display: inline-flex; align-items: center; gap: 3px; padding: 1px 7px;
    border: 1px solid var(--w-border); border-radius: 999px; background: var(--w-surface);
    font-size: 13px; line-height: 1.6; cursor: pointer;
}
.msg-react.mine { border-color: var(--w-primary); background: var(--w-primary-soft); }
.msg-react span { font-size: 11.5px; font-weight: 700; color: var(--w-muted); }
.msg-react.mine span { color: var(--w-primary); }
.msg-reply-btn, .msg-react-btn {
    border: 0; background: none; padding: 0 6px 0 0; font-size: 11.5px; color: var(--w-muted); cursor: pointer;
}
.msg-reply-btn:hover, .msg-react-btn:hover { color: var(--w-primary); }

.react-picker {
    position: fixed; z-index: 60; display: flex; gap: 2px; padding: 5px 7px;
    background: var(--w-surface); border: 1px solid var(--w-border); border-radius: 999px;
    box-shadow: 0 6px 20px rgba(16, 24, 40, .16);
}
.react-picker button { border: 0; background: none; font-size: 20px; line-height: 1; padding: 3px 4px; cursor: pointer; border-radius: 50%; }
.react-picker button:hover { background: var(--w-bg); }

.composer-reply {
    display: flex; align-items: center; gap: 8px; padding: 7px 10px; margin-bottom: 6px;
    border-left: 3px solid var(--w-primary); border-radius: 6px; background: var(--w-bg);
}
/* the picture being answered, where there is one — small enough to stay a label, not a preview pane */
.composer-reply-thumb {
    flex: none; width: 34px; height: 34px; border-radius: 6px; object-fit: cover;
    background: var(--w-bg);
}
.composer-reply-body { flex: 1 1 auto; min-width: 0; }
.composer-reply-body b { display: block; font-size: 12px; color: var(--w-primary); }
.composer-reply-body small { display: block; font-size: 12px; color: var(--w-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.composer-reply-x { flex: none; border: 0; background: none; color: var(--w-muted); cursor: pointer; padding: 4px; }

.chat-find { padding: 8px 12px; border-bottom: 1px solid var(--w-border); background: var(--w-surface); }
.chat-find-results { max-height: 45vh; overflow-y: auto; margin-top: 6px; }
.chat-find-hit {
    display: block; width: 100%; text-align: left; border: 0; border-bottom: 1px solid var(--w-border);
    background: none; padding: 8px 2px; cursor: pointer; font: inherit;
}
.chat-find-hit:last-child { border-bottom: 0; }
.chat-find-hit b { font-size: 13px; }
.chat-find-hit small { font-size: 11.5px; color: var(--w-muted); margin-left: 6px; }
.chat-find-hit span { display: block; font-size: 13px; color: var(--w-text); margin-top: 2px; }
.chat-find-none { padding: 10px 2px; font-size: 13px; color: var(--w-muted); }
.chat-call-btn.is-on { color: var(--w-danger); }

/* ---- the media list ---- */
.media-row { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--w-border); }
.media-row:last-child { border-bottom: 0; }
.media-row.is-gone { opacity: .55; }
.media-thumb { flex: none; width: 44px; height: 44px; border-radius: 9px; overflow: hidden; background: var(--w-bg); }
.media-thumb img { width: 100%; height: 100%; object-fit: cover; }
.media-ico { display: inline-flex; align-items: center; justify-content: center; color: var(--w-muted); font-size: 18px; }
.media-body { flex: 1 1 auto; min-width: 0; }
.media-body b { display: block; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.media-body small { display: block; font-size: 11.5px; color: var(--w-muted); }
.media-open, .media-jump { flex: none; padding: 6px; color: var(--w-muted); }
.media-gone { flex: none; font-size: 11.5px; color: var(--w-muted); }

/* ---- who read an announcement ---- */
.ar-head { margin-bottom: 8px; }
.ar-body { font-size: 14.5px; }
.ar-head small { font-size: 11.5px; color: var(--w-muted); }
.ar-count { font-size: 13px; color: var(--w-muted); margin-bottom: 5px; }
.ar-count b { font-size: 16px; color: var(--w-text); font-variant-numeric: tabular-nums; }
.ar-group { margin-top: 12px; }
.ar-group-head { font-size: 12.5px; font-weight: 700; margin-bottom: 4px; cursor: pointer; }
.ar-group-head.ar-unread { color: var(--w-danger); }
.ar-group-head.ar-read { color: var(--w-primary); }
.ar-person { font-size: 13.5px; padding: 3px 0 3px 18px; }
.ar-person small { font-size: 11.5px; color: var(--w-muted); margin-left: 6px; }

/* ---- the unread line, the pinned bar, @everyone ------------------------------------------- */
.msg-unread-line {
    display: flex; align-items: center; gap: 10px; margin: 14px 0 8px;
    font-size: 11.5px; font-weight: 700; color: var(--w-danger);
}
.msg-unread-line::before, .msg-unread-line::after {
    content: ''; flex: 1 1 auto; height: 1px; background: var(--w-danger); opacity: .35;
}

.chat-pinned {
    display: flex; align-items: center; gap: 8px; padding: 8px 12px;
    background: var(--w-primary-soft); border-bottom: 1px solid var(--w-border); color: var(--w-primary);
}
.chat-pinned > i { flex: none; font-size: 14px; }
.chat-pinned-body {
    flex: 1 1 auto; min-width: 0; text-align: left; border: 0; background: none;
    padding: 0; cursor: pointer; font: inherit; color: inherit;
}
.chat-pinned-body b { display: block; font-size: 11.5px; }
.chat-pinned-body span {
    display: block; font-size: 12.5px; color: var(--w-text); opacity: .8;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chat-pinned-x { flex: none; border: 0; background: none; color: inherit; cursor: pointer; padding: 4px; }
.msg-pin-btn { border: 0; background: none; padding: 0 6px 0 0; font-size: 12px; color: var(--w-muted); cursor: pointer; }
.msg-pin-btn:hover { color: var(--w-primary); }

.mention-all { font-weight: 700; }

/* ---- the chat header ------------------------------------------------------------------------
   It once carried up to seven round icons in a row: choose, notes, search, pictures, bell, call,
   video, settings. At 390px the last of them sat past the edge and the title had been squeezed to
   nothing — and even where they fitted, nothing said which was which.
   Search keeps its button, because finding a line is the thing done often. Everything else is in
   the menu, where each has its name beside it. */
.chat-header-text { flex: 1 1 auto; min-width: 0; }
.chat-header-menu { position: relative; flex: none; }
.chat-menu {
    position: absolute; top: calc(100% + 8px); right: 0; z-index: 60;
    min-width: 232px; max-width: calc(100vw - 24px);
    padding: 6px; border-radius: 14px;
    background: var(--w-surface); border: 1px solid var(--w-border);
    box-shadow: 0 14px 34px rgba(16, 24, 40, .18);
    animation: chat-menu-in .16s ease;
}
.chat-menu[hidden] { display: none; }
@keyframes chat-menu-in { from { opacity: 0; transform: translateY(-8px); } }
.chat-menu-item {
    display: flex; align-items: center; gap: 11px; width: 100%;
    padding: 10px 11px; border: 0; border-radius: 10px; background: transparent;
    font-size: 14.5px; font-weight: 600; color: var(--w-text); text-align: left; cursor: pointer;
}
.chat-menu-item:hover, .chat-menu-item:focus-visible { background: var(--w-bg); color: var(--w-text); text-decoration: none; }
.chat-menu-item i { flex: none; width: 22px; text-align: center; font-size: 17px; color: var(--w-primary); }
.chat-menu-item span { flex: 1; min-width: 0; }
.chat-menu .chat-video-btn { margin-left: 0; }   /* the gap it took beside a round button indents the row here */
.chat-menu-item.is-on, .chat-menu-item.is-on i { color: var(--w-danger); }
.chat-menu-sep { height: 1px; margin: 5px 9px; background: var(--w-border); }
@media (prefers-reduced-motion: reduce) { .chat-menu { animation: none; } }
@media (max-width: 480px) {
    .chat-header { gap: 6px; padding-left: 10px; padding-right: 10px; }
    .chat-header .chat-call-btn { width: 36px; height: 36px; }
    .chat-header .chat-call-btn i { font-size: 16px; }
    .chat-header .chat-avatar { width: 38px; height: 38px; }
}

/* A quote inside my own bubble sits on the accent colour, where the muted grey it uses on a pale
   bubble is unreadable. Same shape, colours taken from the bubble it is on. */
.msg-row.me .msg-quote { background: rgba(255, 255, 255, .16); border-left-color: rgba(255, 255, 255, .75); }
.msg-row.me .msg-quote-name { color: #fff; }
.msg-row.me .msg-quote-text { color: rgba(255, 255, 255, .88); }

/* ---- choosing messages to keep ------------------------------------------------------------ */
body.is-selecting .msg-row { cursor: pointer; position: relative; }
body.is-selecting .msg-bubble { opacity: .7; }
body.is-selecting .msg-row.is-chosen .msg-bubble { opacity: 1; box-shadow: 0 0 0 2px var(--w-in, #2e7d32); }

/* A ring on every message while choosing, filled with a tick on the ones picked — the empty ring
   is what says "this is choosable", and the difference between the two reads at a glance. */
body.is-selecting .msg-row::after {
    content: ''; position: absolute; top: 50%; right: 4px; transform: translateY(-50%);
    width: 22px; height: 22px; border-radius: 50%;
    border: 2px solid var(--w-border); background: var(--w-surface);
    pointer-events: none; transition: background .12s ease, border-color .12s ease;
}
body.is-selecting .msg-row.is-chosen::after {
    border-color: var(--w-in, #2e7d32); background: var(--w-in, #2e7d32);
    /* a tick, drawn rather than fetched */
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23fff' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 8.5l3.2 3.2L13 5'/%3E%3C/svg%3E");
    background-size: 14px 14px; background-position: center; background-repeat: no-repeat;
}
/* room for the ring, so it never sits on top of the words */
body.is-selecting .msg-col { padding-right: 30px; }
body.is-selecting .msg-reply-btn,
body.is-selecting .msg-react-btn,
body.is-selecting .msg-pin-btn,
body.is-selecting .msg-share-btn,
body.is-selecting .msg-resend-btn,
body.is-selecting .msg-download-btn { visibility: hidden; }

.select-bar {
    display: flex; align-items: center; gap: 10px; padding: 9px 12px;
    background: var(--w-surface); border-top: 1px solid var(--w-border);
}
.select-count { flex: 1 1 auto; font-size: 13.5px; color: var(--w-muted); }
.select-count b { font-size: 16px; color: var(--w-text); font-variant-numeric: tabular-nums; }
.select-done {
    position: fixed; left: 50%; bottom: 90px; transform: translateX(-50%); z-index: 70;
    padding: 9px 16px; border-radius: 999px; background: var(--w-text); color: var(--w-surface);
    font-size: 13.5px; box-shadow: 0 6px 20px rgba(16, 24, 40, .22);
}

.note-picker { position: fixed; inset: 0; z-index: 65; background: rgba(16, 24, 40, .45); display: flex; align-items: flex-end; }
.note-picker-card {
    width: 100%; max-height: 80vh; max-height: 80dvh;
    display: flex; flex-direction: column; overflow: hidden;
    background: var(--w-surface);
    border-radius: 18px 18px 0 0; padding: 18px 16px calc(18px + env(safe-area-inset-bottom));
}
.note-picker-head { flex: none; font-size: 17px; font-weight: 700; margin: 0 0 12px; }
/* the only part that scrolls. min-height:0 or a flex child refuses to shrink below its content
   and the footer goes off the bottom, which is exactly what it did. */
.note-picker-list {
    flex: 1 1 auto; min-height: 0; overflow-y: auto;
    display: flex; flex-direction: column; gap: 2px; margin-bottom: 14px;
}
/* naming a new note never scrolls away: with no notes yet it is the only thing to do here */
.note-picker-foot { flex: none; border-top: 1px solid var(--w-border); padding-top: 12px; }
.note-pick {
    display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
    padding: 11px 12px; border: 1px solid var(--w-border); border-radius: 10px;
    background: var(--w-surface); cursor: pointer; font: inherit;
}
.note-pick:hover { background: var(--w-bg); }
.note-pick b { flex: 1 1 auto; min-width: 0; font-size: 14.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.note-pick small { flex: none; font-size: 12px; color: var(--w-muted); }
.note-picker-new { display: flex; gap: 8px; }
.note-picker-new .form-control { flex: 1 1 auto; min-width: 0; }

/* ---- a note and what is in it ---- */
.note-new, .note-inline { display: flex; gap: 8px; margin: 0; }
.note-new .form-control, .note-inline .form-control { flex: 1 1 auto; min-width: 0; }
.note-item-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 8px; }
.note-item-head b { font-size: 14.5px; }
.note-item-head small { flex: 1 1 auto; font-size: 11.5px; color: var(--w-muted); }
.note-item-x { margin: 0; }
.note-item-x button { border: 0; background: none; color: var(--w-muted); cursor: pointer; padding: 2px 4px; }
.note-item-body { font-size: 14.5px; margin-bottom: 8px; }
.note-item-img { display: block; width: 220px; max-width: 100%; border-radius: 10px; overflow: hidden; background: var(--w-bg); }
.note-item-img img { display: block; width: 100%; height: 160px; object-fit: cover; }
.note-item-go { display: inline-block; margin-top: 10px; font-size: 12.5px; }
.composer-none {
    display: flex; align-items: center; justify-content: center; gap: 8px;
    padding: 14px 12px; font-size: 13px; color: var(--w-muted);
    background: var(--w-bg); border-top: 1px solid var(--w-border);
}

/* ---- signing another browser in by scanning it --------------------------------------------
   Two screens are involved, so each has to say plainly which half it is doing: one shows a code
   to be photographed and then six digits to be read off it; the other holds the camera and then
   asks for those digits. */
.login-or { display: flex; align-items: center; gap: 10px; margin: 18px 0 12px; color: var(--w-muted); font-size: 12.5px; }
.login-or::before, .login-or::after { content: ""; flex: 1; height: 1px; background: var(--w-border); }
.login-qr-btn { width: 100%; justify-content: center; }
.login-qr { margin-top: 14px; text-align: center; }
.login-qr-box {
    display: inline-flex; padding: 10px; border-radius: 14px;
    background: #fff; border: 1px solid var(--w-border); min-height: 60px; min-width: 60px;
}
.login-qr-box svg { width: 190px; height: 190px; display: block; }
/* once it has been scanned there is nothing left to photograph — say so rather than inviting a
   second phone to try */
.login-qr-box.is-spent { opacity: .22; filter: grayscale(1); }
.login-qr-step { margin: 10px 0 0; font-size: 13px; color: var(--w-muted); line-height: 1.5; }
.login-qr-code { margin-top: 12px; }
.login-qr-code small { display: block; font-size: 12px; color: var(--w-muted); }
.login-qr-code b {
    display: block; margin-top: 2px;
    font-size: 34px; font-weight: 700; letter-spacing: .16em;
    font-variant-numeric: tabular-nums; color: var(--w-primary-dark);
}
.login-qr-again { margin-top: 12px; }

.scan-stage {
    position: relative; border-radius: 14px; overflow: hidden; background: #0b1220;
    aspect-ratio: 4 / 3; max-width: 100%; display: flex; align-items: center; justify-content: center;
}
.scan-stage video { width: 100%; height: 100%; object-fit: cover; display: block; }
.scan-frame {
    position: absolute; inset: 16% 14%; border: 2px solid rgba(255, 255, 255, .85); border-radius: 12px;
    box-shadow: 0 0 0 100vmax rgba(8, 14, 25, .35);
}
.scan-device { margin: 0 0 4px; font-weight: 700; font-size: 14.5px; }
.scan-code-input {
    text-align: center; font-size: 30px; font-weight: 700; letter-spacing: .3em;
    font-variant-numeric: tabular-nums; padding: 10px 12px;
}
.scan-err { margin: 10px 0 0; font-size: 13px; color: var(--w-danger); }
.scan-done { text-align: center; padding: 18px 8px 8px; }
.scan-done i { font-size: 42px; color: var(--w-ok, #15803d); }
.scan-done b { display: block; margin-top: 8px; font-size: 16px; }
.scan-done small { display: block; margin-top: 4px; font-size: 12.5px; color: var(--w-muted); line-height: 1.5; }

/* ---- the two browsers an account may be signed in on ---- */
.dev-row { display: flex; align-items: center; gap: 11px; padding: 11px 0; border-bottom: 1px solid var(--w-border); }
.dev-row:last-of-type { border-bottom: 0; }
.dev-ico {
    flex: none; width: 34px; height: 34px; border-radius: 10px;
    background: var(--w-primary-soft); color: var(--w-primary);
    display: flex; align-items: center; justify-content: center; font-size: 16px;
}
.dev-row.is-me .dev-ico { background: var(--w-primary); color: #fff; }
.dev-body { flex: 1; min-width: 0; }
.dev-body b { display: block; font-size: 14.5px; }
.dev-body small { display: block; font-size: 12px; color: var(--w-muted); margin-top: 1px; }
.dev-here { font-size: 11.5px; font-weight: 600; color: var(--w-primary); }
.dev-end { flex: none; margin: 0; }
.dev-end .btn-wms { padding: 6px 10px; font-size: 12.5px; }

/* ---- a message that is on screen before the server has it ---------------------------------
   The bubble is drawn the instant Send is pressed. It is dimmed, and carries a clock rather than
   a time, so the difference between "sent" and "on its way" is visible without being alarming. */
.msg-row.is-pending .msg-bubble { opacity: .62; }
.msg-row.is-pending .msg-image { max-width: 190px; border-radius: 12px; display: block; }
.msg-sending, .msg-failed { display: inline-flex; align-items: center; gap: 4px; font-size: 11.5px; color: var(--w-muted); }
.msg-sending i, .msg-failed i { font-size: 12px; }
.msg-row.is-failed .msg-bubble { opacity: .62; outline: 1px solid var(--w-danger); outline-offset: 1px; }
.msg-failed { color: var(--w-danger); }
.msg-retry-btn {
    border: 0; background: transparent; padding: 0 2px;
    font-size: 11.5px; font-weight: 700; color: var(--w-primary); cursor: pointer;
}
.msg-retry-btn:hover { text-decoration: underline; }
/* the one thing in the header menu that takes something away */
.chat-menu-item.is-danger, .chat-menu-item.is-danger i { color: var(--w-danger); }
.chat-menu-item.is-danger:hover { background: #fdecea; color: var(--w-danger); }

/* ---- a Facebook page or LINE account joined to the chat ------------------------------------ */
.chan-url { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.chan-url code {
    flex: 1; min-width: 0; overflow-x: auto; white-space: nowrap;
    padding: 9px 10px; border-radius: 10px; background: var(--w-bg);
    font-size: 12px; color: var(--w-text); -webkit-overflow-scrolling: touch;
}
.chan-url .btn-wms { flex: none; padding: 8px 10px; }
.chan-url .form-control { flex: 1; min-width: 0; margin: 0; }
.chan-agents { display: grid; gap: 4px; margin-bottom: 4px; }
.chan-log {
    display: grid; gap: 2px;
    /* a channel keeps its last two hundred events, and setting one up produces a dozen in a minute.
       Fixed height and scroll, so the form above it stays reachable without a long scroll past it. */
    max-height: 150px; overflow-y: auto; -webkit-overflow-scrolling: touch;
    padding-right: 4px;
}
.chan-log-row { display: flex; align-items: center; gap: 9px; padding: 7px 0; border-bottom: 1px solid var(--w-border); }
.chan-log-row:last-child { border-bottom: 0; }
.chan-log-dir {
    flex: none; width: 26px; height: 26px; border-radius: 8px;
    background: var(--w-primary-soft); color: var(--w-primary);
    display: flex; align-items: center; justify-content: center; font-size: 13px;
}
.chan-log-row.is-bad .chan-log-dir { background: #fdecea; color: var(--w-danger); }
.chan-log-body { flex: 1; min-width: 0; }
.chan-log-body b { display: block; font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chan-log-body small { font-size: 11.5px; color: var(--w-muted); }
.chan-add .form-control { margin-bottom: 4px; }

/* the badge a room wears when the person on the other side is a customer, not a colleague */
.chat-ext-tag {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 1px 7px; border-radius: 9px; font-size: 11px; font-weight: 700;
    background: var(--w-primary-soft); color: var(--w-primary);
}
.chat-avatar.is-ext { background: var(--w-primary-soft); color: var(--w-primary); }

/* a reply the platform would not take. It is stored and on screen either way, so this is the only
   thing that stops the person who typed it believing the customer has it. */
.msg-relay-failed {
    display: flex; align-items: flex-start; gap: 5px; margin-top: 3px;
    font-size: 11.5px; line-height: 1.4; color: var(--w-danger);
}
.msg-relay-failed i { flex: none; margin-top: 1px; }
/* delivery uncertain (review r3 #1): not a refusal, so not red; the resend asks first */
.msg-relay-unknown { color: var(--w-warning, #b7791f); }
.msg-relay-resend {
    flex: none; border: 1px solid currentColor; background: none; color: inherit; border-radius: 10px;
    padding: 0 8px; font-size: 11px; line-height: 1.6; cursor: pointer;
}
.msg-relay-resend:disabled { opacity: .5; cursor: default; }

/* ---- the step-by-step for connecting a page or an account ---- */
.chan-guide { margin-bottom: 14px; border: 1px solid var(--w-border); border-radius: 12px; overflow: hidden; }
.chan-guide > summary {
    display: flex; align-items: center; gap: 8px; cursor: pointer; list-style: none;
    padding: 11px 12px; font-size: 14px; font-weight: 700; color: var(--w-primary-dark);
    background: var(--w-primary-soft);
}
.chan-guide > summary::-webkit-details-marker { display: none; }
.chan-guide > .hint { padding: 10px 12px 0; margin: 0; }
.chan-steps { margin: 8px 0 0; padding: 0 14px 0 30px; }
.chan-steps li { margin-bottom: 9px; font-size: 13.5px; line-height: 1.55; }
.chan-steps li::marker { font-weight: 700; color: var(--w-primary); }
.chan-guide-check {
    display: flex; align-items: flex-start; gap: 6px; margin: 4px 12px 12px;
    padding: 9px 10px; border-radius: 10px; background: var(--w-bg);
    font-size: 12.5px; line-height: 1.5; color: var(--w-muted);
}
.chan-guide-check i { flex: none; margin-top: 2px; color: var(--w-primary); }
.chan-warn { color: var(--w-danger); }

/* the shop's own reply, typed in Facebook's or LINE's inbox rather than here. It belongs to neither
   side: not the customer, and not anybody in this room. */
.msg-avatar.is-page { background: var(--w-primary-soft); color: var(--w-primary); display: inline-flex; align-items: center; justify-content: center; }
.msg-row .msg-sender { font-size: 11.5px; color: var(--w-primary); font-weight: 600; }

/* ---- which kind of conversation is being looked at: colleagues, a page, an official account ----
   A tab strip rather than a chip inside the filter fold: an inbox is a shift somebody works, and
   they should not have to unfold anything to get to it. */
.chat-tabs {
    display: flex; gap: 6px; overflow-x: auto; padding: 2px 2px 10px;
    -webkit-overflow-scrolling: touch; scrollbar-width: none;
}
.chat-tabs::-webkit-scrollbar { display: none; }
.chat-tab {
    flex: none; display: inline-flex; align-items: center; gap: 5px;
    padding: 7px 12px; border: 1px solid var(--w-border); border-radius: 999px;
    background: var(--w-surface); color: var(--w-muted);
    font-size: 13px; font-weight: 600; white-space: nowrap; cursor: pointer;
}
.chat-tab i { font-size: 14px; }
.chat-tab.active { background: var(--w-primary); border-color: var(--w-primary); color: #fff; }
.chat-tab-n {
    min-width: 18px; padding: 0 5px; border-radius: 9px;
    background: #e23b3b; color: #fff;
    font-size: 11px; font-weight: 700; line-height: 18px; text-align: center;
}
.chat-tab-n[hidden] { display: none; }
.chat-tab.active .chat-tab-n { background: rgba(255, 255, 255, .28); color: #fff; }

/* The second line of filters, under the tabs. Deliberately lighter than the row above it: the tab
   is the decision, this is which one within it, and two rows of equal weight would read as two
   competing questions. */
.chat-subs {
    display: flex; gap: 6px; overflow-x: auto; padding: 0 2px 10px; margin-top: -4px;
    -webkit-overflow-scrolling: touch; scrollbar-width: none;
}
.chat-subs::-webkit-scrollbar { display: none; }
/* desktop: a mouse cannot scroll a strip sideways when its scrollbar is hidden, so tabs past the right edge
   (Facebook, LINE OA, Instagram, single accounts) were unreachable — on wide screens both strips wrap instead */
@media (min-width: 768px) {
    .chat-tabs, .chat-subs, .chat-sub-set { flex-wrap: wrap; overflow-x: visible; }
}
.chat-sub-set { display: flex; gap: 6px; }
.chat-sub {
    flex: none; display: inline-flex; align-items: center; gap: 5px;
    padding: 5px 11px; border: 1px solid transparent; border-radius: 999px;
    background: var(--w-bg); color: var(--w-muted);
    font-size: 12px; font-weight: 600; white-space: nowrap; cursor: pointer;
    max-width: 180px;
}
.chat-sub > span:not(.chat-sub-n), .chat-sub { overflow: hidden; text-overflow: ellipsis; }
.chat-sub i { font-size: 13px; }
.chat-sub-mark { font-size: 13px; }
.chat-sub.active {
    background: var(--w-primary-soft); border-color: var(--w-primary-soft-border); color: var(--w-primary-dark);
}
.chat-sub-n {
    min-width: 17px; padding: 0 5px; border-radius: 9px;
    background: #e23b3b; color: #fff;
    font-size: 10.5px; font-weight: 700; line-height: 17px; text-align: center;
}
.chat-sub-n[hidden] { display: none; }

/* ---- desktop: the list's controls in a column beside the list (owner 2026-10-12) ----
   chat/index.php wraps the controls (order, mark all, search, new chat), the tabs and a tab's pages in .chat-rail and
   the list in .chat-main. Below 992px neither wrapper is a box, so a phone and a tablet keep the top bar and the
   sideways strips exactly as before. From 992px the rail is a narrow sticky column: the tabs stand one per line with
   the count at the end, and the list keeps the rest of the width. */
.chat-rail, .chat-main { display: contents; }
@media (min-width: 992px) {
    .wrap.chat-index {
        --chat-rail-w: 196px;
        max-width: 1180px; display: grid; grid-template-columns: var(--chat-rail-w) minmax(0, 1fr);
        column-gap: 16px; align-items: start;
    }
    .chat-rail {
        display: block; position: sticky; top: 14px; min-width: 0;
        max-height: calc(100vh - var(--app-tabbar-h) - 28px); overflow-y: auto; overflow-x: hidden;
        scrollbar-width: thin; padding-bottom: 6px;
    }
    .sa-impersonate-bar ~ .wrap .chat-rail { top: 52px; max-height: calc(100vh - var(--app-tabbar-h) - 66px); }
    .chat-main { display: block; min-width: 0; }
    .chat-main > .chat-list { margin-top: 0; }

    /* the controls card: the order and mark-all each on a line, then the search, then its buttons under it */
    .chat-rail .chat-filter { padding: 10px; margin-bottom: 10px; }
    .chat-rail .chat-list-tools { flex-direction: column; align-items: stretch; gap: 6px; }
    .chat-rail .chat-sort-select { max-width: none; }
    .chat-rail .chat-markall-btn { justify-content: center; }
    .chat-rail .chat-markall-panel { padding: 10px; }
    .chat-rail .chat-markall-actions .btn { flex: 1 1 100%; }
    .chat-rail .chat-filter-head { flex-wrap: wrap; gap: 6px; }
    .chat-rail .chat-filter-head .chat-search { flex: 1 1 100%; }
    .chat-rail .chat-filter-head .chat-new-btn { margin-left: auto; }

    /* the tabs: one per line, the mark and the name on the left, the count on the right */
    .chat-rail .chat-tabs { flex-direction: column; flex-wrap: nowrap; gap: 4px; padding: 0 0 10px; }
    .chat-rail .chat-tab {
        width: 100%; justify-content: flex-start; gap: 8px; padding: 9px 12px; border-radius: 10px;
        font-size: 14px; white-space: normal; text-align: left; line-height: 1.25;
    }
    .chat-rail .chat-tab-n { margin-left: auto; flex: none; }
    /* a tab's pages, under the tabs and indented, one per line; a long page name ends in an ellipsis */
    .chat-rail .chat-subs { margin: 0; padding: 0 0 10px 12px; flex-direction: column; flex-wrap: nowrap; }
    .chat-rail .chat-sub-set { flex-direction: column; flex-wrap: nowrap; gap: 3px; }
    .chat-rail .chat-sub { max-width: none; width: 100%; justify-content: flex-start; padding: 6px 10px; border-radius: 8px; font-size: 13px; }
    .chat-rail .chat-sub-n { margin-left: auto; flex: none; }
}
@media (min-width: 1200px) {
    .wrap.chat-index { --chat-rail-w: 216px; column-gap: 20px; }
}

/* ---- a customer's own picture, with the platform's mark on it ------------------------------
   A customer and a colleague must never be mistaken for one another at a glance, and a photo alone
   would do exactly that. The mark sits on the picture, so it is there whether or not the picture
   loaded — the platforms hand out signed addresses that expire. */
.chat-avatar.is-ext, .msg-avatar.is-ext { position: relative; overflow: visible; }
.chat-avatar.is-ext.has-img img, .msg-avatar.is-ext.has-img img {
    width: 100%; height: 100%; border-radius: 50%; object-fit: cover; display: block;
}
/* a customer's name opens them on the platform they wrote from. The name is long — it carries the
   page it came from — so it truncates and the little arrow stays put beside it rather than being
   pushed off the row. */
.chat-header-link { display: flex; align-items: baseline; gap: 5px; min-width: 0; color: var(--w-text); text-decoration: none; }
.chat-header-link > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.chat-header-link:hover, .chat-header-link:focus { color: var(--w-primary); text-decoration: none; }
.chat-header-link i { font-size: 11px; color: var(--w-muted); flex: none; }
.chat-header-link:hover i { color: var(--w-primary); }

/* ---- the marks of the places a customer writes from ----------------------------------------
   Facebook's f is drawn inline, in Facebook's own blue, so it is the mark people recognise and
   nothing is fetched from anybody else's server. LINE keeps the icon font's glyph, white on
   LINE's green. */
.pmark { display: inline-flex; align-items: center; justify-content: center; flex: none; line-height: 1; }
.pmark svg { width: 1em; height: 1em; display: block; }
/* The LINE mark carries its own green tile now, the way the Facebook one carries its own blue
   disc, so the wrapper must not paint a circle behind it — that used to be how the icon font got
   its colour, and left on it would show as a green ring around the tile's rounded corners. */
.pmark-line { width: 1em; height: 1em; font-size: inherit; }
/* standing in for a missing photo, it fills the circle */
.pmark-fill { width: 100%; height: 100%; font-size: 1.15rem; }
.pmark-fill.pmark-line { border-radius: 50%; }
.chan-badge.pmark { position: absolute; right: -2px; bottom: -2px; font-size: 16px; box-shadow: 0 0 0 2px var(--w-surface); border-radius: 50%; background: var(--w-surface); }
/* square-ish, because the badge is a clip and LINE's mark is a rounded tile rather than a disc */
.chan-badge.pmark-line { border-radius: 5px; }
.msg-avatar .chan-badge.pmark { font-size: 13px; }
.chat-tab .pmark, .chat-ext-tag .pmark { font-size: 14px; }
.btn-wms .pmark { font-size: 15px; }
.m-panel-ico.is-plain { background: transparent; font-size: 26px; }
/* a group with a picture of its own */
.chat-avatar.is-group.has-img, .msg-avatar.is-group.has-img { background: none; padding: 0; }
.chat-avatar.is-group.has-img img, .msg-avatar.is-group.has-img img {
    width: 100%; height: 100%; border-radius: 50%; object-fit: cover; display: block;
}
.avatar-pick .chat-avatar.avatar-preview { width: 76px; height: 76px; font-size: 28px; }

/* ---- what can be done to a member, beside their name ---------------------------------------
   Two switches, not two sentences. Neither acts on the spot: they arm, the form saves, which is
   why they show a state. Armed is ringed so a row of them can be read before pressing save. */
.member-row { display: flex; align-items: center; gap: 8px; }
.member-row .member-row-text { flex: 1; min-width: 0; }
.mem-btn {
    flex: none; display: inline-flex; align-items: center; gap: 5px;
    height: 32px; padding: 0 10px; border-radius: 16px; cursor: pointer;
    border: 1px solid var(--w-border); background: var(--w-surface); color: var(--w-muted);
    font-size: 12.5px; font-weight: 700; line-height: 1;
}
.mem-btn i { font-size: 13px; }
.mem-admin.is-on { background: #e7f6ec; border-color: #9bd8b0; color: #15803d; }
.mem-remove { width: 32px; padding: 0; justify-content: center; color: var(--w-danger); border-color: #f3c9c4; }
.mem-remove:hover { background: #fdecea; }
/* armed, and not yet saved */
.mem-btn.is-armed { box-shadow: 0 0 0 2px var(--w-primary-soft); border-color: var(--w-primary); }
.mem-remove.is-armed { background: var(--w-danger); border-color: var(--w-danger); color: #fff; box-shadow: none; }
.member-row.is-going .member-row-text { opacity: .45; text-decoration: line-through; }
@media (max-width: 400px) { .mem-admin span { display: none; } .mem-admin { width: 32px; padding: 0; justify-content: center; } }
.chan-add-page { display: flex; gap: 8px; align-items: center; margin-bottom: 6px; }
.chan-add-page .form-control { flex: 1; min-width: 0; margin: 0; }
.chan-add-page .btn-wms { flex: none; white-space: nowrap; }
/* a colleague's reply in a shared inbox: our side of the screen, but named */
.msg-row.me .msg-sender-ours { text-align: right; color: var(--w-muted); }

/* ---- a customer, and which of the company's pages they wrote to ------------------------------
   Two faces rather than a name with a bracket after it: at a glance the row says who is asking and
   where they are asking, and the page name drops to its own line where it reads as context rather
   than as part of the person's name. */
.chat-faces { position: relative; flex: none; display: inline-flex; }
.chat-page-face {
    position: absolute; right: -7px; bottom: -3px;
    width: 22px; height: 22px; border-radius: 50%; object-fit: cover;
    background: var(--w-surface); box-shadow: 0 0 0 2px var(--w-surface);
}
.chat-faces + .chat-row-body { margin-left: 8px; }
.chat-row-page {
    display: block; font-size: 11.5px; line-height: 1.3; color: var(--w-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* LINE's own emoji and stickers. The emoji sits in a sentence and is sized against the text around
   it, so it grows and shrinks with the message; the sticker is the whole message and is given room
   to be looked at, capped so a tall one cannot push the rest of the room off the screen. */
.chat-line-emoji { height: 1.5em; width: auto; vertical-align: -.3em; display: inline-block; }
.chat-line-sticker { max-width: 160px; max-height: 160px; width: auto; height: auto; display: block; margin: 2px 0; }

/* The panel of LINE stickers and emoji. It sits above the composer rather than over the room, so a
   person can still see what they are answering while they pick — and it is capped in height so a
   pack of forty does not swallow the screen. */
.line-art { border-top: 1px solid var(--w-border); background: var(--w-surface); }
.line-art-tabs { display: flex; align-items: center; gap: 4px; padding: 6px 8px; border-bottom: 1px solid var(--w-border); }
.line-art-tab {
    border: 0; background: none; color: var(--w-muted); font: inherit; font-size: 13px; font-weight: 600;
    padding: 5px 12px; border-radius: 999px; cursor: pointer;
}
.line-art-tab.is-on { background: var(--w-primary-soft); color: var(--w-primary-dark); }
.line-art-x { margin-left: auto; border: 0; background: none; color: var(--w-muted); padding: 5px 8px; cursor: pointer; }
.line-art-sets {
    display: flex; gap: 4px; padding: 6px 8px; overflow-x: auto; scrollbar-width: thin;
    border-bottom: 1px solid var(--w-border);
}
.line-art-set {
    flex: none; border: 1px solid var(--w-border); background: none; color: var(--w-muted);
    font: inherit; font-size: 11.5px; padding: 4px 10px; border-radius: 999px; white-space: nowrap; cursor: pointer;
}
.line-art-set.is-on { border-color: var(--w-primary-soft-border); background: var(--w-primary-soft); color: var(--w-primary-dark); }
.line-art-grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(64px, 1fr)); gap: 4px;
    padding: 8px; max-height: 210px; overflow-y: auto;
}
.line-art-item { border: 0; background: none; padding: 4px; border-radius: 10px; cursor: pointer; line-height: 0; }
.line-art-item:hover { background: var(--w-primary-soft); }
.line-art-item img { width: 100%; height: auto; aspect-ratio: 1; object-fit: contain; }
.composer-attach.is-on { color: var(--w-primary); }

/* the platform's mark in front of the page name — a touch larger than the text it sits with, so it
   reads as a mark rather than as a letter, and nudged down onto the same baseline */
.chat-row-pmark { font-size: 13px; vertical-align: -2px; margin-right: 4px; }
.chat-row-pmark.pmark-line { border-radius: 4px; }
.login-remember { margin: 4px 0 14px; }

/* the foot of the chat list while the next page is on its way. It is asked for fifteen rows early,
   so this is usually seen for a blink or not at all — which is the point. */
.chat-more { display: flex; justify-content: center; padding: 14px 0 6px; }
.chat-more[hidden] { display: none; }
.chat-more .spinner-border { width: 20px; height: 20px; border-width: 2px; color: var(--w-muted); }

/* Lazada's tile carries its own two colours, the same way Facebook's and LINE's do */
.pmark-laz { width: 1em; height: 1em; font-size: inherit; }
.chan-badge.pmark-laz { border-radius: 5px; }
.chat-row-pmark.pmark-laz { border-radius: 4px; }
.pmark-ig { width: 1em; height: 1em; font-size: inherit; }
.chan-badge.pmark-ig { border-radius: 5px; }
.chat-row-pmark.pmark-ig { border-radius: 4px; }

/* Saved replies — one person's own phrases, dropped into the box rather than sent. Sits above the
   composer like the LINE panel does, so what is being answered stays in view while one is picked. */
.quick-panel { border-top: 1px solid var(--w-border); background: var(--w-surface); padding: 8px; }
.quick-panel[hidden] { display: none; }
.quick-head { display: flex; align-items: center; gap: 6px; font-size: 13px; padding-bottom: 6px; }
.quick-head b { color: var(--w-text); }
.quick-x { margin-left: auto; border: 0; background: none; color: var(--w-muted); padding: 4px 6px; cursor: pointer; }
.quick-list { max-height: 210px; overflow-y: auto; display: flex; flex-direction: column; gap: 5px; }
.quick-list[hidden] { display: none; }
.quick-item { display: flex; align-items: stretch; gap: 4px; }
.quick-use {
    flex: 1 1 auto; min-width: 0; text-align: left; cursor: pointer;
    border: 1px solid var(--w-border); border-radius: var(--w-radius-sm);
    background: var(--w-bg); color: var(--w-text); font: inherit; font-size: 13px;
    padding: 7px 10px; line-height: 1.4;
    /* three lines of a long one, so forty of them still fit a panel */
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.quick-use:hover { border-color: var(--w-primary-soft-border); background: var(--w-primary-soft); }
.quick-del {
    flex: none; border: 1px solid var(--w-border); border-radius: var(--w-radius-sm);
    background: none; color: var(--w-danger); padding: 0 9px; cursor: pointer;
}
.quick-add textarea { resize: vertical; font-size: 14px; line-height: 1.5; }
/* The form grows a box per message and each box is ten lines tall, so with a second or third message
   it is taller than the space above the composer. It scrolls inside itself, like the list does, rather
   than pushing its own Save button off the screen where nothing can reach it. */
.quick-add { max-height: min(60vh, 520px); max-height: min(60dvh, 520px); overflow-y: auto; overscroll-behavior: contain; }
/* The limit above is a share of the screen, but the header, the composer row and the panel's own head
   also take height, so on a short screen the form's bottom could still sit below the edge. While the
   form is open the composer may shrink into the space the messages leave and scroll itself, and the
   Save row stays pinned to the bottom of the form, so Save is always on screen. */
/* …except while the @mention list is open: it hangs above the composer (bottom: 100%), and a scrolling
   composer would clip it out of sight. */
.chat-composer:has(.quick-add:not([hidden])):not(:has(.mention-menu:not([hidden]))) { flex: 0 1 auto; min-height: 0; overflow-y: auto; }
.quick-add > .wms-actions { position: sticky; bottom: 0; background: var(--w-surface); padding-top: 6px; margin-bottom: 0; }

/* What the plus folds away. Labelled rows rather than a row of icons: this is the one place in the
   composer with room to say what each thing does, and an icon alone is a guess. */
.composer-tray {
    border-top: 1px solid var(--w-border); background: var(--w-surface);
    padding: 6px; display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: 6px;
}
.composer-tray[hidden] { display: none; }
.tray-item {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px;
    border: 1px solid var(--w-border); border-radius: var(--w-radius-sm);
    background: var(--w-bg); color: var(--w-text);
    font: inherit; font-size: 12px; padding: 12px 6px; cursor: pointer; text-align: center;
}
.tray-item i { font-size: 20px; color: var(--w-primary); }
.tray-item span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
.tray-item:hover { border-color: var(--w-primary-soft-border); background: var(--w-primary-soft); }

/* A saved reply is a heading with its wording under it — the heading is what the eye lands on. */
.quick-use { display: block; }
.quick-use b { display: block; font-size: 13px; line-height: 1.35; color: var(--w-text); }
.quick-use small {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
    font-size: 12px; line-height: 1.4; color: var(--w-muted); margin-top: 1px;
}
.quick-add .field-label { margin-top: 8px; }
.quick-add .field-label:first-child { margin-top: 0; }
.quick-edit {
    flex: none; border: 1px solid var(--w-border); border-radius: var(--w-radius-sm);
    background: none; color: var(--w-muted); padding: 0 9px; cursor: pointer;
}
.quick-edit:hover { color: var(--w-primary); border-color: var(--w-primary-soft-border); }

/* Putting the list in order. A mode rather than two more buttons on every row: arranging happens
   now and then, choosing a reply happens all day, and the wording is what the row is for. */
.quick-sort { border: 0; background: none; color: var(--w-muted); padding: 4px 6px; cursor: pointer; }
.quick-sort.is-on { color: var(--w-primary); }

/* A saved reply that is several messages. In the form, each message has its own box, numbered in
   the order it will arrive; in the list, the row says how many there are, since tapping it opens a
   preview rather than filling the composer. */
.quick-parts { display: flex; flex-direction: column; gap: 8px; }
.quick-part-head { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--w-muted); margin-bottom: 3px; }
.quick-part-head[hidden] { display: none; }
.quick-part-x { margin-left: auto; border: 0; background: none; color: var(--w-danger); padding: 2px 6px; cursor: pointer; }
.quick-part textarea { resize: vertical; font-size: 14px; line-height: 1.5; }
.quick-part-add { margin-top: 8px; }
.quick-part-add[hidden] { display: none; }
/* a picture part (a product photo beside its description): a thumbnail in the form and in the preview; moved with the
   arrows beside the number like any part; in a room that takes no pictures it is shown struck through (skipped) */
.quick-part-adds { display: flex; flex-wrap: wrap; gap: 6px; }
.quick-part-mv { border: 0; background: none; color: var(--w-primary); padding: 2px 4px; cursor: pointer; }
.quick-part-mv:disabled { opacity: .35; cursor: default; }
.quick-thumb { display: block; max-width: 160px; max-height: 120px; border-radius: var(--w-radius-sm); border: 1px solid var(--w-border); object-fit: contain; }
.quick-preview-list li.is-skipped .quick-thumb { opacity: .35; }
.quick-count {
    display: inline-block; margin-top: 3px; font-size: 11px; line-height: 1.4;
    padding: 0 7px; border-radius: 999px; background: var(--w-primary-soft); color: var(--w-primary);
}
.quick-preview[hidden] { display: none; }
.quick-preview-list {
    max-height: 210px; overflow-y: auto; margin: 0; padding-left: 22px;
    display: flex; flex-direction: column; gap: 6px; font-size: 13px;
}
.quick-preview-list li {
    white-space: pre-wrap; overflow-wrap: anywhere; padding: 6px 9px;
    border: 1px solid var(--w-border); border-radius: var(--w-radius-sm); background: var(--w-bg); color: var(--w-text);
}
.quick-move {
    flex: none; border: 1px solid var(--w-border); border-radius: var(--w-radius-sm);
    background: none; color: var(--w-primary); padding: 0 9px; cursor: pointer;
}
.quick-move:disabled { color: var(--w-border-strong); cursor: default; }

/* Where a saved reply is kept (owner 2026-10-05): several places may be ticked. "Every chat" first, then internal,
   then each kind with its channels indented under it. A short line on each saved reply says where it is kept. */
.quick-scope-hint { margin: 0 0 4px; font-size: 12px; }
.quick-scope { display: flex; flex-direction: column; gap: 2px; max-height: 220px; overflow-y: auto;
    border: 1px solid var(--w-border); border-radius: var(--w-radius-sm); padding: 6px 8px; }
.quick-sc { display: flex; align-items: center; gap: 8px; margin: 0; padding: 5px 2px; font-size: 14px; cursor: pointer; min-height: 32px; }
.quick-sc input { width: 18px; height: 18px; flex: none; margin: 0; }
.quick-sc.is-all, .quick-sc.is-kind { font-weight: 600; }
.quick-sc.is-kind { margin-top: 4px; border-top: 1px solid var(--w-border); padding-top: 8px; }
.quick-sc.is-chan { padding-left: 24px; }
.quick-sc input:disabled + span { color: var(--w-muted); }
.quick-where { display: block; margin-top: 2px; font-size: 11px; line-height: 1.35; color: var(--w-primary-dark);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* the filter belongs to the side panel only: a phone keeps its panel as it was */
.quick-search { display: none; }
.chat-side { display: none; }

/* ≥ 992px: the saved replies open beside the room instead of over the composer (chat/view.php #quickSide). The room
   keeps working in the space left; the panel scrolls inside itself and shows each reply whole. */
@media (min-width: 992px) {
    .chat-page { position: relative; --quick-side-w: 380px; }
    .chat-page.has-side { padding-right: var(--quick-side-w); }
    .chat-side:not([hidden]) {
        display: flex; flex-direction: column; position: absolute; top: 0; right: 0; bottom: 0; width: var(--quick-side-w);
        border-left: 1px solid var(--w-border); background: var(--w-surface); z-index: 4;
    }
    .chat-side .quick-panel { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; border-top: 0; padding: 12px; }
    .chat-side .quick-panel[hidden] { display: none; }
    .chat-side .quick-head { font-size: 15px; }
    .chat-side .quick-search { display: block; flex: none; margin-bottom: 8px; height: 40px; }
    .chat-side .quick-list { flex: 1 1 auto; min-height: 0; max-height: none; gap: 10px; }
    .chat-side .quick-list[hidden] { display: none; }
    .chat-side .quick-add { flex: 1 1 auto; min-height: 0; max-height: none; }
    .chat-side .quick-add[hidden], .chat-side .quick-preview[hidden] { display: none; }
    /* the preview of a long reply takes what is left of the panel's height and scrolls its messages inside itself;
       its head and its Cancel / Send all row stay on screen (the page itself never scrolls: body.chat-body) */
    .chat-side .quick-preview:not([hidden]) { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
    .chat-side .quick-preview > p { flex: none; }
    .chat-side .quick-preview-list { flex: 1 1 auto; min-height: 0; max-height: none; overflow-y: auto; overscroll-behavior: contain; }
    .chat-side .quick-preview > .wms-actions { flex: none; margin-bottom: 0; padding-top: 6px; background: var(--w-surface); }
    .chat-side #quickAddWrap { flex: none; }
    .chat-side .quick-item { flex-wrap: wrap; border: 1px solid var(--w-border); border-radius: var(--w-radius-sm); padding: 6px; background: var(--w-bg); }
    .chat-side .quick-use { -webkit-line-clamp: unset; overflow: visible; border: 0; background: none; padding: 4px; }
    .chat-side .quick-use small { display: none; }
    .chat-side .quick-where { white-space: normal; }
    .chat-side .quick-detail { flex: 1 1 100%; min-width: 0; }
    .chat-side .quick-detail-parts { margin: 0 0 6px; padding-left: 20px; display: flex; flex-direction: column; gap: 6px;
        font-size: 13px; line-height: 1.45; white-space: pre-line; overflow-wrap: anywhere; }
    .chat-side .quick-detail-parts li.is-skipped .quick-thumb { opacity: .35; }
    .chat-side .quick-acts { display: flex; gap: 6px; justify-content: flex-end; flex-wrap: wrap; }
    .chat-side .quick-acts .btn-wms { margin: 0; padding: 4px 10px; min-height: 34px; font-size: 13px; }
    .chat-side .quick-scope { max-height: 260px; }
}
@media (min-width: 1400px) { .chat-page { --quick-side-w: 440px; } }
.quick-use:disabled { cursor: default; opacity: .85; }
/* dragging one into place, where there is a mouse to do it with */
.quick-item[draggable="true"] { cursor: grab; }
.quick-item.is-dragging { opacity: .4; cursor: grabbing; }
.quick-list.is-sorting { padding: 2px; }

/* ============ WMS locations page (wms/locations.php) ============ */
.wms-loc-toolbar { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.wms-loc-toolbar [data-wms-scan-form] { flex: 1 1 320px; }
.wms-loc-toolbar [data-wms-scan-form] { flex-wrap: nowrap; }
.wms-loc-toolbar [data-wms-scan-form] .form-control { max-width: none !important; flex: 1 1 160px; width: auto; min-width: 0; }
.wms-loc-form { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 10px 14px; align-items: end; }
.wms-loc-field { display: flex; flex-direction: column; gap: 4px; margin: 0; min-width: 0; }
.wms-loc-field > span { font-size: 12px; font-weight: 600; color: var(--w-muted); }
.wms-loc-field .form-control { width: 100%; min-width: 0; }
.wms-loc-submit { display: flex; align-items: end; }
.wms-loc-submit .btn-wms { width: 100%; justify-content: center; }
.wms-loc-more { grid-column: 1 / -1; border-top: 1px dashed var(--w-border); padding-top: 8px; }
.wms-loc-more > summary { cursor: pointer; font-size: 13px; font-weight: 600; color: var(--w-muted); }
.wms-loc-more-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 10px 14px; align-items: end; margin-top: 10px; }
.wms-loc-check { display: flex; align-items: center; gap: 6px; margin: 0; font-size: 13px; min-height: 38px; }
.wms-loc-facts { display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; list-style: none; }
.wms-loc-table-wrap { overflow-x: auto; margin: -4px; }
.wms-loc-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.wms-loc-table th { text-align: left; font-size: 12px; font-weight: 600; color: var(--w-muted); padding: 8px 10px; border-bottom: 1px solid var(--w-border); white-space: nowrap; }
.wms-loc-table td { padding: 7px 10px; border-bottom: 1px solid var(--w-border); vertical-align: middle; }
.wms-loc-table tr:last-child td { border-bottom: 0; }
.wms-loc-table tbody tr:hover { background: var(--w-bg); }
.wms-loc-table tr.is-area td { background: var(--w-primary-soft); }
.wms-loc-table tr.is-area .wms-loc-code a { font-weight: 700; }
.wms-loc-table tr.is-off td { color: var(--w-muted); }
.wms-loc-code { white-space: nowrap; }
.wms-loc-code i { color: var(--w-primary); margin-right: 6px; }
.wms-loc-code a { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-weight: 600; color: var(--w-text); }
.wms-loc-pill { display: inline-block; font-size: 11.5px; padding: 1px 8px; border-radius: 999px; background: var(--w-primary-soft); color: var(--w-primary); white-space: nowrap; }
.wms-loc-pill.is-off { background: var(--w-bg); color: var(--w-muted); border: 1px solid var(--w-border); }
.wms-loc-pill.is-frozen { background: #e6f1fb; color: #1d64d8; margin-left: 4px; }
