/* ==========================================================================


/* ==========================================================================
   Soft-TechBd DoctorDesk Ã¢â‚¬â€ responsive layer
   --------------------------------------------------------------------------
   This file is mobile-first in its measurements: every number in it was
   chosen for a thumb first and the desktop rule is the one that widens.

   Breakpoints
     <= 1280px  wide two-column layouts loosen
      <=  767px  the rail becomes a drawer, the tab bar appears (phones only)
     <=  820px  tables restack, forms collapse, spacing tightens
     <=  560px  compact phones Ã¢â‚¬â€ smallest spacing, shortest labels
     <=  380px  narrow phones (320 / 360 / 375)

   Two rules that are never broken here:
     1. Nothing hides horizontal overflow. If it does not fit, the layout is
        wrong and gets rebuilt, not clipped.
     2. No fixed height on anything whose content can wrap or grow.

   Phone widths explicitly checked: 320 / 360 / 375 / 390 / 412 / 430 px.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Touch ergonomics: no double-tap zoom, and 16px controls so focusing a
   field never zooms the page on iOS.
   -------------------------------------------------------------------------- */

button, a, .btn, .tab, .nav-link, .segmented button, .tabbar__item {
    touch-action: manipulation;
}

@media (pointer: coarse), (max-width: 820px) {
    .input, .select, .textarea,
    .filters .input, .filters .select {
        font-size: 16px;
        padding: 10px 12px;
    }
    .select { padding-right: 34px; }
    .textarea { line-height: 1.5; }
    .btn { min-height: 42px; padding: 9px 14px; }
    .btn--sm { min-height: 36px; }
    .btn--icon { width: 42px; height: 42px; }
    .btn--icon.btn--sm { width: 36px; height: 36px; }
    .btn-quiet { width: 36px; height: 36px; }
    .topbar__toggle { width: 34px; height: 34px; }
    .check { min-height: 42px; align-items: center; }
    .check input[type="checkbox"], .check input[type="radio"] { margin-top: 0; }
    .switch { min-height: 42px; }
    .page-link { min-width: 38px; height: 38px; }
}

/* ==========================================================================
   <= 1280px
   ========================================================================== */

@media (max-width: 1280px) {
    .grid--sidebar { grid-template-columns: minmax(0, 1fr) 300px; }
    .grid--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ==========================================================================
   <= 767px  Ã¢â‚¬â€ drawer navigation + thumb tab bar (phones only; tablets keep
   the desktop rail)
   ========================================================================== */

@media (max-width: 767px) {
    :root {
        --gutter: 16px;
        --tabbar-h: 62px;
    }

    /* --- The rail becomes a drawer ----------------------------------
       Narrower than before (280Ã¢â‚¬â€œ300px): readable labels, compact
       padding, 44px+ touch rows. Desktop rail untouched. */
    .sidebar {
        position: fixed;
        top: 0;
        left: 0;
        bottom: 0;
        height: 100dvh;
        width: min(82vw, 230px);
        max-width: 230px;
        flex-basis: auto;
        transform: translateX(-100%);
        transition: transform .24s var(--ease);
        box-shadow: none;
        z-index: 70;
        will-change: transform;
    }
    .app-shell.is-nav-open .sidebar { transform: translateX(0); }
    .app-shell.is-nav-open .sidebar-backdrop { display: block; z-index: 65; }

    .sidebar__close { display: grid; }

    /* Below the breakpoint the rail IS the drawer, which already slides away on
       demand - a collapse button next to the drawer close would be a second
       control doing the same job. The saved desktop preference also has no say
       here, so a phone drawer never opens icon-only. */
    .topbar__collapse { display: none; }
    .is-sidebar-collapsed .sidebar {
        width: min(82vw, 230px);
        max-width: 230px;
    }
    .is-sidebar-collapsed .sidebar__titles,
    .is-sidebar-collapsed .sidebar__foot,
    .is-sidebar-collapsed .nav-group__title,
    .is-sidebar-collapsed .nav-link__label {
        display: revert;
    }
    .is-sidebar-collapsed .nav-link {
        justify-content: flex-start;
        padding-left: 12px;
        padding-right: 12px;
    }
    /* The badge shrinks to a dot while collapsed, and font-size 0 hides the
       number with it - so the count reset has to be undone here too, or a phone
       drawer would show bare dots. */
    .is-sidebar-collapsed .nav-link__count {
        position: static;
        width: auto;
        height: auto;
        min-width: 20px;
        padding: 1px 7px;
        margin-left: auto;
        font-size: 11px;
        border-radius: 99px;
    }

    /* The drawer reaches the bottom edge, so it needs the inset itself. */
    .sidebar__nav { padding: 12px 12px calc(18px + env(safe-area-inset-bottom, 0px)); }
    .sidebar__brand { padding-left: 12px; padding-right: 12px; }
    .nav-group__title { padding: 8px 12px 5px; }
    .nav-link {
        font-size: 14px;
        padding: 11px 12px;
        gap: 11px;
        min-height: 44px;
        white-space: nowrap;
    }
    .sidebar__foot { padding-left: 12px; padding-right: 12px; }

    /* The tab bar's "More" item opens the drawer, so a second menu button in
       the topbar would be two controls for one job. The hamburger stays in
       the markup for the landscape case, where the tab bar is hidden. */
    .main { width: 100%; }

    /* --- Room for the tab bar, so the last row is never covered ----- */
    .page {
        padding-bottom: calc(var(--tabbar-h) + 18px + env(safe-area-inset-bottom, 0px));
    }
    .page--flush {
        padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px));
    }

    .tabbar { display: grid; }

    /* The list itself reserves the room both bars need. */
    .pad-wrap { padding-bottom: calc(var(--tabbar-h) + 96px); }

    .toast-host { bottom: calc(var(--tabbar-h) + 14px + env(safe-area-inset-bottom, 0px)); }
    /* The rail is off-canvas here, so the badge returns to the screen edge. */
    .dev-meter {
        left: 8px;
        bottom: calc(var(--tabbar-h) + 8px + env(safe-area-inset-bottom, 0px));
    }

    /* --- The global lookup drops out of the flow --------------------- */
    .topbar__search { flex: 1 1 90px; max-width: none; }

    .topbar__search .ac__list {
        position: fixed;
        top: var(--topbar-h);
        left: var(--gutter);
        right: var(--gutter);
        width: auto;
        max-height: 60vh;
    }

    /* The dashboard's side column drops under the main one. Without this the
       two keep sharing the row and the narrower one squeezes to nothing. */
    .grid--sidebar,
    .grid--2 { grid-template-columns: minmax(0, 1fr); }

    .filters .field,
    .filters .field--wide { flex: 1 1 46%; max-width: none; }
    .filters__actions { margin-left: 0; flex: 1 1 100%; }
}

/* ==========================================================================
   <= 820px  Ã¢â‚¬â€ restacked tables, single-column forms, tighter rhythm
   ========================================================================== */

/* Compact patient rows (patients/index.php) live here but switch on only
   inside the media query below: desktop never sees .pt-list, phones never
   see the patients table. Same data, same links, same permission gates. */
.pt-list { display: none; }

/* Same treatment for the appointment board: desktop never sees .appt-list,
   phones never see the appointments table (appointments/index.php). */
.appt-list { display: none; }

@media (max-width: 820px) {
    :root { --gutter: 14px; }

    .page-head { margin-bottom: 14px; gap: 10px; align-items: flex-start; }
    .page-head__text { flex-basis: 100%; }
    .page-head__actions { width: 100%; }

    .card__body { padding: 14px; }
    .card__head { padding: 12px 14px; flex-wrap: wrap; }
    .card__tools { margin-left: 0; width: 100%; }
    .card__foot { padding: 12px 14px; }

    /* --- KPI tiles: value-led, two per row, no wasted height -------- */
    .stat-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 10px;
    }
    .stat { padding: 12px; gap: 0; }
    .stat__icon { display: none; }
    .stat__value { font-size: var(--fs-xl); }
    .stat__label { font-size: 10.5px; letter-spacing: .04em; margin-bottom: 4px; }

    .form-grid, .form-grid--2, .form-grid--3, .form-grid--4 { grid-template-columns: minmax(0, 1fr); }
    .grid--3 { grid-template-columns: minmax(0, 1fr); }
    .grid { gap: 14px; }

    .filters { padding: 12px 14px; }
    /* Two short filters share a row Ã¢â‚¬â€ one per row doubles the height for
       nothing. Full width is kept for .field--wide (search, date ranges). */
    .filters .field { flex: 1 1 46%; max-width: none; }
    .filters .field--wide { flex: 1 1 100%; max-width: none; }

    /* ---- Tables become stacked cards -------------------------------- */
    .table--cards { font-size: 13.5px; }
    .table--cards thead { display: none; }
    .table--cards tbody,
    .table--cards tr,
    .table--cards td { display: block; width: 100%; }
    /* Rows become full-bleed sections separated by a rule, not cards inside a
       card: a bordered box inside an already-bordered card reads as two
       frames around one patient, and it cost 20px of height per row. */
    .table--cards tr {
        border: 0;
        border-bottom: 1px solid var(--line);
        border-radius: 0;
        margin: 0;
        padding: 2px 14px 8px;
        background: var(--paper);
    }
    .table--cards tr:last-child { border-bottom: 0; }
    .table--cards tbody tr:hover { background: var(--paper); }
    /* Status-coloured rows keep a full-width tint so they still stand out. */
    .table--cards tr.is-current { background: var(--brand-soft); }
    .table--cards tr.is-highlight { background: var(--brand-softer); }
    .table--cards td {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: 12px;
        padding: 7px 0;
        border-bottom: 1px dashed var(--line-soft);
        text-align: right;
    }
    .table--cards td:last-child { border-bottom: 0; }
    .table--cards td::before {
        content: attr(data-label);
        flex: 0 0 auto;
        font-size: 10.5px;
        font-weight: 700;
        letter-spacing: .04em;
        text-transform: uppercase;
        color: var(--muted);
        text-align: left;
        padding-right: 8px;
    }
    html[lang="bn"] .table--cards td::before { letter-spacing: 0; text-transform: none; }
    .table--cards td:not([data-label])::before { content: none; }
    /* The primary cell reads as a heading, not a label/value pair. */
    .table--cards td.cell-title {
        display: block;
        text-align: left;
        padding: 10px 0 8px;
        border-bottom: 1px solid var(--line-soft);
        font-size: var(--fs-md);
        font-weight: 600;
        color: var(--ink);
    }
    .table--cards td.cell-title::before { content: none; }
    .table--cards td.cell-actions {
        display: flex;
        justify-content: flex-end;
        flex-wrap: wrap;
        gap: 8px;
        padding-top: 10px;
        border-top: 1px solid var(--line-soft);
        border-bottom: 0;
    }
    .table--cards td.cell-actions::before { content: none; }
    .table--cards .token-pill { min-width: 52px; }
    .table--cards .truncate { max-width: 62%; }

    /* ---- Instant Visit compact form (visits/instant.php) ---------------
       Same fields, tighter rhythm: two short fields share every row,
       8px inputs (16px text kept so iOS never zooms), small labels,
       checkboxes without the 42px coarse rule, slim divider + footer. */
    .instant-page .card__body { padding: 12px 14px; }
    .instant-page .card__title { flex-wrap: wrap; row-gap: 4px; }
    .instant-page .form-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 10px; }
    .instant-page .field__label { margin-bottom: 3px; }
    .instant-page .input,
    .instant-page .select { padding: 8px 10px; }
    .instant-page .field__hint { margin-top: 2px; font-size: 11px; line-height: 1.4; }
    .instant-page .pad-readonly-value { font-size: 13px; min-height: 0; }
    .instant-page .check { min-height: 0; font-size: 13px; }
    .instant-page .check + .check { margin-top: 6px; }
    .instant-page .instant-div { margin: 2px 0 0; font-size: 13px; }
    .instant-page .card__foot { padding: 10px 14px; gap: 8px; }
    .instant-page .card__foot .btn--lg { padding: 9px 16px; }

    /* ---- Pre-check board (precheck/index.php, phones only) --------------
       Slim stat tiles (date/vitals hints already live in the filters, so
       the meta lines hide), compact filters, and 3-line patient rows:
       token Ã‚Â· name Ã‚Â· status / code Ã‚Â· age Ã‚Â· gender / vitals-or-waiting
       with edit + send + view icons. Desktop keeps the pc-table. */
    .precheck-stats { gap: 8px; margin-bottom: 10px; }
    .precheck-stats .stat { padding: 8px 12px; }
    .precheck-stats .stat__label { margin-bottom: 2px; }
    .precheck-stats .stat__value { font-size: var(--fs-xl); }
    .precheck-stats .stat__meta { display: none; }
    .precheck-page .table-wrap { display: none; }
    .precheck-page .filters { padding: 10px 14px; gap: 8px; }
    .precheck-page .empty-state { padding: 16px 12px; }
    .pc-list { display: block; padding: 0 14px 6px; }
    .pc-row { padding: 7px 0; border-bottom: 1px solid var(--line-soft); }
    .pc-row:last-child { border-bottom: 0; }
    .pc-row__line1 {
        display: flex;
        align-items: center;
        gap: 7px;
        min-width: 0;
    }
    .pc-row .token-pill { min-width: 0; padding: 2px 7px; font-size: 12px; }
    .pc-row__name {
        flex: 1 1 auto;
        min-width: 0;
        font-weight: 600;
        font-size: 14px;
        line-height: 1.35;
        color: var(--ink);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .pc-row__status { flex: 0 0 auto; }
    .pc-row .badge { font-size: 11px; padding: 1px 8px; }
    .pc-row__line2 {
        margin-top: 1px;
        font-size: 12px;
        line-height: 1.5;
        color: var(--muted);
    }
    .pc-row__line3 {
        display: flex;
        align-items: center;
        gap: 6px;
        margin-top: 1px;
        min-width: 0;
    }
    .pc-row__vitals {
        flex: 1 1 auto;
        min-width: 0;
        font-size: 12px;
        line-height: 1.5;
        color: var(--body);
    }
    .pc-row__tools {
        flex: 0 0 auto;
        display: flex;
        align-items: center;
        gap: 2px;
    }

    /* ---- Pre-check vitals form (precheck/form.php, phones only) ---------
       Same groups (patient / vitals / notes), tighter rhythm: 2-column
       vitals, 4px labels, ~42px inputs, slim footer. */
    .precheck-form .card__body { padding: 12px 14px; }
    .precheck-form .form-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 10px; }
    .precheck-form .field__label { margin-bottom: 4px; }
    .precheck-form .input { padding: 9px 11px; }
    .precheck-form .field__hint { margin-top: 2px; font-size: 11px; }
    .precheck-form .section-title { margin: 4px 0 8px; }
    .precheck-form .textarea { min-height: 64px; }
    .precheck-form .card__foot { padding: 10px 14px; }
    .precheck-form .card__foot .btn--lg { padding: 9px 16px; }
    .precheck-form .alert { margin-bottom: 10px; }

    /* ---- Patient profile (patients/view.php, phones only) --------------
       Summary: tighter body, 20px name, compact action buttons.
       Tabs card: only pv-table lists collapse (overview tables keep
       their columns), pv rows mirror the appointment language. */
    .pf-page { margin-bottom: 10px; }
    .pf-page .card__body { padding: 12px 14px; }
    .pf-page h2 { font-size: 20px; }
    .pf-page .badge { min-height: 30px; }
    .pf-page .btn-row { gap: 6px; margin-top: 8px; }
    .pf-page .btn-row .btn { min-height: 42px; padding: 8px 12px; font-size: 13px; }
    .pf-tabs table.pv-table { display: none; }
    .pf-tabs .table-wrap:has(> table.pv-table) { display: none; }
    .pf-tabs .tabs { padding: 0 14px; }
    .pf-tabs .pv-list { display: block; padding: 0 14px 6px; }
    .pv-row { padding: 6px 0; border-bottom: 1px solid var(--line-soft); }
    .pv-row:last-child { border-bottom: 0; }
    /* Two lines per row: line 1 = date + status + icon actions, line 2 =
       code Ã‚Â· doctor. The diagnosis meta hides on phones (the details page
       has it) and text buttons shrink to icons Ã¢â‚¬â€ labels stay in the DOM for
       screen readers. */
    .pv-row { display: grid; grid-template-columns: minmax(0, 1fr) auto auto auto auto; column-gap: 7px; row-gap: 1px; align-items: center; }
    .pv-row__line1, .pv-row__actions { display: contents; }
    .pv-row__title {
        grid-column: 1; grid-row: 1; min-width: 0;
        font-weight: 700; font-size: 14.5px; line-height: 1.3; color: var(--ink);
        white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .pv-row__status { grid-column: 2; grid-row: 1; }
    .pv-row__actions .btn { grid-row: 1; }
    .pv-row__meta {
        grid-column: 1 / -1; grid-row: 2; min-width: 0;
        margin-top: 0;
        font-size: 12px;
        line-height: 1.4;
        color: var(--muted);
        white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .pv-row__meta + .pv-row__meta { display: none; }
    .pv-row .badge { font-size: 10.5px; padding: 1px 7px; white-space: nowrap; }
    .pv-row__actions .btn {
        min-height: 30px;
        min-width: 30px;
        padding: 0 7px;
        font-size: 0;
        gap: 0;
        justify-content: center;
    }
    .pv-row__actions .btn .icon { width: 15px; height: 15px; }
    /* One button system inside the tabs card: every small action Ã¢â‚¬â€
       View, print, history, receipt Ã¢â‚¬â€ shares a 40px touch height. */
    .pf-tabs .btn--sm { min-height: 40px; }
    .pf-tabs .card__body { padding: 10px 14px 13px; }
    .pf-tabs .section-title { font-size: var(--fs-base); margin: 0 0 8px; }
    .pf-tabs .detail-grid { gap: 12px 20px; }
    .pf-tabs .detail__key { font-size: 11px; }
    .pf-tabs .detail__val { font-size: 14px; }
    .pf-tabs .hr { margin: 12px 0; }
    .pf-tabs .alert { margin-bottom: 10px; }
    .pf-tabs .card__foot { padding: 10px 14px; }
    .pf-tabs .card__foot .flex { gap: 8px 14px; }
    .pf-tabs .empty-state { padding: 16px 12px; }
    .pf-tabs .empty-state__icon { width: 48px; height: 48px; margin-bottom: 10px; }
    /* Timeline stays a timeline, just tighter. Rail indent reduced so
       item content shares the card's left boundary. */
    .pf-tabs .timeline { padding-left: 18px; }
    .pf-tabs .timeline::before { left: 4px; }
    .pf-tabs .timeline__dot { left: -19px; }
    .pf-tabs .timeline__item { padding-bottom: 10px; }
    .pf-tabs .timeline__body { padding: 10px 12px; }
    .pf-tabs .timeline__head { gap: 5px; }
    .pf-tabs .timeline__head .btn { min-height: 40px; }
    .pf-tabs .timeline__row { margin-top: 4px; }
    .pf-tabs .timeline__head .btn--ghost { padding-left: 8px; padding-right: 8px; }

    /* ---- Strict 2-line visit rows (visits/index.php, phones only) ------
       Line 1: date Ã‚Â· code / status. Line 2: name Ã‚Â· age / View. No
       doctor, diagnosis or medicine info on mobile; details page has it. */
    .visit-page .table-wrap { display: none; }
    .visit-page .filters { padding: 10px 14px; gap: 8px; }
    .visit-page .card__head { flex-wrap: nowrap; gap: 8px; padding: 10px 14px; align-items: center; }
    .visit-page .card__title { font-size: 15px; white-space: nowrap; }
    .visit-page .card__tools { width: auto; margin-left: auto; flex: 0 0 auto; gap: 6px; }
    .visit-page .card__tools .badge { font-size: 11px; }
    .vs-list { display: block; padding: 0 14px 6px; }
    .vs-row { padding: 8px 0; border-bottom: 1px solid var(--line-soft); }
    .vs-row:last-child { border-bottom: 0; }
    .vs-line1, .vs-line2 {
        display: flex;
        align-items: center;
        gap: 8px;
        min-width: 0;
    }
    .vs-line2 { margin-top: 3px; }
    .vs-datecode {
        flex: 1 1 auto;
        min-width: 0;
        font-size: 12.5px;
        color: var(--muted);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .vs-datecode strong { color: var(--ink); font-weight: 600; }
    .vs-status { flex: 0 0 auto; }
    .vs-row .badge { font-size: 11px; padding: 1px 8px; }
    .vs-name {
        flex: 1 1 auto;
        min-width: 0;
        font-weight: 600;
        font-size: 14.5px;
        line-height: 1.35;
        color: var(--ink);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .vs-name span { font-weight: 400; font-size: 12.5px; color: var(--muted); }
    .vs-line2 .btn { flex: 0 0 auto; }

    /* ---- Visit details (visits/view.php, phones only) ------------------
       Same language: 2-column identity facts, tight summary sections,
       inline vitals, wrapping chips, compact advice + stacked meds. */
    .visit-page .card__body { padding: 12px 14px; }
    .visit-page .card__foot { padding: 10px 14px; }
    .visit-page .card__foot .btn { min-height: 40px; font-size: 13px; }
    .visit-page .detail-grid { grid-template-columns: 1fr 1fr; gap: 8px 14px; }
    .visit-page .detail__key { font-size: 11px; }
    .visit-page .detail__val { font-size: 14px; }
    .visit-page .section-title { margin: 0 0 6px; }
    .visit-page .mb-2 { margin-bottom: 8px; }
    .visit-page .mb-3 { margin-bottom: 10px; }
    .visit-page .mt-3 { margin-top: 12px; }
    .visit-page .alert { margin-bottom: 10px; }
    .visit-page .flex.gap-4 { gap: 8px 14px; }
    .visit-page .pad-readonly-value { font-size: 13.5px; }
    .visit-page .pad-readonly-list { font-size: 13px; line-height: 1.5; }
    .visit-page .pad-readonly-list li + li { margin-top: 5px; }
    .visit-page .chip { font-size: 12.5px; padding: 2px 6px 2px 9px; }
    .visit-page .table--cards td { padding: 6px 0; }

    /* ---- Visit medicines as single flowing rows (phones only) ----------
       The desktop table above stays untouched. Desktop vitals flex
       hides here; the short-label line shows instead. */
    .visit-page .table-wrap { display: none; }
    .visit-page .rx-vitals-d { display: none; }
    .visit-page .rx-vitals-m {
        display: block;
        font-size: 13px;
        line-height: 1.5;
        color: var(--body);
    }
    /* ---- Visit medicines mirror the printed pad (phones only) --------
       Same structure + class names as prescriptions/print.php; screen
       sizing/tokens here since print.css never loads on this page. */
    .rx-list { display: block; padding: 0 14px 12px; }
    .rx-list .rx-med-list { list-style: decimal; margin: 0; padding-left: 22px; }
    .rx-list .rx-med-list li::marker { color: var(--brand); font-weight: 700; }
    .rx-list .rx-med { padding: 7px 0; border-bottom: 1px solid var(--line-soft); }
    .rx-list .rx-med:last-child { border-bottom: 0; }
    .rx-list .rx-med__name { font-weight: 700; font-size: 14px; color: var(--ink); }
    .rx-list .rx-med__sched { display: flex; align-items: baseline; gap: 8px; margin-top: 1px; font-size: 12.5px; flex-wrap: wrap; }
    .rx-list .rx-med__dose { font-weight: 600; color: var(--brand-darker); }
    .rx-list .rx-med__meal { color: var(--body); }
    .rx-list .rx-med__dur { margin-left: auto; color: var(--body); white-space: nowrap; }
    .rx-list .rx-med__note { font-size: 12px; color: var(--muted); margin-top: 1px; }

    /* ---- Compact patient rows (patients/index.php, phones only) --------
       Same design language as the appointment board: avatar | name +
       inline meta | call pill + action icons, subtle separators. */
    .patients-page .table-wrap { display: none; }
    .patients-page .filters { padding: 10px 14px; gap: 8px; }
    /* One-line card head: title + total on the left, New-patient on the
       right Ã¢â‚¬â€ no wrapping into two rows on phones. ~55Ã¢â‚¬â€œ65px tall. */
    .patients-page .card__head { flex-wrap: nowrap; gap: 8px; padding: 8px 14px; align-items: center; }
    .patients-page .card__title { font-size: 15px; white-space: nowrap; flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; }
    .patients-page .card__tools { width: auto; margin-left: auto; flex: 0 0 auto; gap: 8px; flex-wrap: nowrap; }
    .patients-page .card__tools .badge { font-size: 12px; min-height: 32px; padding: 5px 10px; display: inline-flex; align-items: center; }
    .patients-page .card__tools .btn { min-height: 40px; padding: 8px 14px; font-size: 14px; }
    .patients-page .filters .input,
    .patients-page .filters .select { padding: 8px 10px; }
    .patients-page .filters .field__label { margin-bottom: 3px; }
    .patients-page .filters__actions .btn { min-height: 40px; }
    .pt-list {
        display: block;
        margin: 0;
        padding: 0 14px 6px;
    }
    .pt-row {
        display: flex;
        align-items: center;
        gap: 8px;
        padding: 6px 0;
        border-bottom: 1px solid var(--line-soft);
        background: var(--paper);
    }
    .pt-row:last-child { border-bottom: 0; }
    .pt-row__avatar {
        width: 38px;
        height: 38px;
        flex: 0 0 38px;
        font-size: 14px;
        margin-top: 0;
        align-self: center;
    }
    .pt-row__main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; justify-content: center; }
    .pt-row__name {
        display: block;
        font-weight: 600;
        font-size: 14.5px;
        line-height: 1.3;
        color: var(--ink);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .pt-row__name:hover { color: var(--brand-dark); }
    .pt-row__sub {
        font-size: 12px;
        line-height: 1.4;
        color: var(--muted);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .pt-row__meta {
        font-size: 11.5px;
        line-height: 1.4;
        color: var(--muted);
    }
    .pt-row__sep { margin: 0 4px; }
    .pt-row__side {
        flex: 0 0 auto;
        display: flex;
        flex-direction: column;
        align-items: flex-end;
        justify-content: center;
        align-self: center;
        gap: 5px;
    }
    /* Compact row actions: same 15px glyphs, smaller boxes, tighter gap. */
    .pt-row__actions { display: flex; gap: 4px; }
    .pt-row__actions .btn { min-height: 30px; }
    .pt-row__actions .btn--icon.btn--sm { width: 30px; height: 30px; padding: 2px; }
    .pt-call {
        display: inline-flex;
        align-items: center;
        gap: 5px;
        max-width: 100%;
        min-height: 30px;
        padding: 4px 9px;
        border: 1px solid var(--brand-line);
        border-radius: 999px;
        background: var(--brand-soft);
        color: var(--brand-darker);
        font-size: 12.5px;
        font-weight: 700;
        line-height: 1.2;
        white-space: nowrap;
        text-decoration: none;
    }
    .pt-call:hover { background: var(--brand-softer); color: var(--brand-darker); text-decoration: none; }
    .pt-call .icon { flex: 0 0 auto; }
    .pt-call__num { font-variant-numeric: tabular-nums; }
    .pt-call--none {
        background: transparent;
        border-color: transparent;
        min-height: 0;
        padding: 4px 2px;
    }
    .pt-row__actions { display: flex; gap: 4px; }

    /* Any table without the card treatment still scrolls inside its wrap. */
    .table-wrap { -webkit-overflow-scrolling: touch; }

    /* Short fact rows stay on ONE line (key left, value right) instead of
       stacking key-above-value: four facts read as four lines, not eight. */
    .def-list__row { flex-direction: row; gap: 8px; padding: 6px 0; align-items: baseline; }
    .def-list__key { flex: 0 0 40%; max-width: 40%; }
    .def-list__val { flex: 1 1 auto; min-width: 0; }

    /* ---- Modals become bottom sheets ------------------------------- */
    .modal-backdrop { padding: 0; place-items: end stretch; }
    .modal,
    .modal--wide {
        width: 100%;
        max-width: none;
        max-height: 88dvh;
        border-radius: var(--radius-lg) var(--radius-lg) 0 0;
        border-bottom: 0;
        animation: sheet-in .22s var(--ease);
    }
    .modal__head { padding: 14px 16px 12px; }
    .modal__head::before {
        content: "";
        position: absolute;
        top: 6px;
        left: 50%;
        transform: translateX(-50%);
        width: 38px;
        height: 4px;
        border-radius: 99px;
        background: var(--line-strong);
    }
    .modal__head { position: relative; }
    .modal__body { padding: 16px; }
    .modal__foot { padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px)); }
    .modal__foot .btn { flex: 1 1 auto; }

    /* ---- Check-in sheet packs tighter (appointments/index.php) --------
       Vitals ride two-up like the instant form (the generic single-column
       collapse made five tall rows), inputs lose their desktop padding but
       keep 16px text so iOS never zooms, and the footer buttons stay 42px. */
    #checkinModal .modal__head { padding: 10px 12px 8px; }
    #checkinModal .modal__title { font-size: 15px; }
    #checkinModal .modal__body { padding: 10px 12px 12px; }
    #checkinModal .modal__patient { margin: 0 0 6px; font-size: 14px; }
    #checkinModal .form-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
    #checkinModal .field__label { font-size: 10.5px; margin-bottom: 2px; }
    #checkinModal .input { padding: 7px 10px; }
    #checkinModal .inv-list { gap: 5px; margin-bottom: 6px; }
    #checkinModal .inv-row { padding: 5px 8px; gap: 4px 7px; }
    /* One line per test on phones: name Ã‚Â·Ã‚Â·Ã‚Â· value Ãƒâ€”. The head dissolves so
       its children join the row grid; the value input keeps 16px text. */
    #checkinModal .inv-row {
        grid-template-columns: minmax(0, 1fr) 104px auto;
        align-items: center;
    }
    #checkinModal .inv-row__head { display: contents; }
    #checkinModal .inv-row__name {
        grid-column: 1;
        grid-row: 1;
        min-width: 0;
        font-size: 12.5px;
        font-weight: 600;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    #checkinModal .inv-row .input {
        grid-column: 2;
        grid-row: 1;
        padding: 5px 8px;
    }
    #checkinModal .inv-row__head .inv-chip__x { grid-column: 3; grid-row: 1; }
    #checkinModal .input-group { gap: 6px; }
    #checkinModal .field__hint { margin: 6px 0 0; font-size: 11.5px; line-height: 1.4; }
    #checkinModal .modal__foot { padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px)); }

    @keyframes sheet-in { from { transform: translateY(14px); opacity: 0; } }

    /* Confirmations stay centered popups, not bottom sheets: a destructive
       question must appear where the eye already is. */
    .modal-backdrop--center { place-items: center; padding: 20px; }
    .modal-backdrop--center .modal {
        width: min(430px, 100%);
        max-width: none;
        max-height: calc(100dvh - 80px);
        border-radius: var(--radius-lg);
        border-bottom: 1px solid var(--line);
        animation: modal-in .2s var(--ease);
    }

    .toast-host { left: 12px; right: 12px; max-width: none; }

    .dropdown__menu { right: 0; min-width: 190px; max-width: calc(100vw - 24px); }

    /* The account name is the first thing to go when space is short Ã¢â‚¬â€ the
       avatar still identifies the signed-in user. */
    .user-chip .user-chip__name { display: none; }

    /* --- Compact, non-wrapping action rows ------------------------- */
    .btn-row { gap: 7px; }
    .quick-actions { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
    .week-grid { grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); }
    .trend { min-height: 108px; }
    .trend__col { height: 108px; }
}

/* ==========================================================================
   <= 560px  Ã¢â‚¬â€ compact phones
   ========================================================================== */

@media (max-width: 560px) {
    :root { --gutter: 12px; --topbar-h: 38px; --tabbar-h: 60px; }

    /* ---- Appointment rows: two lines per patient ---------------------
       Kept in the 560px block as a safety net; the real phone layout is
       the dedicated max-width: 767px block at the end of this file. */
    .appt-page .appt-table tbody td.num { display: none; }

    h1 { font-size: var(--fs-xl); }
    .page-head__title { font-size: var(--fs-lg); }
    .topbar__title { font-size: var(--fs-md); }

    /* The page description adds a line but no information on a phone;
       the title alone already answers "where am I?". */
    .topbar__crumb { display: none; }

    /* One icon row is enough: the language switch moves into the account menu. */
    .topbar__lang { display: none; }
    .topbar__lang-inline { display: block; }

    /* Two rows on a phone: identity and account above, the patient lookup on
       a row of its own below. The lookup is the control a receptionist uses
       most, so it gets full width and a 16px field rather than being squeezed
       into whatever is left of a 320px bar. */
    .topbar { flex-wrap: wrap; row-gap: 7px; }
    .topbar__heading { order: 1; flex: 1 1 auto; }
    .topbar__spacer { display: none; }

    /* Children of this wrapper join the topbar's own flex line. */
    .topbar__actions { display: contents; }
    .topbar__actions .theme-toggle { order: 2; }
    .topbar__actions .dropdown { order: 3; }
    .topbar__search {
        order: 4;
        flex: 1 1 100%;
        max-width: none;
    }

    /* The lookup list hangs off its own field again now that the field is
       the full width of the bar. */
    .topbar__search .ac__list {
        position: absolute;
        top: calc(100% + 5px);
        left: 0;
        right: 0;
        max-height: 58vh;
    }

    .stat-grid { gap: 8px; }
    .stat { border-radius: var(--radius-md); padding: 10px 11px; }
    .stat__value { font-size: var(--fs-lg); }

    /* Fact grids (patient file, visit meta): two compact facts per row
       instead of one stacked pair per row. */
    .detail-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 12px; }
    .detail__key { font-size: 10.5px; margin-bottom: 1px; }
    .detail__val { font-size: 13.5px; }

    .grid { gap: 12px; }
    .page-head { margin-bottom: 12px; }
    .modal__body { padding: 14px; }

    /* Spacing utilities step down one notch Ã¢â‚¬â€ the same rhythm, less air. */
    .gap-4 { gap: 12px; }
    .mt-3 { margin-top: 12px; }
    .mb-3 { margin-bottom: 12px; }

    .card { border-radius: var(--radius-md); }
    .card__body { padding: 13px; }
    .card__head { padding: 11px 13px; gap: 8px; }
    .card__title { font-size: var(--fs-base); }
    .card__foot { padding: 11px 13px; }

    .pagination { padding: 10px 13px; gap: 8px; justify-content: space-between; }
    .pagination__info { flex: 1 1 100%; }
    .pagination__pages { margin-left: 0; }
    /* Keep the pager on one line: swap the long window for prev/next. */
    .pagination__pages .page-link:not(.is-active):not(:first-child):not(:last-child) { display: none; }

    .user-chip { padding: 3px; gap: 0; border-color: transparent; background: transparent; }
    .user-chip .user-chip__name { display: none; }
    .user-chip .icon { display: none; }
    .user-chip:hover { background: transparent; }

    /* Appointment booking keeps field pairs per row (date+time,
       fee+discount, source+referral) instead of stacking everything,
       like the instant-visit form. */
    #appointmentForm .form-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 8px; }
    #appointmentForm .field__hint { margin-top: 2px; }
    #appointmentForm #patientSummary { margin-top: 6px; }
    #appointmentForm [role="radiogroup"] { gap: 4px 10px; }
    #appointmentForm [role="radiogroup"] .check { min-height: 36px; font-size: 13px; }

    .table--cards tr { padding: 2px 13px 7px; }
    .table--cards td { font-size: 13px; }
    .table--cards td.cell-title { font-size: var(--fs-base); }

    .pt-list { padding: 0 13px 5px; }
    .pt-row { gap: 8px; padding: 5px 0; }
    .pt-call { font-size: 12px; padding: 4px 9px; }
    .patients-page .filters { padding: 10px 13px; }

    .empty-state { padding: 22px 14px; }
    .empty-state__text { max-width: 100%; }

    .ac__list { max-height: 250px; }

    .section-title { font-size: var(--fs-base); }
    .quick-action { padding: 10px; font-size: 12.5px; }
    .quick-action .icon { width: 28px; height: 28px; padding: 6px; }

    .timeline { padding-left: 20px; }
    .timeline__dot { left: -20px; }
}

/* ==========================================================================
   <= 380px  Ã¢â‚¬â€ narrow phones (320 / 360 / 375)
   --------------------------------------------------------------------------
   At this width every padding step is spent deliberately: the content keeps
   its full width and the chrome gives its own space back.
   ========================================================================== */

@media (max-width: 380px) {
    :root { --gutter: 10px; --fs-base: 13.5px; --tabbar-h: 58px; }

    .topbar { padding: 3px var(--gutter); gap: 8px; }
    .topbar__actions { gap: 4px; }

    .stat__value { font-size: var(--fs-md); }
    .stat__label { font-size: 10px; }

    .card__head { padding: 10px 11px; }
    .card__body { padding: 11px; }
    .card__foot { padding: 10px 11px; }

    .table--cards tr { padding: 2px 10px 4px; }
    .table--cards td { padding: 6px 0; }
    .table--cards .truncate { max-width: 52%; }

    .pt-list { padding: 0 11px 4px; }
    .pt-row__actions { gap: 5px; }
    .patients-page .filters { padding: 10px 11px; }
    .patients-page .card__head { padding: 8px 11px; }

    .appt-list { padding: 0 11px 5px; }
    .appt-types { padding: 7px 11px; }
    .appt-page .filters { padding: 10px 11px; }

    .pc-list { padding: 0 11px 5px; }
    .precheck-page .filters { padding: 10px 11px; }
    .precheck-form .card__body { padding: 11px; }
    .precheck-form .form-grid--4 { grid-template-columns: minmax(0, 1fr); }
    .precheck-form .card__foot { padding: 10px 11px; }

    .pv-list { padding: 0 11px 5px; }

    .vs-list { padding: 0 11px 5px; }
    .visit-page .filters { padding: 10px 11px; }
    .visit-page .card__head { padding: 8px 11px; }

    .visit-page .card__body { padding: 11px; }
    .visit-page .card__foot { padding: 10px 11px; }
    .rx-list { padding: 0 11px 10px; }

    /* Booking form at <=380px: the 3-column billing row and the one-line
       type pills cannot fit Ã¢â‚¬â€ stack billing, let type pills wrap. Never
       squeeze to overlap. */
    #appointmentForm .appt-billing { grid-template-columns: minmax(0, 1fr); }
    #appointmentForm .appt-billing .appt-fee,
    #appointmentForm .appt-billing .appt-discount,
    #appointmentForm .appt-billing .appt-total { grid-column: auto; grid-row: auto; }
    #appointmentForm [role="radiogroup"] { flex-wrap: wrap; }
    #appointmentForm [role="radiogroup"] .check { flex: 1 1 40%; white-space: normal; text-align: center; }

    .avatar--lg { width: 46px; height: 46px; flex-basis: 46px; font-size: var(--fs-md); }

    .tabbar__item { font-size: 10px; padding: 5px 1px; }
    .tabbar__label { letter-spacing: 0; }

    .dropdown__menu { min-width: 172px; }
}

/* ==========================================================================
   Patient timeline, phones Ã¢â‚¬â€ one compact record per visit.
   --------------------------------------------------------------------------
   Mobile-only: every rule lives inside this query, so the desktop card is
   untouched. Related facts share rows, labels stay small caps, chips stay
   tappable, and nothing may scroll sideways at 320px.
   ========================================================================== */

@media (max-width: 767px) {
    .timeline { padding-left: 18px; }
    .timeline::before { left: 4px; width: 2px; }
    .timeline__dot {
        left: -18px;
        top: 9px;
        width: 9px;
        height: 9px;
        box-shadow: 0 0 0 2px var(--paper);
    }
    .timeline__item { padding-bottom: 10px; }
    .timeline__body { padding: 10px 11px; line-height: 1.3; }

    /* Header: intro + Print history share one ~44px row. */
    .timeline__toolbar { flex-wrap: nowrap; gap: 8px; margin-bottom: 10px; min-height: 42px; }
    .timeline__toolbar p {
        font-size: 12px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .timeline__toolbar .btn { flex: 0 0 auto; min-height: 32px; padding: 5px 10px; font-size: 12px; }

    /* Date + visit no. + status + View flow in one wrapping row. */
    .timeline__head { gap: 5px; margin-bottom: 3px; }
    .timeline__date { font-size: 13.5px; }
    .timeline__head .badge { font-size: 10.5px; padding: 1px 7px; line-height: 1.6; }
    .timeline__head .btn { min-height: 30px; padding: 4px 8px; font-size: 12px; }

    /* Doctor + visit id: one muted 12px line. */
    .timeline__meta {
        font-size: 12px;
        line-height: 1.25;
        margin-bottom: 2px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    /* Fact rows: label + values inline, 5px apart. */
    .timeline__row { gap: 6px; margin-top: 5px; font-size: 12.5px; line-height: 1.3; }
    .timeline__key { min-width: 0; font-size: 10.5px; }

    /* Investigation chips: compact pills that wrap naturally. */
    .timeline__chips { gap: 5px; }
    .timeline__chips .chip { font-size: 11.5px; padding: 2px 5px 2px 8px; gap: 5px; line-height: 1.5; }
    .timeline__chips .chip .badge { font-size: 10px; padding: 1px 6px; }
}

/* ==========================================================================
   Landscape phones Ã¢â‚¬â€ the sticky topbar would eat the little height there is
   ========================================================================== */

@media (max-height: 500px) and (orientation: landscape) {
    .topbar { position: static; }
    .sidebar { overflow-y: auto; }
    .tabbar { display: none; }
    .page, .page--flush { padding-bottom: var(--gutter); }
    .toast-host { bottom: 14px; }
    .dev-meter { bottom: 8px; }
    /* No tab bar here, so the drawer needs its own opener back. */
    .topbar__toggle { display: inline-flex; }
    .bottom-bar { bottom: 0; }
    .has-bottom-bar { padding-bottom: calc(74px + env(safe-area-inset-bottom, 0px)); }
}

/* ==========================================================================
   Module bottom action bar (queue board, prescription pad)
   ========================================================================== */

.has-bottom-bar { padding-bottom: calc(74px + env(safe-area-inset-bottom, 0px)); }

.bottom-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 50;
    background: var(--paper);
    border-top: 1px solid var(--line);
    padding: 10px 14px calc(10px + env(safe-area-inset-bottom, 0px));
    box-shadow: 0 -4px 18px rgba(11, 26, 31, .08);
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

@media (min-width: 768px) {
    .bottom-bar { display: none; }
    .has-bottom-bar { padding-bottom: 0; }
}

/* --------------------------------------------------------------------------
   Stacking above the tab bar.

   This block sits after the .bottom-bar definition above on purpose: a
   module's own action bar and the prescription pad's toolbar both declare
   bottom: 0, and a rule placed earlier in the file would lose to them. Here
   they are lifted clear of the tab bar instead of hiding behind it, after
   the tab bar has already been given its height.
   -------------------------------------------------------------------------- */

@media (max-width: 767px) {
    .bottom-bar,
    .pad-actions {
        bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px));
        z-index: 55;
    }

    .bottom-bar { padding-bottom: 10px; }
    .pad-actions { padding-bottom: 10px; }
}

/* ==========================================================================
   Book Appointment, phones Ã¢â‚¬â€ a sectioned clinical workflow.
   --------------------------------------------------------------------------
   Mobile-only: desktop keeps the plain two-column form. Sections group
   patient, appointment, billing, referral and chamber info with one divider
   each; billing pairs fee beside a strong total; the visit type becomes
   segmented pills. No markup hooks, ids or names change Ã¢â‚¬â€ only presentation.
   ========================================================================== */

/* Book Appointment sections read as grouped blocks on every width. */
.appt-sec + .appt-sec { margin-top: 20px; }

@media (max-width: 767px) {
    .appt-sec { margin: 0; }
    .appt-sec + .appt-sec { margin-top: 14px; }
    .appt-sec__title {
        font-size: 13px;
        font-weight: 700;
        letter-spacing: .06em;
        text-transform: uppercase;
        color: var(--brand-darker);
        margin: 0 0 10px;
        padding-bottom: 6px;
        border-bottom: 1px solid var(--line-soft);
    }
    html[lang="bn"] .appt-sec__title { letter-spacing: 0; }

    /* Billing: fee beside a strong total, discount full width below.
       Placement pairs with the two-column billing grid, which only holds
       at compact widths (see below) Ã¢â‚¬â€ wider phones stack normally. */
    #appointmentForm #feeTotal {
        font-size: 19px;
        font-weight: 700;
        line-height: 1.25;
        color: var(--brand-darker);
    }

    /* Visit type as segmented pills on ONE line; the checked one fills. */
    #appointmentForm [role="radiogroup"] { display: flex; flex-wrap: nowrap; gap: 6px; }
    #appointmentForm [role="radiogroup"] .check {
        flex: 1 1 auto;
        min-width: 0;
        min-height: 40px;
        margin: 0;
        padding: 6px 4px;
        align-items: center;
        justify-content: center;
        border: 1px solid var(--line);
        border-radius: 10px;
        font-size: 12px;
        white-space: nowrap;
        overflow: hidden;
    }
    #appointmentForm [role="radiogroup"] .check span {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    #appointmentForm [role="radiogroup"] .check input { flex: 0 0 auto; }
    #appointmentForm [role="radiogroup"] .check:has(input:checked) {
        border-color: var(--brand);
        background: var(--brand-softer);
        color: var(--brand-darker);
        font-weight: 700;
    }
    #appointmentForm [role="radiogroup"] .check input { accent-color: var(--brand); }

    /* Compact patient summary + chamber banner. */
    #appointmentForm #patientSummary { padding: 8px 10px; }
    #appointmentForm #patientSummary strong { font-size: 13.5px; }
    #appointmentForm #patientSummary .faint.small { font-size: 12px; }
    #appointmentForm .appt-chamber { padding: 10px 12px; font-size: 12.5px; }

    /* Save leads, cancel follows Ã¢â‚¬â€ both reachable above the tab bar. */
    #appointmentForm .card__foot { gap: 8px; }
    #appointmentForm .card__foot .btn--primary { flex: 1 1 auto; min-height: 46px; }

    /* The report card always stacks below the booking form on phones,
       never beside it. Full-width title, hint and buttons. */
    #reportSection { display: block; width: 100%; max-width: 100%; min-width: 0; float: none; position: static; clear: both; box-sizing: border-box; margin: 12px 0 0; }
    #reportSection[hidden] { display: none; }
    #reportSection .card__title { font-size: 19px; font-weight: 700; line-height: 1.3; }
    #reportSection .field__hint { font-size: 12.5px; line-height: 1.45; }
    #reportSection .card__foot { flex-direction: column; align-items: stretch; }
    #reportSection .card__foot .btn { width: 100%; min-height: 46px; justify-content: center; }
}

@media (max-width: 560px) {
    /* Fee, discount and total share ONE row. */
    #appointmentForm .appt-billing { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px 6px; }
    .appt-billing .field__label { font-size: 10.5px; letter-spacing: .02em; }
    .appt-billing .appt-fee { grid-column: 1; grid-row: 1; }
    .appt-billing .appt-discount { grid-column: 2; grid-row: 1; }
    .appt-billing .appt-total { grid-column: 3; grid-row: 1; }
    .appt-billing .input { padding-left: 8px; padding-right: 8px; }
    #appointmentForm #feeTotal { font-size: 17px; }

    /* Tighter sections so the whole form fits one screen height. */
    .appt-sec + .appt-sec { margin-top: 12px; }
    .appt-sec__title { margin-bottom: 8px; }

    /* PC checkbox rides at input height beside the referral select. */
    .appt-pccheck { align-self: center; }
    .appt-pccheck .check { min-height: 44px; }
}

/* ==========================================================================
   APPOINTMENT LIST - PHONE ONLY (<= 767px)
   --------------------------------------------------------------------------
Desktop keeps its full table. On phones each patient is two lines:

     line 1   patient name + type + status      edit / cross icons
     line 2   patient code + mobile             payment / check-in

   Time, token, age, gender, fee, doctor, reason and the extra status
   badges are dropped. Cells are matched by role class (the cell-* hooks in
   appointments/_panel.php), never by data-label, because labels are
   translated and every tab has its own column set. The hooks are plain
   spans/classes with no desktop styling, so desktop is pixel-identical.
   ========================================================================== */

@media (max-width: 767px) {
/* --- Section shell: no dead space around the list ------------------- */
    .appt-page .card__body { padding: 0; }
    /* Header packs into two tight lines: date title, then badges + book. */
    .appt-page .card__head {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 6px;
        padding: 8px 10px;
    }
    .appt-page .card__head .card__title {
        flex: 1 1 0;
        min-width: 0;
        font-size: 15px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .appt-page .card__head .card__title svg { flex: 0 0 auto; }
    .appt-page .card__head .card__tools { display: contents; }
    /* Book rides the title line; the two badges share the line below. */
    .appt-page .card__head .card__tools .badge { order: 2; }
    .appt-page .card__head .card__tools .btn {
        order: 1;
        margin-left: auto;
        min-height: 32px;
        padding: 0 6px;
        font-size: 12px;
        white-space: nowrap;
    }
    .appt-page .card__head .card__tools .badge {
        font-size: 10.5px;
        padding: 1px 7px;
        white-space: nowrap;
    }
    .appt-page .card__head .card__tools .btn {
        min-height: 32px;
        padding: 0 8px;
        font-size: 12px;
        white-space: nowrap;
    }
    .appt-page .pagination {
        padding: 5px 10px;
        font-size: 11px;
        gap: 6px;
    }
    .appt-page .pagination__info { line-height: 1.3; }

/* --- Search / date / filter row ------------------------------------ */
    /* Two fields per line instead of one: this block was 179px tall on a
       360px screen, which is more than the list itself. */
    .appt-page .filters { padding: 6px 10px; gap: 5px 6px; }
    .appt-page .filters .field { flex: 1 1 46%; min-width: 0; }
    .appt-page .filters .field--wide { flex: 1 1 46%; }
    .appt-page .filters .field__label { margin-bottom: 1px; font-size: 10px; }
    /* 16px text is kept on purpose: anything smaller makes iOS zoom on focus. */
    .appt-page .filters .input,
    .appt-page .filters .select { padding: 5px 8px; }
    .appt-page .filters__actions {
        flex: 1 1 46%;
        gap: 4px;
        margin-left: 0;
    }
    .appt-page .filters__actions .btn {
        min-height: 32px;
        padding: 0 8px;
        font-size: 12px;
    }
    .appt-page .filters__actions .btn--icon {
        width: 32px;
        height: 32px;
    }

    /* --- New / follow-up / report counter strip ------------------------- */
    .appt-page .appt-types {
        padding: 5px 10px;
        gap: 3px 9px;
        font-size: 11px;
    }

    /* --- Status tabs: one dense, scrollable strip ----------------------- */
    .appt-page .appt-tabs {
        display: flex;
        padding: 2px 0 6px 12px;
        gap: 6px;
        overflow-x: auto;
        scrollbar-width: none;
        -ms-overflow-style: none;
        -webkit-overflow-scrolling: touch;
        /* Snap so a tab dragged halfway settles on one edge instead of resting
           between two. appointment.js centres the active tab on load. */
        scroll-snap-type: x proximity;
    }
    .appt-page .appt-tabs::-webkit-scrollbar { display: none; }
.appt-page .appt-tabs .tab {
        flex: 0 0 auto;
        display: inline-flex;
        align-items: center;
        min-height: 44px;
        padding: 7px 10px;
        font-size: 12px;
        line-height: 1.3;
        white-space: nowrap;
        scroll-snap-align: center;
    }
    .appt-page .appt-tabs .tab__count {
        margin-left: 0;
        min-width: 20px;
        height: 20px;
        padding: 0 6px;
        font-size: 10.5px;
        line-height: 1;
    }
    .appt-page .appt-panel { min-height: 72px; }
    /* Compact action toolbar: buttons share one comfortable tap height. */
    .appt-page .appt-panel__tools {
        padding: 8px 10px 0;
        gap: 6px;
    }
    .appt-page .appt-panel__tools .btn {
        min-height: 34px;
        padding: 4px 10px;
        font-size: 12.5px;
    }
    /* On the in-room board the only top action is Live TV, so it gets the whole
       row instead of sitting small at the left. Phone only - above 640px it is
       an ordinary button in the toolbar with the other actions. */
    @media (max-width: 640px) {
        .appt-page .appt-panel__tools--single {
            padding: 8px 12px 0;
            gap: 0;
        }
        .appt-page .appt-panel__tools--single .btn {
            display: flex;
            align-items: center;
            justify-content: center;
            width: 100%;
            height: 36px;
            min-height: 36px;
            padding: 0 12px;
            font-size: 13px;
            background: #fff;
            border: 1px solid #e5e7eb;
            border-radius: 10px;
        }
    }
    /* Compact footer. */
    .appt-page .pagination { padding: 8px 10px; }
    .appt-page .pagination__info { font-size: 12px; }

/* --- Rows: one skeleton for every tab --------------------------------
   ROW 1: name | type | status | token+check
   ROW 2: phone (appointed only Ã¢â‚¬â€ collapses elsewhere)
   ROW 3: primary actions left .... icons right (shared-area pair)
   Empty tracks collapse, so every tab keeps the same shape. */
    .appt-page .appt-table { table-layout: fixed; width: 100%; }
    /* Unified 2-line flex row (every tab):
       ROW 1: check Ã‚Â· name Ã‚Â· type Ã‚Â· status Ã‚Â· Next Ã¢â‚¬Â¦ hold/resume toggle
       ROW 2: mobile/phone Ã¢â‚¬Â¦ primary Ã¢â‚¬Â¦ print Ã‚Â· priority Ã‚Â· cross Ã‚Â· menu
       Dissolved cells flow as individual items; the ::before rule forces
       the single line break. Missing pieces simply leave no gap. */
    .appt-page .appt-table tbody tr {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 4px 6px;
        padding: 7px 10px;
        border-bottom: 1px solid var(--line-soft);
        border-radius: 0;
    }
    .appt-page .appt-table tbody tr::before {
        content: "";
        order: 7;
        flex-basis: 100%;
        height: 0;
    }
    /* Keep only the skeleton cells (token is back: selection must work). */
    .appt-page .appt-table tbody td:not(.cell-title):not(.cell-type):not(.cell-status):not(.cell-phone):not(.cell-token):not(.cell-actions) {
        display: none;
    }
    .appt-page .appt-table tbody td {
        padding: 0;
        border-bottom: 0;
        text-align: left;
        min-width: 0;
    }
    .appt-page .appt-table tbody td::before { content: none; }

    /* ROW 1: name block. Doctor badge hides here Ã¢â‚¬â€ the row tint + edge
       bar already say who booked it. */
    .appt-page .appt-table tbody td.cell-title,
    .appt-page .appt-table tbody td.cell-status,
    .appt-page .appt-table tbody td.cell-token,
    .appt-page .appt-table tbody td.cell-actions,
    .appt-page .appt-table tbody td.cell-actions .cell-act-top,
    .appt-page .appt-table tbody td.cell-actions .cell-act-bottom,
    .appt-page .appt-table tbody .d-inline {
        display: contents;
    }
    .appt-page .appt-table tbody td.cell-token .rep-check {
        order: 1;
        flex: none;
        width: 20px;
        height: 20px;
        margin: 0;
    }
    .appt-page .appt-table tbody td.cell-token .token-pill { display: none; }
    .appt-page .appt-table tbody td.cell-title .cell-strong {
        order: 2;
        flex: 1 1 64px;
        min-width: 0;
        font-size: 17px;
        font-weight: 600;
        line-height: 1.25;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .appt-page .appt-table tbody td.cell-title .doc-badge { display: none; }
    /* Code/extra sub-bits hide; the phone line (waiting) survives. */
    .appt-page .appt-table tbody td.cell-title .faint.small {
        order: 8;
        min-width: 0;
        font-size: 12px;
        font-weight: 400;
        line-height: 1.35;
        color: var(--muted);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .appt-page .appt-table tbody td.cell-title .cell-code,
    .appt-page .appt-table tbody td.cell-title .cell-sub-rest { display: none; }
    .appt-page .appt-table tbody td.cell-title .cell-phone-mob,
    .appt-page .appt-table tbody td.cell-title .cell-phone-inline {
        display: inline;
        font-size: 12.5px;
    }
    /* Code stays, the rest of the sub-line (age/gender/time) goes. */
    .appt-page .appt-table tbody td.cell-title .cell-sub-rest { display: none; }

    /* ROW 1 middle: the type badge (rides row 2 with the meta). */
    .appt-page .appt-table tbody td.cell-type {
        order: 9;
        flex: none;
    }
    .appt-page .appt-table tbody td.cell-type .badge {
        font-size: 10px;
        font-weight: 600;
        padding: 1px 6px;
        white-space: nowrap;
    }

    /* Status badges split: first stays with the meta line, Next rides
       row 1 right Ã¢â‚¬â€ but a lone badge always stays on row 2. */
    .appt-page .appt-table tbody td.cell-status .badge { order: 10; }
    .appt-page .appt-table tbody td.cell-status .badge:last-child:not(:first-child) {
        order: 3;
        margin-left: auto;
    }
    /* Third badge onwards (pre-check etc.) stays desktop-only. */
    .appt-page .appt-table tbody td.cell-status .badge ~ .badge ~ .badge { display: none; }
    /* Report fraction rides with its badge on row 2. */
    .appt-page .appt-table tbody td.cell-status .faint.small {
        order: 10;
        flex: none;
        margin-left: 4px;
        font-size: 10px;
        white-space: nowrap;
    }

    /* ROW 2: phone only (appointed + waiting line) Ã¢â‚¬â€ collapses when empty. */
    .appt-page .appt-table tbody td.cell-phone {
        order: 8;
        flex: 0 1 auto;
        min-width: 0;
        font-size: 12.5px;
        line-height: 1.35;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .appt-page .appt-table tbody td.cell-phone a {
        color: var(--muted);
        text-decoration: none;
    }

    /* Token pill hides on mobile (name + phone identify the row);
       the checkbox stays for batch selection. */
    .appt-page .appt-table tbody td.cell-token .rep-check {
        order: 1;
        flex: none;
        width: 20px;
        height: 20px;
        margin: 0;
    }
    .appt-page .appt-table tbody td.cell-token .token-pill { display: none; }
    /* ROW 2 right: primary action first, then the overflow menu. Icon-only
       row buttons (print/cancel) live inside the menu on mobile. */
    .appt-page .appt-table tbody .cell-act-bottom > a.btn:not(.btn--icon),
    .appt-page .appt-table tbody .cell-act-bottom > form button.btn--primary {
        order: 11;
        flex: none;
        margin-left: auto;
    }
    .appt-page .appt-table tbody .cell-act-bottom > button.btn--icon {
        order: 12;
        flex: none;
    }
    .appt-page .appt-table tbody .cell-act-top > .dropdown { order: 12; flex: none; }
    .appt-page .appt-table tbody .cell-act-top .btn--icon,
    .appt-page .appt-table tbody .cell-act-top .btn-quiet,
    .appt-page .appt-table tbody .cell-act-bottom .btn--icon,
    .appt-page .appt-table tbody .cell-act-bottom .btn-quiet {
        width: 28px;
        height: 28px;
        min-height: 28px;
        padding: 0;
        flex: none;
    }
    .appt-page .appt-table tbody .cell-act-bottom .btn {
        min-height: 30px;
        padding: 0 10px;
        font-size: 12px;
        gap: 4px;
        white-space: nowrap;
        flex: none;
    }
    /* Mobile-only / desktop-only twins for the consolidated row menu. */
    .appt-page .appt-table tbody .hide-mobile { display: none !important; }

/* Doctor-created rows keep their tint on phones too, with the accent bar. */
    .appt-page .appt-table tbody tr.is-doctor {
        background: var(--brand-softer);
        border-left: 3px solid var(--brand);
        padding-left: 7px;
    }
    /* Selected / active card: white, with a small teal accent on its edge. */
    .appt-page .appt-table tbody tr.is-current {
        background: var(--paper);
        border-left: 3px solid var(--brand);
        padding-left: 7px;
    }
    .appt-page .appt-table tbody tr.is-highlight {
        background: var(--paper);
        border-left: 3px solid var(--brand-line);
        padding-left: 7px;
    }

    /* Completed tab uses the same unified skeleton above (its missing
       status/phone cells simply collapse). The table keeps fixed layout so
       names truncate instead of overflowing the screen. */
    .appt-page .appt-table--oneline { table-layout: fixed; width: 100%; }
    .appt-page .appt-table--oneline tbody td.cell-title,
    .appt-page .appt-table--oneline tbody td.cell-type {
        width: auto;
    }

    /* Empty state still needs some air. */
    .appt-page .empty-state { padding: 14px 12px; }
    /* Mobile-only menu copies of the row icon buttons. */
    .appt-page .appt-table tbody li.show-mobile { display: list-item; }
    /* Filters stack: search full row, then date full row, 44px targets. */
    .appt-page .filters .field { flex: 1 1 100%; max-width: none; }
    .appt-page .filters .input,
    .appt-page .filters .select { min-height: 44px; }
}

/* ==========================================================================
   TABLET BAND - 641px to 1023px
   --------------------------------------------------------------------------
   One value at both ends of the tablet range, to match the card/table swap at
   1024px below: this is a tablet, not a wide phone and not a small desktop.

   The cards themselves already come from the <=1023px block, so nothing here
   rebuilds a card - it only lets the tablet breathe: bigger touch targets,
   roomier padding, the facts a phone has to drop, and a header and toolbar
   sized like the desktop ones.
   ========================================================================== */
@media (min-width: 641px) and (max-width: 1023px) {
    .appt-page .appt-tabs {
        gap: 10px;
        padding: 2px 0 6px;
        /* A pointer device, so the drag-to-settle snap of a phone is dropped;
           the strip still scrolls when it has to. */
        scroll-snap-type: none;
    }
    .appt-page .appt-tabs .tab {
        min-height: 0;
        padding: 8px 14px;
        font-size: 13px;
        scroll-snap-align: none;
    }
    .appt-page .appt-tabs .tab__count {
        min-width: 22px;
        height: 22px;
        padding: 0 7px;
        font-size: 11.5px;
    }
    /* The header keeps the same two-row packing, just with desktop-sized type,
       so it does not look flattened beside the desktop tabs. */
    .appt-page .card__head {
        gap: 10px;
        padding: 11px 16px;
    }
    .appt-page .card__head .card__tools .badge {
        font-size: 11.5px;
        padding: 2px 9px;
    }
    /* The phone block pins a 32px button with tight type. A tablet is still a
       touch screen, so the 42px target from the global coarse-pointer rule
       (pointer: coarse), (max-width: 820px) stands - only the phone's padding
       and font size are undone. */
    .appt-page .card__head .card__tools .btn {
        min-height: 42px;
        padding: 9px 14px;
        font-size: inherit;
        white-space: nowrap;
    }

    /* ---- cards get room to breathe ----------------------------------
       That lives in its own band below, after the <=1023px card block: both
       scopes are the same specificity, so source order decides, and the card
       block comes later in this file. */

    /* ---- filters: search and date side by side ---------------------- */
    .appt-page .filters .field { flex: 1 1 0; max-width: none; }
    .appt-page .filters .field--wide { flex: 2 1 0; }
    .appt-page .filters .input,
    .appt-page .filters .select { min-height: 40px; }
    .appt-page .filters__actions { gap: 8px; }

    /* ---- top action buttons: auto width, left aligned --------------- */
    .appt-page .appt-panel__tools {
        padding: 8px 16px 0;
        gap: 8px;
        flex-wrap: wrap;
    }
    .appt-page .appt-panel__tools .btn { min-height: 36px; padding: 0 14px; font-size: 13px; }
    .appt-page .appt-panel__tools--single { padding: 8px 16px 0; gap: 0; }
    .appt-page .appt-panel__tools--single .btn {
        width: auto;
        height: 36px;
        min-height: 36px;
        padding: 0 14px;
        font-size: 13px;
    }

    /* ---- pagination ------------------------------------------------ */
    .appt-page .pagination { padding: 8px 16px; font-size: 12px; }
}

/* Only one of the two date forms is ever shown: the long one everywhere, the
   short one on a phone where the Book button needs the room. Without this the
   heading reads both dates at once on a desktop. */
.card__title-short { display: none; }

/* The patient code lives in the card markup on every board - a phone card has
   no room for it, so it is hidden by default and the tablet band turns it back
   on. Keeping it in the markup means nothing shifts when that happens. */
.patient-card__code { display: none; }

/* --------------------------------------------------------------------------
   The name opens the patient's profile on every board.
   --------------------------------------------------------------------------
   It is an <a> on all six cards now, but deliberately styled as plain text: in
   a list this dense a row of underlined links reads as noise, and the colour
   and underline of a link would fight the type chip beside it. So the link
   affordance is carried by the cursor and, for anyone not using a mouse, by the
   focus ring and the press state below - which is why the ring is not optional
   decoration here. */
.appt-card__name:focus-visible,
.patient-card__name:focus-visible {
    outline: 2px solid #0f5c4d;
    outline-offset: 2px;
    border-radius: 3px;
}
.appt-card__name:active,
.patient-card__name:active { opacity: .7; }

/* --- Header: short date + Book on one row, badges on the row below -----
       The long "Friday, 09 October 2026" and the Book button could not both
       fit on one phone row, so the date ellipsised into nothing. index.php
       renders the short form alongside the long one and only one of the two
       shows at a time - tablet and desktop keep the long date untouched. */
@media (max-width: 640px) {
    .appt-page .card__head .card__title {
        order: 0;
        flex: 1 1 0;
        min-width: 0;
        font-size: 13px;
        font-weight: 600;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .appt-page .card__head .card__title-short { display: inline; }
    .appt-page .card__head .card__title-full { display: none; }
    /* The ellipsis has to sit on the text itself: the title is a flex row, so
       text-overflow on the heading alone would clip mid-word with no dots. */
    .appt-page .card__head .card__title-short,
    .appt-page .card__head .card__title-full {
        min-width: 0;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
    }

    .appt-page .card__head .card__tools .btn {
        order: 1;
        flex: 0 0 auto;
        height: 34px;
        min-height: 34px;
        padding: 0 12px;
        font-size: 13px;
        white-space: nowrap;
    }
    /* A full-width zero-height item: the cheapest way to say "the next thing
       starts a new row" without touching the markup. */
    .appt-page .card__head::after {
        content: '';
        order: 2;
        flex: 0 0 100%;
        height: 0;
    }
    .appt-page .card__head .card__tools .badge {
        order: 3;
        font-size: 11px;
        padding: 1px 8px;
        white-space: nowrap;
    }
}

/* ==========================================================================
   WAITING BOARD - COMPACT CARDS (PHONE ONLY, <= 640px)
   --------------------------------------------------------------------------
   The stacked table above spends three or four lines per patient and buries
   the token, age, gender and time. This is a two-row card instead:

     row 1   [x] [A-002] Name 25y NEXT              [Ã¢â€“Â¶ Visit]
     row 2   (blank)     Male Ã‚Â· 05:10 PM [New] [Ã¢â€”Â Waiting]  [Ã¢Å¡â€˜][Ã¢ÂÂ¸][Ã°Å¸â€“Â¨][Ã¢Å“â€¢]

   A three-column grid (auto | 1fr | auto) over two rows does the alignment:
   the empty first cell of row 2 lines the meta text up under the patient name,
   and the icon group lands directly beneath the Visit button.

   Scoped to .appt-cards--waiting because the report board reuses the same card
   classes with different content and its own (flex) layout.

   The card list is display:none above this breakpoint, so a tablet or desktop
   keeps the table untouched.
   ========================================================================== */
@media (max-width: 1023px) {
    /* Swap the board: cards in, table out.
       Both halves come from this one value. Above 1023px nothing here applies,
       so the base rules take over - .appt-cards is display:none (style.css) and
       the table shows as an ordinary table. There is no width in between where
       neither branch is in charge. */
    .appt-page .appt-cards { display: block; }
    .appt-page .appt-table-wrap,
    .appt-page .appt-table { display: none; }

    /* The old stacked-label fallback (.table--cards turns every tr/td into a
       block and prints "TOKEN / AGE / GENDER..." from td::before) is what used
       to fill the 641-1023px gap. This board has a real card list for every
       width below 1024px, so that fallback must never reach it: below the
       breakpoint the table is hidden outright, and at or above it the table is
       laid out as a table. Scoped to .appt-page because .table--cards is the
       normal mobile table pattern on every other page in the app. */
    .appt-page .appt-table.table--cards thead { display: table-header-group; }
    .appt-page .appt-table.table--cards tbody { display: table-row-group; }
    .appt-page .appt-table.table--cards tr { display: table-row; }
    .appt-page .appt-table.table--cards td { display: table-cell; width: auto; }
    .appt-page .appt-table.table--cards td::before { content: none; }
    .appt-page .appt-table.table--cards tbody tr {
        display: table-row;
        padding: 0;
        border-bottom: 0;
    }

    .appt-cards {
        list-style: none;
        margin: 0;
        padding: 0;
        border: 1px solid #e5e7eb;
        border-radius: 12px;
        background: #fff;
        overflow: hidden;
    }

    /* ---- the grid ---- */
    /* Two 34px rows + a 2px gap + 2x3px padding + a 1px divider = ~76px. The
       rows are pinned at 34 by their touch targets, so the height comes down
       through padding and gap rather than by shrinking a button below the
       32px target floor. */
    .appt-cards--waiting .appt-card {
        display: grid;
        /* minmax, not a bare 1fr: the middle column has a floor so the name
           never collapses, and the fixed side columns keep their own natural
           size so the checkbox and token are not squeezed out. */
        grid-template-columns: min-content minmax(70px, 1fr) auto;
        column-gap: 10px;
        row-gap: 2px;
        align-items: center;
        min-height: 0;
        padding: 3px 12px;
        border-left: 3px solid transparent;
        border-bottom: 1px solid #e5e7eb;
    }
    .appt-card:last-child { border-bottom: 0; }
    .appt-card:active { background: #f3f4f6; }

    /* A ticked checkbox marks the card. Done in CSS with :has so it needs no
       JS and stays correct when the 15s poll replaces the rows. */
    .appt-card:has(.appt-card__check:checked) {
        background: #ecf7f3;
        border-left-color: #0f5c4d;
    }
    /* The next patient is called out twice over: a teal edge, a mint wash and
       the NEXT pill on the first row. */
    .appt-card.is-next {
        border-left-color: #0f5c4d;
        background: #ecf7f3;
    }
    /* A held row is dimmed so the queue order still reads at a glance. */
    .appt-card.is-held { background: #fafbfc; }
    .appt-card.is-held .appt-card__name { color: var(--muted); }

    /* ---- row 1, cell 1: checkbox + token ---- */
    /* min-width, not just flex: the checkbox and token must never be squeezed -
       an auto grid column will happily shrink them to fit the middle column. */
    .appt-card__lead {
        display: flex;
        align-items: center;
        align-self: stretch;
        gap: 8px;
        min-width: max-content;
    }
    .appt-card__check {
        flex: 0 0 auto;
        width: 18px;
        height: 18px;
        margin: 0;
        accent-color: #0f5c4d;
    }
    .appt-card__token {
        flex: 0 0 auto;
        font-family: var(--font-mono);
        font-size: 12px;
        font-weight: 700;
        line-height: 1.2;
        color: #0f5c4d;
        background: #e8f5f1;
        border-radius: 999px;
        padding: 3px 8px;
        white-space: nowrap;
    }
    .appt-card__token.token-pill--current { background: #0f5c4d; color: #fff; }

    /* ---- row 1, cell 2: name + age + NEXT ---- */
    /* The three items share one 1fr column. The name has flex:1 so it absorbs
       whatever is left and ellipsises; age and NEXT are fixed so they never
       push it out. They are ordered by "cost": the name first, then the age,
       then the badge, which the CSS below trims on its own when space is tight
       (see the min-width:0 order in .appt-card__name). */
    .appt-card__id {
        grid-column: 2;
        grid-row: 1;
        display: flex;
        align-items: center;
        align-self: stretch;
        gap: 6px;
        min-width: 0;
    }
    /* The name takes the slack and ellipsises; min-width:0 is what lets the
       ellipsis work inside a grid cell. It is a link to the patient, so the
       underline and the default link blue both have to go. */
    .appt-card__name,
    .appt-card__id a {
        flex: 1 1 auto;
        min-width: 0;
        font-size: 15px;
        font-weight: 600;
        line-height: 1.25;
        color: #0f172a;
        text-decoration: none;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    /* Both are fixed, so the name keeps whatever the 1fr column has left. The
       <=360px rules below drop the age, and the NEXT badge is redundant with
       the teal card edge, so a very narrow row can lose it too. */
    .appt-card__age {
        flex: 0 0 auto;
        margin-left: 6px;
        font-size: 12px;
        font-weight: 400;
        line-height: 1.25;
        color: #6b7280;
        white-space: nowrap;
    }
    .appt-card__next {
        flex: 0 0 auto;
        font-size: 10px;
        font-weight: 700;
        letter-spacing: .04em;
        line-height: 1.2;
        color: #0f5c4d;
        background: #d1ede5;
        border-radius: 999px;
        padding: 2px 6px;
        white-space: nowrap;
    }

    /* ---- row 1, cell 3: Visit ---- */
    .appt-card__cta {
        display: flex;
        align-items: center;
        justify-content: flex-end;
        align-self: stretch;
        grid-column: 3;
        grid-row: 1;
    }
    .appt-card__visit {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: 6px;
        height: 34px;
        min-height: 34px;
        padding: 0 14px;
        border-radius: 10px;
        font-size: 13px;
        font-weight: 600;
        position: relative;
    }
    .appt-card__visit .icon { flex: 0 0 auto; }

    /* ---- row 2, cell 1: the spacer that lines row 2 up with the name ---- */
    /* Row 2's first cell is an empty spacer. It carries the same class as row 1's
       checkbox+token group, so the placement has to be stated again with a
       selector that wins - otherwise it would land back on row 1. */
    .appt-cards--waiting .appt-card__lead--spacer {
        grid-column: 1;
        grid-row: 2;
    }
    .appt-cards--waiting .appt-card__lead:not(.appt-card__lead--spacer) {
        grid-column: 1;
        grid-row: 1;
    }

    /* ---- row 2: meta text and the four action buttons share one wide cell.
       Two separate columns could not hold both - the four 34px buttons are
       ~160px, and the name on row 1 then had under 100px and collapsed to a
       few characters. __l2 spans columns 2-3, and display:contents lifts its
       two children (the meta span and __tools) into that one cell. */
    /* ---- row 2: meta text, then the two action pairs ----
       Four cells on this row: the meta words take BOTH the first and the
       middle column, so the line starts under the checkbox/token exactly as
       the name does and still has room for the chips. The two button pairs
       sit under the Visit button on the right.

       The meta starts at the left edge of the card's content box, which is
       what the brief asks for - it lines up with the token, not the name. The
       name sits one indent to the right because the checkbox and token occupy
       the first column. */
    .appt-cards--waiting .appt-card__l2 {
        grid-column: 1 / 4;
        grid-row: 2;
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto auto;
        align-items: center;
        column-gap: 8px;
        align-self: stretch;
        min-width: 0;
    }
    .appt-cards--waiting .appt-card__l2 > .appt-card__meta {
        /* Own column, min-width:0 so the tail ellipses rather than pushing the
           buttons off the card. */
        grid-column: 1;
        flex: 1 1 auto;
        min-width: 0;
        margin-left: 0;
    }
    .appt-card__meta {
        display: flex;
        align-items: center;
        gap: 5px;
        min-width: 0;
        font-size: 12px;
        font-weight: 400;
        line-height: 1.2;
        color: #6b7280;
        white-space: nowrap;
        overflow: hidden;
        /* Overflowing tail fades instead of wrapping into a third row. */
        mask-image: linear-gradient(90deg, #000 88%, transparent 100%);
        -webkit-mask-image: linear-gradient(90deg, #000 88%, transparent 100%);
    }
    .appt-card__sep { color: #c3c8ce; }
    .appt-card__gender,
    .appt-card__time { flex: 0 0 auto; }

    .appt-card__chip {
        flex: 0 0 auto;
        display: inline-flex;
        align-items: center;
        gap: 4px;
        font-size: 11px;
        font-weight: 600;
        line-height: 1.2;
        color: #5b6472;
        background: #f1f5f9;
        border-radius: 999px;
        padding: 2px 8px;
        white-space: nowrap;
    }
    .appt-card__chip--doc { color: #0f5c4d; background: #e8f5f1; }
    /* Status dots: amber while the lab still owes results, green once every
       ordered test is ready, blue while the doctor is reviewing. */
    .appt-card__chip--amber { color: #8a5a12; background: #fdf0d8; }
    .appt-card__chip--green { color: #1c6b47; background: #dff3e8; }
    .appt-card__chip--blue  { color: #2b5c86; background: #e3edf7; }
    .appt-card__dot {
        width: 6px;
        height: 6px;
        border-radius: 999px;
        background: #f59e0b;
        flex: 0 0 auto;
    }
    .appt-card__chip--green .appt-card__dot { background: #22a06b; }
    .appt-card__chip--blue  .appt-card__dot { background: #3b82f6; }

    /* ---- the four action buttons ----
       They sit inside __l2's flex row as two pairs around the meta text:
       prioritise and hold/resume on the left, print and cancel on the right.
       Four 34px buttons in a single run took ~160px and left the meta text no
       room at all; two pairs leave the middle for the words. */
    .appt-cards--waiting .appt-card__tools {
        display: flex;
        align-items: center;
        gap: 8px;
        flex: 0 0 auto;
    }
    /* DOM order is left-pair, meta, right-pair (the print/cancel markup follows
       the hold/resume forms). The columns want meta first so it lines up under
       the name, so order puts it there without moving any markup. */
    .appt-cards--waiting .appt-card__l2 > .appt-card__meta        { order: 1; }
    .appt-cards--waiting .appt-card__l2 > .appt-card__tools--left  { order: 2; }
    .appt-cards--waiting .appt-card__l2 > .appt-card__tools--right { order: 3; }
    /* The forms are only a wrapper here - the button is the control. */
    .appt-card__tools form { display: inline-flex; }

    /* A full reset first: an unreset <button> on a phone keeps its UA border,
       background and padding, which is what made these look unstyled. */
    .appt-card__tool,
    .appt-card__tools .appt-card__tool {
        appearance: none;
        -webkit-appearance: none;
        flex: 0 0 auto;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 34px;
        height: 34px;
        min-height: 34px;
        padding: 0;
        margin: 0;
        border: 0;
        border-radius: 10px;
        background: #f1f5f9;
        color: #475569;
        font: inherit;
        line-height: 0;
        cursor: pointer;
        text-decoration: none;
        transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
    }
    .appt-card__tool:active { background: #e2e8f0; }
    /* icon() writes width/height as SVG attributes, so the size has to be set
       here to win over them. 18px on the 34px button leaves 8px of breathing
       room on each side. */
    .appt-card__tool .icon,
    .appt-card__tools .appt-card__tool .icon {
        width: 18px;
        height: 18px;
        flex: 0 0 auto;
    }

    .appt-card__tool:focus-visible,
    .appt-card__visit:focus-visible,
    .appt-card__check:focus-visible {
        outline: 2px solid #0f5c4d;
        outline-offset: 2px;
    }

    /* Priority: amber while the flag is set. */
    .appt-card__tool.is-on {
        background: #fff4e0;
        color: #d97706;
    }
    .appt-card__tool.is-on:active { background: #fde9c4; }
    /* Hold and resume share one mint treatment. */
    .appt-card__tool--hold,
    .appt-card__tool--resume {
        background: #e8f5f1;
        color: #0f5c4d;
    }
    .appt-card__tool--hold:active,
    .appt-card__tool--resume:active { background: #d7ece5; }
    .appt-card__tool--done { background: #dff3e8; color: #1c6b47; }
    /* Cancel stays red on a red wash so it never reads as a neutral action. */
    .appt-card__tool--cancel { background: #fef2f2; color: #dc2626; }
    .appt-card__tool--cancel:active { background: #fee2e2; }
}

/* ==========================================================================
   REPORT BOARD - SINGLE LINE (PHONE ONLY, <= 640px)
   --------------------------------------------------------------------------
   The report row is read, not worked on: token, who, how old, how far the
   lab has got, then Review and Print. That fits one line comfortably, so the
   card drops its second row here and every item shares the row:

     [x] [A-001] Habib  49y  [Ã¢â€”Â All reports ready] 1/1   [Review] [Ã°Å¸â€“Â¨]

   display:contents lifts the two row wrappers out of the flow so their
   children become flex items of the card itself - no markup change, and the
   waiting board above keeps its own two-line layout untouched.
   ========================================================================== */
@media (max-width: 1023px) {
    .appt-cards--report .appt-card {
        display: flex;
        flex-direction: row;
        align-items: center;
        gap: 8px;
        padding: 6px 12px;
        border-left: 3px solid transparent;
        border-bottom: 1px solid #e5e7eb;
    }
    /* The two row wrappers vanish; their children become flex items. */
    .appt-cards--report .appt-card__l1,
    .appt-cards--report .appt-card__l2 {
        display: contents;
    }

    /* Reading order on the single row. */
    .appt-cards--report .appt-card__check  { order: 1; }
    .appt-cards--report .appt-card__token  { order: 2; }
    .appt-cards--report .appt-card__name   { order: 3; }
    .appt-cards--report .appt-card__meta   { order: 4; }
    .appt-cards--report .appt-card__next   { order: 5; }
    .appt-cards--report .appt-card__actions { order: 6; }

    /* Name and meta share the slack. The name is the row's identity, so it keeps a
       guaranteed floor and the meta absorbs the shortfall instead - its tail
       already fades rather than wrapping. */
    .appt-cards--report .appt-card__name {
        flex: 1 1 auto;
        min-width: 68px;
    }
    /* A shrink factor of 6 against the name's 1 is what makes the status text
       give way before the patient's name does. */
    .appt-cards--report .appt-card__meta {
        flex: 0 6 auto;
        min-width: 0;
        gap: 5px;
    }
    /* Age is short and always fits; the status chip after it is what the fade
       takes from, so it is allowed to be the flexible one. */
    .appt-cards--report .appt-card__chip--status {
        flex: 0 1 auto;
        min-width: 0;
        overflow: hidden;
    }
    /* "All reports ready" and "49 years" are two long words fighting for the same
   row, so the chips carry their short form on a phone: "Ready", "Waiting",
       "Reviewing". The long text stays in the markup, so a screen reader still
   announces it in full. */
    .appt-cards--report .appt-card__chip-text { font-size: 0; }
    .appt-cards--report .appt-card__chip-text::after {
        content: attr(data-short);
        font-size: 12px;
    }
    /* Same short-form swap for the age: "49 years" becomes "49y" so the name gets
       the space instead. The full wording stays for screen readers. */
    .appt-cards--report .appt-card__age-text { font-size: 0; }
    .appt-cards--report .appt-card__age-text::after {
        content: attr(data-short);
        font-size: 12px;
    }
    /* The visit code (V-20261009-20405) is long and the age already says who
       this is, so it gives up its place on a phone. */
    .appt-cards--report .appt-card__code { display: none; }
    .appt-cards--report .appt-card__sep { display: none; }

    .appt-cards--report .appt-card__actions {
        flex: 0 0 auto;
        display: flex;
        align-items: center;
        gap: 6px;
    }
    /* The two action buttons are 32px squares here, matching the print button's
   treatment on the waiting card. A labelled "Review" button is ~119px on its
       own and squeezes the patient name down to two characters, so the label
       goes and the button keeps its title and aria-label. */
    .appt-cards--report .appt-card__visit {
        width: 32px;
        height: 32px;
        min-height: 32px;
        padding: 0;
        font-size: 12.5px;
        justify-content: center;
    }
    .appt-cards--report .appt-card__visit > span {
        display: none;
    }
    .appt-cards--report .appt-card__visit .icon {
        width: 17px;
        height: 17px;
    }
    .appt-cards--report .appt-card__menu-btn,
    .appt-cards--report .appt-card__actions .btn--icon {
        width: 32px;
        height: 32px;
        min-height: 32px;
        padding: 0;
    }
    .appt-cards--report .appt-card__menu-btn .icon {
        width: 17px;
        height: 17px;
    }
    /* Age in the short form, matching the waiting card. */
    .appt-cards--report .appt-card__meta > span:first-child { flex: 0 0 auto; }
}

/* ==========================================================================
/* ==========================================================================
   APPOINTED BOARD - PHONE CARD (PHONE ONLY, <= 640px)
   --------------------------------------------------------------------------
   One appointment, two rows, about 68px:

     row 1  Rafiqul  56y Ã‚Â· Male  [phone]           [Ã¢Å“â€œ Check in]
     row 2  [New] [Ã¢â€”Â Booked]                [$] [edit] [x]

   The four things a booked slot does not need on a phone - the time, the
   patient code, the number as text and the fee - are simply not printed. They
   all remain in the table above, and the fee still opens the payment dialog,
   so the card shows less without the record having lost anything.

   Every control is a visible icon or a labelled button; there is no menu, so
   nothing on this card is a second tap away.
   ========================================================================== */
@media (max-width: 1023px) {
    .appt-cards--appointed {
        background: #fff;
        border-radius: 12px;
        overflow: hidden;
    }
    .appt-cards--appointed .patient-card {
        display: flex;
        flex-direction: column;
        gap: 4px;
        padding: 6px 12px;
        background: #fff;
        border-bottom: 1px solid #e5e7eb;
    }
    .appt-cards--appointed .patient-card:last-child { border-bottom: 0; }
    .appt-cards--appointed .patient-card:active { background: #f3f4f6; }

    /* ---- row 1: who, how old, call them, check them in ---- */
    .appt-cards--appointed .patient-card__row1 {
        display: flex;
        align-items: center;
        gap: 8px;
        height: 30px;
    }
    .appt-cards--appointed .patient-card__left {
        flex: 1;
        min-width: 0;
        display: flex;
        align-items: center;
        gap: 6px;
    }
    .appt-cards--appointed .patient-card__name {
        font-size: 15px;
        font-weight: 600;
        color: #0f172a;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        min-width: 0;
    }
    /* "56y Ã‚Â· Male" is short and never truncated: it is the only thing telling
       you whether this is the right patient. */
    .appt-cards--appointed .patient-card__sub {
        display: flex;
        align-items: center;
        gap: 4px;
        flex-shrink: 0;
        font-size: 12px;
        font-weight: 400;
        color: #6b7280;
        white-space: nowrap;
    }
    .appt-cards--appointed .patient-card__sep { color: #c3c8ce; }

    /* ---- row 2: type, status, then the three actions ---- */
    .appt-cards--appointed .patient-card__row2 {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 8px;
    }
    .appt-cards--appointed .patient-card__tags {
        flex: 1;
        min-width: 0;
        display: flex;
        gap: 6px;
        overflow: hidden;
        white-space: nowrap;
    }
    .appt-cards--appointed .patient-card__tools {
        flex-shrink: 0;
        display: flex;
        align-items: center;
        gap: 8px;
    }

    /* ---- the one labelled control ---- */
    .appt-cards--appointed .patient-card__checkin {
        appearance: none;
        -webkit-appearance: none;
        flex-shrink: 0;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: 4px;
        height: 30px;
        min-height: 30px;
        padding: 0 12px;
        margin: 0;
        border: 0;
        border-radius: 8px;
        background: #0f5c4d;
        color: #fff;
        font: inherit;
        font-size: 13px;
        font-weight: 600;
        line-height: 1;
        white-space: nowrap;
        cursor: pointer;
        transition: background var(--dur) var(--ease);
    }
    .appt-cards--appointed .patient-card__checkin:active { background: #0b4639; }
    .appt-cards--appointed .patient-card__checkin .icon {
        width: 14px;
        height: 14px;
        flex: 0 0 auto;
    }

    /* ---- icon buttons: call, payment, edit, cancel ----
       A full reset first: an unreset <a> or <button> on a phone keeps its UA
       border, background and padding, which is what makes these look broken. */
    .appt-cards--appointed .patient-card__icon {
        appearance: none;
        -webkit-appearance: none;
        flex-shrink: 0;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 28px;
        height: 28px;
        padding: 0;
        margin: 0;
        border: 0;
        border-radius: 8px;
        font: inherit;
        line-height: 0;
        cursor: pointer;
        text-decoration: none;
        transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
    }
    /* icon() writes the size as SVG attributes, so CSS has to win over them. */
    .appt-cards--appointed .patient-card__icon .icon {
        width: 16px;
        height: 16px;
        flex: 0 0 auto;
    }
    .appt-cards--appointed .patient-card__icon:focus-visible,
    .appt-cards--appointed .patient-card__checkin:focus-visible {
        outline: 2px solid #0f5c4d;
        outline-offset: 2px;
    }
    .appt-cards--appointed .patient-card__icon--call {
        background: #e8f5f1;
        color: #0f5c4d;
    }
    .appt-cards--appointed .patient-card__icon--call:active { background: #d7ece5; }
    .appt-cards--appointed .patient-card__icon--pay {
        background: #e8f5f1;
        color: #0f5c4d;
    }
    .appt-cards--appointed .patient-card__icon--pay:active { background: #d7ece5; }
    /* Money in and nothing owing. */
    .appt-cards--appointed .patient-card__icon--pay.is-paid {
        background: #dcfce7;
        color: #16a34a;
    }
    .appt-cards--appointed .patient-card__icon--pay.is-paid:active { background: #bbf7d0; }
    .appt-cards--appointed .patient-card__icon--edit {
        background: #f1f5f9;
        color: #475569;
    }
    .appt-cards--appointed .patient-card__icon--edit:active { background: #e2e8f0; }
    .appt-cards--appointed .patient-card__icon--cancel {
        background: #fef2f2;
        color: #dc2626;
    }
    .appt-cards--appointed .patient-card__icon--cancel:active { background: #fee2e2; }

    /* ---- the two chips ---- */
    .appt-cards--appointed .patient-card__chip {
        flex-shrink: 0;
        display: inline-flex;
        align-items: center;
        gap: 4px;
        padding: 2px 8px;
        border-radius: 999px;
        font-size: 11px;
        font-weight: 600;
        line-height: 1.4;
        white-space: nowrap;
    }
    .appt-cards--appointed .patient-card__chip--type {
        background: #f1f5f9;
        color: #475569;
    }
    .appt-cards--appointed .patient-card__chip--status {
        background: #eff6ff;
        color: #1d4ed8;
    }
    .appt-cards--appointed .patient-card__dot {
        width: 6px;
        height: 6px;
        border-radius: 999px;
        background: #3b82f6;
        flex: 0 0 auto;
    }
}
   /* ==========================================================================
/* ==========================================================================
   ONE-LINE BOARDS - COMPLETED and IN ROOM (PHONE ONLY, <= 640px)
   --------------------------------------------------------------------------
   Two boards read the same way on a phone - one line per patient, nothing to
   work on - so they share one layout under .patient-list-one and differ only
   in what sits on the right:

     completed   [A-002] Rafiqul  25y [New] (Doctor)          [Rx] [view]
     in room     [A-003] Karim   53y · M [New]          [▶ Visit] [x]

   Same .patient-card / .patient-card__row1 and the same icon-button reset as
   every other board here. What is deliberately not on either card - the
   patient code, the time, the status chip, the token-print icon, the
   checkbox - all stays in the table above.
   ========================================================================== */
@media (max-width: 1023px) {
    .patient-list-one {
        background: #fff;
        border-radius: 12px;
        overflow: hidden;
    }
    .patient-list-one .patient-card {
        display: flex;
        align-items: center;
        gap: 8px;
        padding: 8px 12px;
        min-height: 46px;
        background: #fff;
        border-bottom: 1px solid #e5e7eb;
    }
    .patient-list-one .patient-card:last-child { border-bottom: 0; }
    .patient-list-one .patient-card:active { background: #f3f4f6; }
    /* A doctor-sourced row gets the teal edge the desktop table gives it. An
       inset border rather than border-left, so the row does not shift 3px. */
    .patient-list-one .patient-card.is-doctor {
        box-shadow: inset 3px 0 0 #0f5c4d;
    }

    .patient-list-one .patient-card__row1 {
        display: flex;
        align-items: center;
        gap: 8px;
        width: 100%;
        min-width: 0;
    }

    /* ---- left: everything that identifies the row ---- */
    .patient-list-one .patient-card__left {
        flex: 1;
        min-width: 0;
        display: flex;
        align-items: center;
        gap: 6px;
        overflow: hidden;
    }
    .patient-list-one .patient-card__token {
        flex-shrink: 0;
        padding: 3px 8px;
        border-radius: 999px;
        background: #e8f5f1;
        color: #0f5c4d;
        font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
        font-size: 12px;
        font-weight: 700;
        line-height: 1.2;
        white-space: nowrap;
    }
    /* The one element allowed to give way: the name ellipsises, nothing else. */
    .patient-list-one .patient-card__name {
        min-width: 0;
        font-size: 15px;
        font-weight: 600;
        color: #0f172a;
        text-decoration: none;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    /* "25y" on its own, or "53y · Male" when the board shows the sex too. */
    .patient-list-one .patient-card__age {
        flex-shrink: 0;
        font-size: 12px;
        font-weight: 400;
        color: #6b7280;
        white-space: nowrap;
    }
    .patient-list-one .patient-card__sub {
        display: flex;
        align-items: center;
        gap: 4px;
        flex-shrink: 0;
        font-size: 12px;
        font-weight: 400;
        color: #6b7280;
        white-space: nowrap;
    }
    .patient-list-one .patient-card__sep { color: #c3c8ce; }
    .patient-list-one .patient-card__chip--type {
        flex-shrink: 0;
        display: inline-flex;
        align-items: center;
        padding: 2px 8px;
        border-radius: 999px;
        background: #f1f5f9;
        color: #475569;
        font-size: 11px;
        font-weight: 600;
        line-height: 1.4;
        white-space: nowrap;
    }
    /* The cancelled board carries its status on the right of the row, where the
       other two carry their buttons. */
    .patient-list-one .patient-card__chip--status {
        flex-shrink: 0;
        display: inline-flex;
        align-items: center;
        gap: 4px;
        padding: 2px 8px;
        border-radius: 999px;
        background: #eff6ff;
        color: #1d4ed8;
        font-size: 11px;
        font-weight: 600;
        line-height: 1.4;
        white-space: nowrap;
    }
    .patient-list-one .patient-card__dot {
        width: 6px;
        height: 6px;
        border-radius: 999px;
        background: #3b82f6;
        flex: 0 0 auto;
    }
    /* The word "Doctor", never the doctor's name: the name repeats on every
       row of a one-doctor day and left the patient's own name no room. */
    .patient-list-one .patient-card__doc {
        flex-shrink: 0;
        padding: 2px 6px;
        border-radius: 999px;
        background: #d1ede5;
        color: #0f5c4d;
        font-size: 10px;
        font-weight: 600;
        line-height: 1.4;
        white-space: nowrap;
    }

    /* ---- right: the controls, never a menu ---- */
    .patient-list-one .patient-card__tools {
        flex-shrink: 0;
        display: flex;
        align-items: center;
        gap: 6px;
    }
    /* One reset for every right-hand control on both boards. */
    .patient-list-one .patient-card__print,
    .patient-list-one .patient-card__visit,
    .patient-list-one .patient-card__icon {
        appearance: none;
        -webkit-appearance: none;
        flex-shrink: 0;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        border: 0;
        border-radius: 8px;
        font: inherit;
        text-decoration: none;
        line-height: 0;
        cursor: pointer;
        transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
    }
    .patient-list-one .patient-card__print:focus-visible,
    .patient-list-one .patient-card__visit:focus-visible,
    .patient-list-one .patient-card__icon:focus-visible {
        outline: 2px solid #0f5c4d;
        outline-offset: 2px;
    }
    .patient-list-one .patient-card__print .icon,
    .patient-list-one .patient-card__visit .icon,
    .patient-list-one .patient-card__icon .icon {
        width: 16px;
        height: 16px;
        flex: 0 0 auto;
    }
    .patient-list-one .patient-card__icon {
        width: 30px;
        height: 30px;
        padding: 0;
    }

    /* ---- completed board: Rx (print) and view ---- */
    .patient-list-one .patient-card__print {
        gap: 4px;
        height: 30px;
        padding: 0 10px;
        background: #0f5c4d;
        color: #fff;
        font-size: 13px;
        font-weight: 600;
        line-height: 1;
        white-space: nowrap;
    }
    .patient-list-one .patient-card__print:active { background: #0b4639; }
    .patient-list-one .patient-card__print .icon { width: 14px; height: 14px; }
    .patient-list-one .patient-card__icon--view {
        background: #f1f5f9;
        color: #475569;
    }
    .patient-list-one .patient-card__icon--view:active { background: #e2e8f0; }

    /* ---- in-room board: Visit and the one cancel ---- */
    .patient-list-one .patient-card__visit {
        gap: 4px;
        height: 30px;
        padding: 0 10px;
        background: #0f5c4d;
        color: #fff;
        font-size: 13px;
        font-weight: 600;
        line-height: 1;
        white-space: nowrap;
    }
    .patient-list-one .patient-card__visit:active { background: #0b4639; }
    .patient-list-one .patient-card__visit .icon { width: 14px; height: 14px; }
    /* Cancel stays red on a red wash so it never reads as a neutral action. */
    .patient-list-one .patient-card__icon--cancel {
        background: #fef2f2;
        color: #dc2626;
    }
    .patient-list-one .patient-card__icon--cancel:active { background: #fee2e2; }
    /* The visit-cancel form is only a wrapper here - the button is the control. */
    .patient-list-one .patient-card__tools form { display: inline-flex; }
}

/* ==========================================================================
   SMALLER PHONES (<= 360px)

/* The doctor badge is the word "Doctor", not the doctor's name, so it is small
   enough to keep on every width - it only appears on rows whose appointment
   came from the doctor. Only the name is ever allowed to truncate. */

/* "Male" becomes "M" a little earlier on the in-room board than it does
   elsewhere: that row already carries the age, the type chip and the doctor
   badge, and the full word was squeezing the patient's name to three letters.
   The doctor badge stays - it says something the row's other chips do not. */
@media (max-width: 440px) {
    .appt-cards--called .patient-card__gender { font-size: 0; }
    .appt-cards--called .patient-card__gender::after {
        content: attr(data-short);
        font-size: 12px;
    }
    .appt-cards--called .patient-card__token { padding-left: 6px; padding-right: 6px; }
}

/* ==========================================================================
   TABLET CARD POLISH - 641px to 1023px
   --------------------------------------------------------------------------
   Same cards as a phone, only bigger. This band sits after the <=1023px card
   block on purpose: both scopes carry the same specificity, so the later one
   wins, and this one has to be the later one.

   The tablet also gets the facts a phone has to drop - the patient code and
   the full word for the sex - because there is room for them. Both elements
   are always in the markup, so nothing shifts when this turns on.
   ========================================================================== */
@media (min-width: 641px) and (max-width: 1023px) {
    .patient-list-one .patient-card { padding: 10px 16px; min-height: 54px; }
    .patient-list-one .patient-card__tools { gap: 8px; }
    .patient-list-one .patient-card__icon { width: 32px; height: 32px; }
    .patient-list-one .patient-card__print,
    .patient-list-one .patient-card__visit {
        height: 32px;
        padding: 0 12px;
    }
    .appt-cards--appointed .patient-card { padding: 10px 16px; }
    .appt-cards--appointed .patient-card__tools { gap: 8px; }
    .appt-cards--appointed .patient-card__icon { width: 32px; height: 32px; }
    .appt-cards--appointed .patient-card__checkin {
        height: 32px;
        min-height: 32px;
        padding: 0 16px;
    }
    .appt-cards--appointed .patient-card__checkin .icon { width: 15px; height: 15px; }
    /* Only the icon button is square. Visit and Rx carry a word, so they grow to
       fit it - pinning their width to 32px clips the label. */
    .appt-cards--called .patient-card__icon { width: 32px; height: 32px; }
    .appt-cards--called .patient-card__visit { height: 32px; padding: 0 12px; }
    .appt-cards--waiting .appt-card { padding: 6px 16px; }
    .appt-cards--waiting .appt-card__tool,
    .appt-cards--waiting .appt-cards .appt-card__tool {
        width: 32px;
        height: 32px;
        min-height: 32px;
    }
    .appt-cards--report .appt-card { padding: 8px 16px; }
    .appt-cards--report .appt-card__visit,
    .appt-cards--report .appt-card__menu-btn,
    .appt-cards--report .appt-card__actions .btn--icon {
        width: 32px;
        height: 32px;
        min-height: 32px;
    }

    .appt-page .patient-card__code {
        display: inline-flex;
        font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
        font-size: 11px;
        color: #94a3b8;
        flex: 0 0 auto;
    }
    /* "Male", not "M" - there is width for the whole word on a tablet. */
    .appt-page .patient-card__gender::after { content: none; }
    .appt-page .appt-card__gender::after { content: none; }
}

/* ==========================================================================
   SMALLER PHONES (<= 360px)
   --------------------------------------------------------------------------
   Four action buttons plus a readable name do not fit 360px comfortably, so the
   chrome shrinks rather than wrapping. The type and doctor chips are the first
   things to go - both are informational, and the age and status stay.
   ========================================================================== */
@media (max-width: 360px) {
    /* On the narrowest phones the report row keeps only the coloured dot of
       the status chip - green or amber already says what the word said - so
       the patient name has room to be read rather than guessed at. */
    .appt-cards--report .appt-card__chip-text::after { content: none; }
    .appt-cards--report .appt-card__chip--status { padding-left: 6px; padding-right: 6px; }
    /* Every gap and every pixel of padding on this row goes back to the name,
       which is the one thing that has to stay readable. */
    .appt-cards--report .appt-card { gap: 6px; padding-left: 9px; padding-right: 9px; }
    .appt-cards--report .appt-card__actions { gap: 5px; }
    .appt-cards--report .appt-card__token { padding-left: 5px; padding-right: 5px; font-size: 11px; }

    /* Same selectors as the 640px rules above, so these win on source order
       rather than on specificity. */
  .appt-cards--waiting .appt-card {
          column-gap: 8px;
          row-gap: 2px;
          padding: 3px 10px;
      }
    .appt-card__tools { gap: 6px; }
    .appt-card__tool,
    .appt-card__tools .appt-card__tool {
        width: 30px;
        height: 30px;
        min-height: 30px;
        border-radius: 8px;
    }
    .appt-card__tool .icon { width: 16px; height: 16px; }
    .appt-card__visit { height: 30px; min-height: 30px; padding: 0 10px; }
    /* The appointment type is the least useful word on this row once space is
       tight; the status chip and the age carry more. */
    .appt-card__chip--type,
    .appt-card__chip--doc { display: none; }
    /* "Male"/"Female" becomes "M"/"F"; the full word stays in the markup so a
       screen reader still announces it in full. */
    .appt-card__gender { font-size: 0; }
    .appt-card__gender::after {
        content: attr(data-short);
        font-size: 12px;
    }
    /* The name keeps as much width as possible: the age and the NEXT pill give
       up first, since the teal edge already marks the next patient. */
    .appt-card__age { display: none; }

    /* ---- appointed board on a 320px phone ----
       The card is already spare: no time, no number, no fee. What is left is
       the name, "56y Ã‚Â· Male", the four 26px icon buttons and the Check in, and
       at 320px that no longer fits on one row with Check in spelled out - so
       Check in becomes the check icon alone, the type chip goes (the patient's
       record already says it), and the gender shortens to M or F. */
    .appt-cards--appointed .patient-card { padding: 6px 10px; }
    .appt-cards--appointed .patient-card__chip--type { display: none; }
    .appt-cards--appointed .patient-card__icon {
        width: 26px;
        height: 26px;
        border-radius: 8px;
    }
    .appt-cards--appointed .patient-card__icon .icon { width: 15px; height: 15px; }
    .appt-cards--appointed .patient-card__tools { gap: 6px; }
    /* "Male" becomes "M"; the full word stays in the markup so a screen reader
       still announces it in full. */
    .appt-cards--appointed .patient-card__gender { font-size: 0; }
    .appt-cards--appointed .patient-card__gender::after {
        content: attr(data-short);
        font-size: 12px;
    }
    .appt-cards--appointed .patient-card__checkin {
        width: 26px;
        padding: 0;
        gap: 0;
    }
    .appt-cards--appointed .patient-card__checkin .icon { width: 15px; height: 15px; }
    .appt-cards--appointed .patient-card__checkin-text { display: none; }

    /* ---- completed board on a 320px phone ----
       One line, and the line has to hold a token, a name, an age, two chips
       and two controls. Print drops its word first, then the type chip, then
       the doctor tag - in that order, so the name keeps as much room as it
       can and is the last thing to be squeezed. */
    /* ---- the one-line boards on a 320px phone ----
       One line has to hold a token, a name, some meta, a chip and two
       controls. Each labelled button drops its word first, then the type chip,
       then the sex ("Male" becomes "M"), so the name keeps as much room as it
       can and is the last thing to be squeezed. */
    .patient-list-one .patient-card { padding: 8px 10px; }
    .patient-list-one .patient-card__tools { gap: 4px; }
    .patient-list-one .patient-card__print,
    .patient-list-one .patient-card__visit {
        width: 28px;
        height: 28px;
        padding: 0;
        gap: 0;
    }
    .patient-list-one .patient-card__print .icon,
    .patient-list-one .patient-card__visit .icon { width: 15px; height: 15px; }
    .patient-list-one .patient-card__print-text,
    .patient-list-one .patient-card__visit-text { display: none; }
    .patient-list-one .patient-card__icon { width: 28px; height: 28px; }
    .patient-list-one .patient-card__icon .icon { width: 15px; height: 15px; }
    .patient-list-one .patient-card__chip--type { display: none; }
    /* "Male" becomes "M"; the full word stays in the markup so a screen reader
       still announces it in full. */
    .patient-list-one .patient-card__gender { font-size: 0; }
    .patient-list-one .patient-card__gender::after {
        content: attr(data-short);
        font-size: 12px;
    }
}
/* ==========================================================================
   TODAY'S COLLECTION - PHONE ONLY (<= 767px)
   --------------------------------------------------------------------------
   Each patient is two lines:

     line 1   patient name          fee  [type]
     line 2   patient code Ã‚Â· phone  disc Collected amount

   Fee/discount/collected are NOT stacked rows. Discount shows only when it
   exists (has-disc); the phone dash hides. Desktop keeps the full table.
   ========================================================================== */
@media (max-width: 767px) {
    .collect-card .card__head { padding: 7px 10px; }
    .collect-card .card__head .card__title { font-size: 14px; }
    .collect-card .card__body { padding: 0; }

    .collect-table tbody tr {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto auto auto;
        align-items: center;
        column-gap: 7px;
        row-gap: 1px;
        padding: 7px 10px;
        border-bottom: 1px solid var(--line-soft);
        border-radius: 0;
    }
    .collect-table tbody td {
        padding: 0;
        border-bottom: 0;
        text-align: left;
        min-width: 0;
    }
    .collect-table tbody td::before { content: none; }

    /* Name cell dissolves: name rides line 1, the code rides line 2. */
    .collect-table tbody td.cell-title { display: contents; }
    .collect-table tbody td.cell-title .cell-strong {
        grid-column: 1;
        grid-row: 1;
        min-width: 0;
        font-size: 14.5px;
        font-weight: 700;
        line-height: 1.3;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .collect-table tbody td.cell-title .faint.small {
        grid-column: 1;
        grid-row: 2;
        min-width: 0;
        font-size: 11px;
        font-weight: 400;
        line-height: 1.35;
        color: var(--muted);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    /* Line 1: fee, then the short type badge. */
    .collect-table tbody td.cell-cfee {
        grid-column: 2;
        grid-row: 1;
        font-size: 13px;
        font-weight: 600;
        white-space: nowrap;
    }
    .collect-table tbody td.cell-ctype { grid-column: 3; grid-row: 1; }
    .collect-table tbody td.cell-ctype .ctype-full { display: none; }
    .collect-table tbody td.cell-ctype .ctype-short {
        display: inline-block;
        font-size: 9px;
        padding: 1px 5px;
        white-space: nowrap;
    }

    /* Line 2: phone, discount (only when it exists), collected. */
    .collect-table tbody td.cell-cphone {
        grid-column: 2;
        grid-row: 2;
        font-size: 11px;
        line-height: 1.35;
        white-space: nowrap;
    }
    .collect-table tbody td.cell-cphone a { color: var(--muted); text-decoration: none; }
    .collect-table tbody td.cell-cphone .faint { display: none; }
    .collect-table tbody td.cell-cdisc { display: none; }
    .collect-table tbody td.cell-cdisc.has-disc {
        display: block;
        grid-column: 3;
        grid-row: 2;
        font-size: 10px;
        line-height: 1.35;
        color: var(--muted);
        white-space: nowrap;
    }
    .collect-table tbody td.cell-cdisc.has-disc .cdisc-label { display: inline; }
    .collect-table tbody td.cell-ccol {
        grid-column: 4;
        grid-row: 2;
        justify-self: end;
        font-size: 13px;
        font-weight: 700;
        line-height: 1.35;
        white-space: nowrap;
    }
    .collect-table tbody td.cell-ccol .ccol-label {
        display: inline;
        font-size: 10px;
        font-weight: 400;
        color: var(--muted);
        margin-right: 3px;
    }
}
/* ==========================================================================
   BILLING LIST - PHONE ONLY (<= 767px)
   --------------------------------------------------------------------------
   Each payment is two lines:

     line 1   patient name          paid  [method/status]  [receipt]
     line 2   receipt - date        due*                  [x/refund]

   Total hides on phones (the receipt page carries the breakdown); due shows
   only when the row owes (has-due). Desktop keeps the full table.
   ========================================================================== */
@media (max-width: 767px) {
    .bill-card .card__head {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 6px;
        padding: 8px 10px;
    }
    .bill-card .card__head .card__title { font-size: 15px; }
    .bill-card .card__head .card__tools {
        display: flex;
        align-items: center;
        flex-wrap: wrap;
        gap: 6px;
        margin-left: auto;
    }
    .bill-card .card__head .card__tools .btn {
        min-height: 32px;
        padding: 0 8px;
        font-size: 12px;
        white-space: nowrap;
    }
    .bill-card .card__body { padding: 0; }
    .bill-card .filters { padding: 6px 10px; gap: 5px 6px; }
    .bill-card .filters .field { flex: 1 1 46%; min-width: 0; }
    .bill-card .filters .field--wide { flex: 1 1 46%; }
    .bill-card .filters .field__label { margin-bottom: 1px; font-size: 10px; }
    /* 16px text is kept on purpose: anything smaller makes iOS zoom on focus. */
    .bill-card .filters .input,
    .bill-card .filters .select { padding: 5px 8px; }
    .bill-card .filters__actions { flex: 1 1 46%; gap: 4px; margin-left: 0; }
    .bill-card .filters__actions .btn {
        min-height: 32px;
        padding: 0 8px;
        font-size: 12px;
    }
    .bill-card .pagination { padding: 5px 10px; font-size: 11px; gap: 6px; }
    .bill-card .pagination__info { line-height: 1.3; }

    .bill-table tbody tr {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto auto auto;
        align-items: center;
        column-gap: 6px;
        row-gap: 1px;
        padding: 6px 10px;
        border-bottom: 1px solid var(--line-soft);
        border-radius: 0;
    }
    .bill-table tbody td {
        padding: 0;
        border-bottom: 0;
        text-align: left;
        min-width: 0;
    }
    .bill-table tbody td::before { content: none; }

    /* Patient name rides line 1; the code stays on desktop. */
    .bill-table tbody td.cell-bpatient { display: contents; }
    .bill-table tbody td.cell-bpatient .cell-strong {
        grid-column: 1;
        grid-row: 1;
        min-width: 0;
        font-size: 14.5px;
        font-weight: 700;
        line-height: 1.3;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .bill-table tbody td.cell-bpatient .faint.small { display: none; }

    /* Line 1: paid, then the method/status badges as one inline group. */
    .bill-table tbody td.cell-bpaid {
        grid-column: 2;
        grid-row: 1;
        font-size: 13px;
        font-weight: 700;
        white-space: nowrap;
    }
    .bill-table tbody td.cell-bmethod { grid-column: 3; grid-row: 1; }
    .bill-table tbody td.cell-bmethod .badge {
        font-size: 9px;
        padding: 1px 5px;
        white-space: nowrap;
    }

    /* Line 2: receipt, date, due (only when owed). Total stays desktop-only. */
    .bill-table tbody td.cell-btotal { display: none; }
    .bill-table tbody td.cell-breceipt {
        grid-column: 1;
        grid-row: 2;
        min-width: 0;
        font-size: 11px;
        color: var(--muted);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .bill-table tbody td.cell-bdate {
        grid-column: 2;
        grid-row: 2;
        font-size: 11px;
        line-height: 1.35;
        color: var(--muted);
        white-space: nowrap;
    }
    .bill-table tbody td.cell-bdate .faint.small { display: none; }
    .bill-table tbody td.cell-bdue { display: none; }
    .bill-table tbody tr.has-due td.cell-bdue {
        display: block;
        grid-column: 3;
        grid-row: 2;
        font-size: 11px;
        white-space: nowrap;
    }
    .bill-table tbody td.cell-bdue .bdue-label {
        display: inline;
        font-size: 10px;
        color: var(--muted);
        margin-right: 3px;
    }

    /* Actions span both lines on the right; text buttons become icons
       (their labels stay in the DOM with aria-labels for screen readers). */
    .bill-table tbody td.cell-actions {
        grid-column: 4;
        grid-row: 1 / span 2;
        justify-self: end;
        align-self: center;
        display: flex;
        align-items: center;
        gap: 4px;
    }
    .bill-table tbody td.cell-actions .btn {
        min-height: 30px;
        min-width: 30px;
        padding: 0 7px;
        font-size: 0;
        gap: 0;
        justify-content: center;
    }
    .bill-table tbody td.cell-actions .btn .icon { width: 15px; height: 15px; }
    .bill-table tbody td.cell-actions .btn-quiet {
        width: 30px;
        height: 30px;
        min-height: 30px;
    }
}
/* ==========================================================================
   EXPENSES LIST - PHONE ONLY (<= 767px)
   --------------------------------------------------------------------------
   Each expense is two lines:

     line 1   description ............ amount  [x]
     line 2   date - category

   Desktop keeps the full table.
   ========================================================================== */
@media (max-width: 767px) {
    .exp-card .card__head {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 6px;
        padding: 8px 10px;
    }
    .exp-card .card__head .card__title { font-size: 15px; }
    .exp-card .card__head .card__tools {
        display: flex;
        align-items: center;
        margin-left: auto;
    }
    .exp-card .card__head .card__tools .badge { font-size: 10.5px; padding: 1px 7px; }
    .exp-card .card__body { padding: 0; }
    .exp-card .filters { padding: 6px 10px; gap: 5px 6px; }
    .exp-card .filters .field { flex: 1 1 46%; min-width: 0; }
    .exp-card .filters .field__label { margin-bottom: 1px; font-size: 10px; }
    /* 16px text is kept on purpose: anything smaller makes iOS zoom on focus. */
    .exp-card .filters .input,
    .exp-card .filters .select { padding: 5px 8px; }
    .exp-card .filters__actions { flex: 1 1 46%; gap: 4px; margin-left: 0; }
    .exp-card .filters__actions .btn {
        min-height: 32px;
        padding: 0 8px;
        font-size: 12px;
    }
.exp-card .pagination { padding: 5px 10px; font-size: 11px; gap: 6px; }
    .exp-card .pagination__info { line-height: 1.3; }

    /* Add-expense form: shorter fields, tighter rhythm (16px text kept). */
    .exp-add .card__head { padding: 8px 10px; }
    .exp-add .card__head .card__title { font-size: 15px; }
    .exp-add .card__body { padding: 9px 12px; }
    .exp-add .card__body .field + .field { margin-top: 7px; }
    .exp-add .field__label { font-size: 10.5px; line-height: 1.2; margin-bottom: 2px; }
    .exp-add .input,
    .exp-add .select { min-height: 38px; padding: 5px 10px; }
    .exp-add input[type="date"] { min-height: 36px; padding-top: 4px; padding-bottom: 4px; }
    .exp-add .card__foot { padding: 9px 12px; }
    .exp-add .card__foot .btn { min-height: 40px; }

    .exp-table tbody tr {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto auto;
        align-items: center;
        column-gap: 6px;
        row-gap: 1px;
        padding: 6px 10px;
        border-bottom: 1px solid var(--line-soft);
        border-radius: 0;
    }
    .exp-table tbody td {
        padding: 0;
        border-bottom: 0;
        text-align: left;
        min-width: 0;
    }
    .exp-table tbody td::before { content: none; }

    /* Line 1: description plus amount; the delete icon spans both lines. */
    .exp-table tbody td.cell-expdesc {
        grid-column: 1;
        grid-row: 1;
        min-width: 0;
        font-size: 14px;
        font-weight: 600;
        line-height: 1.3;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .exp-table tbody td.cell-expamt {
        grid-column: 2;
        grid-row: 1;
        font-size: 13px;
        font-weight: 700;
        white-space: nowrap;
    }
    .exp-table tbody td.cell-actions {
        grid-column: 3;
        grid-row: 1 / span 2;
        justify-self: end;
        align-self: center;
    }
    .exp-table tbody td.cell-actions .btn-quiet {
        width: 30px;
        height: 30px;
        min-height: 30px;
    }

    /* Line 2: date plus category. */
.exp-table tbody td.cell-expdate {
        grid-column: 1;
        grid-row: 2;
        padding: 0;
        border-bottom: 0;
        font-size: 11px;
        font-weight: 400;
        line-height: 1.35;
        color: var(--muted);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .exp-table tbody td.cell-expcat { grid-column: 2; grid-row: 2; }
    .exp-table tbody td.cell-expcat .badge {
        font-size: 9px;
        padding: 1px 5px;
        white-space: nowrap;
    }
}
/* Mobile-only twins (appointment row menu copies). Desktop shows the
   inline originals instead, so desktop layout stays pixel-identical. */
.show-mobile { display: none; }

@media (max-width: 767px) {
    .page:not(.page--flush) { padding-left: 8px; padding-right: 8px; }
    /* The flash wrapper mirrors the content page's side padding so the alert
       lines up with the card below it. */
    .flash-wrap { padding-left: 8px; padding-right: 8px; }
}

/* ======================================================================
   Check-in dialog, phones and small tablets: a centered popup, everything on
   one screen.

   The foot sat below the fold and could not be reached. The form was a
   plain block child of .modal, so .modal__body never became the scroll
   container and the sheet was simply clipped: with seven investigations
   the foot landed ~100px under an 844px viewport. Making the form a
   flex column pins the footer, and the trims below (3-up vitals, 36px
   rows, capped list) keep the whole dialog near 600px.

   The breakpoint is 820px, not 640: a phone in landscape or desktop-site
   mode clears 640 CSS px but still needs the compact dialog (the tall
   two-line rows seen there cannot scroll the foot into view). Desktop
   (min-width 1024px, the table layout) keeps the old modal untouched.
   ====================================================================== */
@media (max-width: 820px) {
    /* Centered popup, not a bottom sheet: the other dialogs keep their
       sheet behaviour, only #checkinModal is pulled out of it. */
    .modal-backdrop#checkinModal { place-items: center; padding: 16px; }
    #checkinModal .modal {
        width: min(440px, 100%);
        max-height: calc(100vh - 32px);
        max-height: calc(100dvh - 32px);
        border: 1px solid var(--line);
        border-radius: var(--radius-lg);
        animation: modal-in .2s var(--ease);
    }
    #checkinModal .modal__head::before { display: none; }
    #checkinModal .modal__head {
        gap: 8px;
        min-height: 44px;
        padding: 0 14px;
    }
    #checkinModal .modal__title { font-size: 15px; font-weight: 600; }
    #checkinModal .modal__head .btn-quiet { width: 32px; height: 32px; padding: 0; }

    #checkinModal #checkinForm {
        display: flex;
        flex-direction: column;
        flex: 1 1 auto;
        min-height: 0;
    }
    #checkinModal .modal__body {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        /* Both scroll levels must answer a finger: the tall order list is
           capped at 190px, and whatever still overflows (small landscape
           screens) scrolls the body. pan-y keeps the page behind still. */
        touch-action: pan-y;
        padding: 10px 14px;
        max-height: calc(100vh - 44px - 56px - 24px);
        max-height: calc(100dvh - 44px - 56px - 24px);
    }
    #checkinModal .modal__foot {
        flex: 0 0 auto;
        gap: 8px;
        height: 56px;
        padding: 8px 14px calc(8px + env(safe-area-inset-bottom, 0px));
    }
    #checkinModal .modal__foot .btn {
        flex: 1 1 0;
        height: 40px;
        padding: 0 14px;
        border-radius: 10px;
        font-size: 14px;
        font-weight: 600;
    }
    #checkinModal .modal__foot .btn--primary { flex: 2 1 0; }

    #checkinModal .modal__patient {
        display: flex;
        align-items: center;
        margin: 0 0 6px;
        font-size: 14px;
        font-weight: 600;
        color: #0f172a;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    /* Five vitals fit two rows instead of three. */
    #checkinModal .form-grid--2 { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
    #checkinModal .field__label {
        font-size: 10px;
        font-weight: 600;
        line-height: 12px;
        margin-bottom: 2px;
        text-transform: uppercase;
        letter-spacing: .04em;
        color: #64748b;
    }
    #checkinModal .ci-unit {
        font-weight: 500;
        text-transform: none;
        letter-spacing: 0;
        color: #94a3b8;
    }
    /* 14px floor: below that iOS zooms the page when a field takes focus. */
    #checkinModal .input {
        height: 34px;
        padding: 0 8px;
        border-radius: 8px;
        font-size: 14px;
        border-color: #cfd8d6;
    }
    #checkinModal .field__hint {
        margin: 6px 0 0;
        font-size: 11px;
        line-height: 14px;
        color: #64748b;
    }

    #checkinModal .ci-sec__head {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 8px;
        min-height: 28px;
        margin: 10px 0 6px;
    }
    #checkinModal .ci-sec__label {
        font-size: 11px;
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: .04em;
        color: #64748b;
    }
    #checkinModal .ci-sec__count { color: #0f5c4d; }
    #checkinModal .ci-sec__head .btn {
        height: 28px;
        margin: 0;
        padding: 0 10px;
        border: 1px solid transparent;
        border-radius: 8px;
        background: #e8f5f1;
        color: #0f5c4d;
        font-size: 12px;
        font-weight: 600;
        white-space: nowrap;
    }

    /* One slim line per test: name, result, remove. */
    #checkinModal .inv-list {
        display: flex;
        flex-direction: column;
        flex-wrap: nowrap;
        gap: 4px;
        margin-bottom: 6px;
    }
    #checkinModal .inv-list--scroll {
        max-height: 190px;
        overflow-y: auto;
        overscroll-behavior: contain;
        touch-action: pan-y;
    }
    #checkinModal .inv-list--scroll.ci-fade {
        -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 18px), transparent);
        mask-image: linear-gradient(to bottom, #000 calc(100% - 18px), transparent);
    }
    #checkinModal .inv-row {
        display: flex;
        flex: 0 0 auto;
        align-items: center;
        gap: 8px;
        height: 36px;
        padding: 0 8px 0 10px;
        border-radius: 8px;
        border-color: transparent;
        background: #e8f5f1;
    }
    #checkinModal .inv-row__head { display: contents; }
    #checkinModal .inv-row__name {
        flex: 1 1 auto;
        min-width: 0;
        font-size: 13px;
        font-weight: 500;
        color: #0f3d33;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    /* The tag only ever appears on the prescription picker, never here, and
       it would eat the name's width. */
    #checkinModal .inv-row__head .badge { display: none; }
    #checkinModal .inv-row .input {
        flex: 0 0 auto;
        width: 96px;
        height: 28px;
        padding: 0 8px;
        border-radius: 6px;
        /* 13px was specified for this one field, but the global iOS floor is
           14px: "Result" still fits the 96px box at 14px. */
        font-size: 14px;
    }
    #checkinModal .inv-chip__x {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        flex: 0 0 auto;
        position: relative;
        width: 24px;
        height: 24px;
        padding: 0;
        border: 0;
        background: transparent;
        color: #64748b;
        font-size: 14px;
        appearance: none;
    }
    /* 24px glyph, 32px tap target. */
    #checkinModal .inv-chip__x::after { content: ""; position: absolute; inset: -4px; }

    #checkinModal .input-group.ac { gap: 8px; margin-top: 6px; }
    #checkinModal .input-group.ac .input { flex: 1 1 auto; min-width: 0; }
    #checkinModal #ciInvAdd {
        height: 34px;
        padding: 0 14px;
        border-radius: 8px;
        font-size: 13px;
        font-weight: 600;
    }
}

@media (max-width: 360px) {
    /* Three 1fr columns squeeze the BP sample out of view; two is honest. */
    #checkinModal .form-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}


