  :root {
    /* D-4 (Kartik, 2026-10-03): the phone's own font, nothing downloaded.
       Inter first so a phone that already has it installed uses it; then San
       Francisco on iPhone, Segoe UI on Windows, Roboto on Android, Noto Sans
       elsewhere. The downloaded Inter it replaces held up every first open by
       ~1.1 s (the inline script in index.html waits for every stylesheet). */
    --font: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans", "Helvetica Neue", Arial, sans-serif;
    --bg: #f5f6f8;
    --surface: #ffffff;
    --surface2: #f0f1f4;
    --border: #e4e6eb;
    --border2: #d0d3da;
    --text: #1a1d23;
    /* Was #6b7280: 4.47:1 on --bg, a hair under the 4.5:1 AA floor, and 4.28 on
       --surface2 (2026-10-07). #666d7a clears all three: --bg 4.81, --surface
       5.21, --surface2 4.61. The eye cannot tell them apart. */
    --muted: #666d7a;
    --accent: #2563eb;
    --accent-dim: rgba(37,99,235,0.08);
    --accent-dark: #1d4ed8;
    --danger: #dc2626;
    --danger-dim: rgba(220,38,38,0.09);
    /* Small red text. --danger is 4.19:1 on its own 9% fill; this is 4.98. */
    --danger-dark: #c81e1e;
    --success: #16a34a;
    --success-dim: rgba(22,163,74,0.10);
    /* Small green text. --success is 2.96:1 on its own 10% fill; this is 4.73. */
    --success-dark: #167c3a;
    --warning: #d97706;
    --warning-dim: rgba(217,119,6,0.10);
    --warning-dark: #b45309;
    --violet: #7c3aed;
    --violet-dim: rgba(124,58,237,0.10);
    /* Approved leave (2026-09-28). Teal, because violet is holiday and blue is
       the accent. #0e7490 clears 4.5:1 on its own 10% fill. */
    --leave: #0e7490;
    --leave-dim: rgba(14,116,144,0.10);
    /* D9c: was #9ca3af — 2.35:1 on --bg and 2.54:1 on a card, well under the
       4.5:1 WCAG AA needs for normal text, and this is used at ~11.5px for
       real information (the punch-refusal hints, report footnotes). #676d76
       clears 4.5:1 on all three surfaces it actually appears on: --bg 4.82,
       --surface 5.22, --surface2 4.62.
       It ends up close to --muted, and that is the honest outcome: a colour
       light enough to be visibly "fainter" than --muted is a colour too light
       to read. The hierarchy is carried by size and weight instead. */
    --faint: #676d76;
    --radius: 14px;
    --radius-sm: 10px;
    --shadow: 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.04);
    --shadow-md: 0 4px 12px rgba(0,0,0,0.08), 0 2px 4px rgba(0,0,0,0.04);
  }

  * { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; }

  /* Inside the Android app, press-and-hold on text opened the browser's Copy /
     Share / Select all / Read aloud menu — which read as the app's own
     long-press (Kartik, 2026-09-16). Off there; fields keep it, since typing and
     editing need it. html.in-app is set by js/main.js; a browser is untouched. */
  html.in-app body { -webkit-user-select: none; user-select: none; }
  html.in-app input, html.in-app textarea { -webkit-user-select: text; user-select: text; }

  body {
    font-family: var(--font);
    accent-color: var(--accent);
    background: var(--bg);
    color: var(--text);
    /* These two lines are the whole browser floor. An engine that does not know
       dvh drops the declaration entirely and the layout collapses — on exactly
       the un-updated Android WebView this app targets. The fallback first, the
       override second, takes the floor from Chrome 108 to Chrome 88. */
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    overflow-x: hidden;
  }

  .screen {
    display: none;
    width: 100%;
    max-width: 420px;
    min-height: 100vh;
    min-height: 100dvh;
    /* 16px sides, not 20. On a 390pt iPhone the old pair cost 40px of a 390px
       screen, and the owner/master dashboards paid it twice — `.wide-content`
       below used to add another 16 on top, so those screens ran at a 36px
       gutter while the employee dashboard ran at 20. One number now: every
       screen, every role, the same edge. */
    padding: 24px 16px 32px;
    flex-direction: column;
  }
  .screen.active { display: flex; }

  /* D3. These two values were inline on #owner-content and #master-content in
     index.html, where a stylesheet could not override them. .screen already
     caps at 420px, and 640 is bigger than that, so the max-width has never
     been the binding constraint on a phone. Moving it here is what lets the
     desktop query below exist. */
  /* `width: 100%` is load-bearing and was missing. `.screen` is a flex column,
   * and `margin: 0 auto` on a flex item overrides `align-items: stretch` in the
   * cross axis — so this box sized to fit-content and centred itself instead of
   * filling its track. On desktop that meant the owner dashboard rendered 703px
   * wide inside the 1100px it declares, and the 2fr/1fr tracks resolved to
   * 434/217 rather than 713/357. Measured 2026-07-30.
   *
   * The old D3 test read `max-width` — the declared value, which was always
   * 1100px — so it certified a width the page never actually took.
   *
   * On a phone this changes nothing: `.screen` caps at 420px, the cards inside
   * are block-level and already filled that, and 640 was never the binding
   * constraint there.
   *
   * The side padding is gone. It stacked on top of `.screen`'s, which is the
   * only reason the owner and master dashboards had a wider gutter than every
   * other screen in the app — a difference nobody chose. `.screen` owns the
   * horizontal edge now; this rule owns the width cap and the bottom run-off. */
  .wide-content { width: 100%; max-width: 640px; margin: 0 auto; padding: 0 0 40px; }

  /* ── Logo / header ── */
  .logo {
    font-size: 20px;
    font-weight: 700;
    letter-spacing: -0.5px;
    color: var(--accent);
  }
  .logo span { color: var(--muted); font-weight: 400; }

  .page-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 28px;
  }

  .back-btn {
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--muted);
    padding: 7px 14px;
    border-radius: var(--radius-sm);
    font-family: var(--font);
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.15s;
    box-shadow: var(--shadow);
  }
  .back-btn:hover { color: var(--text); border-color: var(--border2); }

  /* ── Greeting ── */
  .greeting { margin-bottom: 28px; }
  .greeting h1 {
    font-size: 28px;
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: -0.5px;
    color: var(--text);
  }
  .greeting p {
    color: var(--muted);
    font-size: 14px;
    margin-top: 5px;
    font-weight: 400;
  }

  /* ── Employee list ── */
  .emp-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 16px;
    flex: 1;
  }

  /* A real <button> so it is keyboard-focusable and announced to screen
     readers; these resets strip the native button chrome so it still looks like
     the card it always was. width:100% + text-align:left keep the flex layout. */
  .emp-card {
    width: 100%;
    text-align: left;
    font: inherit;
    color: inherit;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 14px 16px;
    display: flex;
    align-items: center;
    gap: 12px;
    cursor: pointer;
    transition: all 0.15s;
    box-shadow: var(--shadow);
  }
  .emp-card:active { transform: scale(0.985); }
  .emp-card:hover { border-color: var(--accent); box-shadow: var(--shadow-md); }
  /* A visible focus ring for keyboard users, suppressed for mouse/touch. */
  .emp-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

  .emp-avatar {
    width: 42px; height: 42px;
    border-radius: 50%;
    background: var(--accent-dim);
    border: 1.5px solid rgba(37,99,235,0.2);
    display: flex; align-items: center; justify-content: center;
    font-size: 15px; font-weight: 600; color: var(--accent);
    flex-shrink: 0;
  }

  .emp-info { flex: 1; min-width: 0; }
  .emp-name { font-size: 14px; font-weight: 600; color: var(--text); }
  .emp-role { font-size: 12px; color: var(--muted); margin-top: 1px; }

  /* ── PIN screen ── */
  .pin-wrap {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }

  .pin-employee {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 32px;
    padding: 16px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
  }

  .pin-dots {
    display: flex;
    gap: 12px;
    justify-content: center;
    margin-bottom: 32px;
  }
  .pin-dot {
    width: 13px; height: 13px;
    border-radius: 50%;
    border: 2px solid var(--border2);
    transition: all 0.15s;
  }
  .pin-dot.filled {
    background: var(--accent);
    border-color: var(--accent);
    transform: scale(1.15);
  }
  .pin-dot.error {
    background: var(--danger);
    border-color: var(--danger);
    animation: shake 0.35s ease;
  }

  @keyframes shake {
    0%,100% { transform: translateX(0); }
    20%      { transform: translateX(-6px); }
    40%      { transform: translateX(6px); }
    60%      { transform: translateX(-4px); }
    80%      { transform: translateX(4px); }
  }

  .numpad {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
  }

  /* Keyboard users get a visible focus ring on every interactive element;
     :focus-visible keeps it off for mouse and touch, so the look is unchanged
     for the vast majority who never tab. Accessibility basics (chunk 6). */
  button:focus-visible, a:focus-visible, input:focus-visible,
  select:focus-visible, textarea:focus-visible, [tabindex]:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
  }

  .num-btn {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 17px 10px;
    font-family: var(--font);
    font-size: 20px;
    font-weight: 600;
    color: var(--text);
    cursor: pointer;
    transition: all 0.1s;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: var(--shadow);
  }
  .num-btn:active { background: var(--surface2); transform: scale(0.94); box-shadow: none; }
  .num-btn.del { font-size: 15px; color: var(--muted); }
  .num-btn.empty { opacity: 0; pointer-events: none; }

  .pin-hint { text-align: center; font-size: 12px; color: var(--muted); margin-top: 14px; }

  .pin-submit-btn {
    width: 100%;
    margin-top: 18px;
    padding: 15px;
    border-radius: var(--radius);
    background: var(--accent);
    color: #fff;
    border: none;
    font-family: var(--font);
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    box-shadow: 0 2px 8px rgba(37,99,235,0.3);
    transition: all 0.15s;
    display: none;
  }
  .pin-submit-btn.visible { display: block; }
  .pin-submit-btn:active { transform: scale(0.97); }

  /* ── CTA buttons ── */
  .cta-btn {
    width: 100%;
    padding: 16px;
    border-radius: var(--radius);
    font-family: var(--font);
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    border: none;
    transition: all 0.15s;
    margin-bottom: 10px;
    letter-spacing: -0.1px;
  }
  .cta-btn:active { transform: scale(0.97); }
  .cta-btn:disabled { opacity: 0.4; cursor: not-allowed; }

  .cta-btn.primary {
    background: var(--accent);
    color: #ffffff;
    box-shadow: 0 2px 8px rgba(37,99,235,0.3);
  }
  .cta-btn.primary:hover { background: var(--accent-dark); }
  .cta-btn.danger {
    background: var(--danger);
    color: #ffffff;
    box-shadow: 0 2px 8px rgba(220,38,38,0.3);
  }
  .cta-btn.ghost {
    background: var(--surface);
    color: var(--muted);
    border: 1px solid var(--border);
    box-shadow: var(--shadow);
  }
  .cta-btn.ghost:hover { color: var(--text); border-color: var(--border2); }

  /* .cta-btn defaults to width:100% for the big stacked CTAs. When one is
     placed in a flex row as a non-growing item (style="…flex:0 0 auto…"),
     that 100% becomes its flex-basis and squishes its row siblings (e.g. the
     "Add city" input, the fee field next to Save/Suspend). Force auto width
     in that case so the row lays out naturally. */
  .cta-btn[style*="flex:0 0 auto"],
  .cta-btn[style*="flex: 0 0 auto"] { width: auto; }

  /* ── Admin screen ── */
  .section-title {
    font-size: 11px;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    margin-bottom: 10px;
    font-weight: 600;
  }

  .input-group { margin-bottom: 10px; }
  .input-label {
    font-size: 12px;
    color: var(--muted);
    font-weight: 500;
    margin-bottom: 5px;
    display: block;
  }
  .text-input {
    width: 100%;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 11px 13px;
    font-family: var(--font);
    font-size: 14px;
    color: var(--text);
    outline: none;
    transition: border-color 0.15s, box-shadow 0.15s;
    box-shadow: var(--shadow);
  }
  .text-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-dim); }
  ::placeholder { color: var(--faint); opacity: 1; }

  .salary-prefix { position: relative; }
  .salary-prefix .prefix {
    position: absolute;
    left: 13px; top: 50%;
    transform: translateY(-50%);
    color: var(--muted);
    font-size: 14px;
    pointer-events: none;
  }
  .salary-prefix .text-input { padding-left: 28px; }

  .emp-delete-btn {
    background: var(--danger-dim);
    border: 1px solid rgba(220,38,38,0.2);
    color: var(--danger);
    padding: 5px 11px;
    border-radius: var(--radius-sm);
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    font-family: var(--font);
    transition: all 0.15s;
  }
  .emp-delete-btn:hover { background: rgba(220,38,38,0.15); }

  /* Admin link on login */
  .admin-link {
    text-align: center;
    margin-top: auto;
    padding-top: 20px;
    font-size: 13px;
    font-weight: 500;
    color: var(--muted);
    cursor: pointer;
  }
  .admin-link:hover { color: var(--accent); }

  /* Toast */
  /* Sits above the bottom tab bar, and WRAPS. It used to be a nowrap pill
     pinned 28px up, which was fine for "Saved" but silently clipped anything
     longer at both edges and hid it behind the tabs — so the one message that
     matters most, why a punch was refused, was the one nobody could read. */
  .toast {
    position: fixed;
    bottom: calc(84px + env(safe-area-inset-bottom, 0px));
    /* Centred with auto margins rather than left:50% + translateX(-50%):
       with only `left` set, the available width is the space to the RIGHT of
       that point, so a wrapping toast could never grow past half the screen. */
    left: 12px;
    right: 12px;
    margin-inline: auto;
    width: fit-content;
    max-width: min(92vw, 420px);
    /* Must clear its own height PLUS the bottom offset, or the "hidden" toast
       just sits lower on screen and never actually goes away. A fixed 80px
       worked only while bottom was 28px and the toast was one line tall. */
    transform: translateY(calc(100% + 96px));
    background: var(--text);
    border-radius: 16px;
    padding: 12px 18px;
    font-size: 13px;
    line-height: 1.45;
    font-weight: 500;
    text-align: center;
    color: #ffffff;
    z-index: 999;
    /* Ease-out, no overshoot: this is where a refused punch explains itself. */
    transition: transform 0.3s cubic-bezier(0.22,1,0.36,1);
    box-shadow: var(--shadow-md);
  }
  .toast.show { transform: translateY(0); }

  .live-clock { font-size: 13px; color: var(--muted); font-weight: 500; }

  /* The company's logo beside its name on the picker — added by js/company.js
     only when the company has one. */
  .co-logo { width: 16px; height: 16px; border-radius: 4px; object-fit: contain; }

  /* Section divider */
  .divider {
    height: 1px;
    background: var(--border);
    margin: 20px 0;
  }

  /* Scrollbar */
  ::-webkit-scrollbar { width: 4px; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 4px; }

  /* Modal overlay for manual attendance */
  /* Phase 6: bottom-sheet look (matches .sheet) — same .open mechanics. */
  .modal-overlay {
    display: none;
    position: fixed; inset: 0;
    background: rgba(15,17,21,0.45);
    z-index: 100;
    align-items: flex-end;
    justify-content: center;
  }
  .modal-overlay.open { display: flex; }
  .modal-box {
    background: var(--surface);
    border-radius: 20px 20px 0 0;
    padding: 20px 20px 26px;
    width: 100%;
    max-width: 520px;
    max-height: 88%;
    overflow-y: auto;
    box-shadow: var(--shadow-md);
    animation: slideUp 0.25s ease;
  }
  @keyframes slideUp {
    from { transform: translateY(30px); opacity: 0; }
    to   { transform: translateY(0); opacity: 1; }
  }
  .modal-title { font-size: 16px; font-weight: 700; margin-bottom: 4px; }
  .modal-sub { font-size: 13px; color: var(--muted); margin-bottom: 18px; }
  .modal-actions { display: flex; gap: 8px; margin-top: 16px; }
  .modal-actions .cta-btn { margin-bottom: 0; flex: 1; }
  .modal-actions .ghostbtn, .modal-actions .bigbtn { flex: 1; }

  /* ════════════════════════════════════════════════════════════════════
     Phase 6 design system — ported from docs/design/prototype.html.
     Frame-shell rules (.frame/.stage/.personas/.sbar/.scr/.top/.hint) are
     NOT ported; overlays/tabs use position:fixed for the real viewport. */

  /* ── Header / identity ── */
  .hdr { display:flex; align-items:center; justify-content:space-between; margin-bottom:14px; }
  .avatar { width:34px; height:34px; border-radius:50%; background:var(--accent-dim); color:var(--accent-dark); display:flex; align-items:center; justify-content:center; font-size:12px; font-weight:700; }
  .cobrand { display:flex; align-items:center; gap:9px; margin-bottom:14px; }
  .colo { width:30px; height:30px; border-radius:9px; background:var(--warning-dim); color:var(--warning-dark); font-size:11px; font-weight:700; display:flex; align-items:center; justify-content:center; }
  .cobrand b { font-size:13.5px; } .cobrand i { font-style:normal; font-size:11px; color:var(--faint); display:block; }
  .hello { font-size:24px; font-weight:700; letter-spacing:-.4px; line-height:1.15; }
  .hello.md { font-size:20px; }
  .sub { font-size:13px; color:var(--muted); margin-top:3px; }

  /* ── Chips / status ── */
  .chip { display:inline-flex; align-items:center; gap:7px; font-size:12px; font-weight:600; padding:6px 12px; border-radius:99px; }
  .dot { width:8px; height:8px; border-radius:50%; }
  .ok { background:var(--success-dim); color:var(--success); } .ok .dot { background:var(--success); }
  .bad { background:var(--danger-dim); color:var(--danger); } .bad .dot { background:var(--danger); }
  .warn { background:var(--warning-dim); color:var(--warning-dark); } .warn .dot { background:var(--warning); }

  /* ── Cards ── */
  .card { background:var(--surface); border:1px solid var(--border); border-radius:var(--radius); padding:14px 16px; margin-top:12px; box-shadow:var(--shadow); }
  /* On a phone the owner's side column (Overview, Managers, Notifications)
     stacks under the outlet cards, and its first heading carries no top margin
     — right at the top of its own desktop column, flush against the last outlet
     card here. The desktop block below sets both columns' gap. */
  #owner-side { margin-top:28px; }
  .card h4, .card h2 { margin:0 0 10px; font-size:11px; letter-spacing:.08em; text-transform:uppercase; color:var(--faint); font-weight:700; }
  /* Staff-screen section heading (admin, owner, master): the muted, lighter
     sibling of .card h4. Same specificity as .card h4, so it must stay below it.
     h2 too: the admin panel's headings are h2 (2026-10-07), so its outline
     does not skip from the screen to h4. */
  h4.sec-h, h2.sec-h { font-size:11.5px; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); font-weight:600; }

  /* ── Buttons ── */
  .bigbtn { margin-top:16px; width:100%; border:none; border-radius:var(--radius); padding:17px; font:700 16px var(--font); background:var(--accent); color:#fff; cursor:pointer; }
  .bigbtn:active { transform:scale(.98); }
  /* "Day complete". --faint on --border was 4.18:1, under the 4.5:1 16px bold
     text needs — the one surface D9c's audit of --faint missed (2026-10-18). On
     --surface2 it is 4.62; the inset line keeps the shape on a white card. */
  .bigbtn.off { background:var(--surface2); box-shadow:inset 0 0 0 1px var(--border); color:var(--faint); cursor:not-allowed; }
  .bigbtn.out { background:var(--text); }
  .bigbtn.sm { padding:13px; font-size:14px; }
  .ghostbtn { width:100%; margin-top:10px; background:var(--surface); border:1px solid var(--border2); border-radius:var(--radius-sm); padding:11px; font:600 13px var(--font); color:var(--muted); cursor:pointer; }
  .smbtn { background:var(--surface); border:1px solid var(--border2); border-radius:8px; padding:6px 12px; font:600 12px var(--font); color:var(--muted); cursor:pointer; }
  .smbtn.pri { background:var(--accent); border-color:var(--accent); color:#fff; }
  /* Back, Export Excel, Backup: the admin header's buttons are finger-sized. */
  .admin-hdr .smbtn { min-height:36px; padding:8px 12px; white-space:nowrap; }
  /* Quick links on the owner and manager dashboards (Company settings, Holidays…). */
  .pillbtn { padding:8px 16px; border:1px solid var(--border); border-radius:20px; background:var(--surface); cursor:pointer; font-family:var(--font); font-size:13.5px; color:var(--text); }
  /* Press, hover and busy for the secondary buttons. Approve/Reject rows are
     disabled while their write is in flight and used to look exactly as
     tappable as before. The punch button keeps its own states: its label
     already says what it is doing ("Getting location…"). */
  /* The owner's quick links, led by the monthly report — "what do I pay?" is
   * the second of the owner's two questions, so it is the one link drawn as a
   * button of its own (icon, one line on what it holds, a chevron) while the
   * rest stay pills. Built in js/owner.js. */
  .navlead { display:flex; flex-direction:column; gap:8px; margin-bottom:12px; }
  .navlead-row { display:flex; flex-wrap:wrap; gap:8px; }
  .navlead-btn { display:flex; align-items:center; gap:12px; width:100%; text-align:left; background:var(--surface); border:1px solid var(--border); border-radius:var(--radius); padding:13px 16px; box-shadow:var(--shadow); color:var(--text); font-family:var(--font); cursor:pointer; transition: transform .1s, box-shadow .15s; }
  .navlead-btn:active { transform: scale(.98); }
  @media (hover: hover) { .navlead-btn:hover { box-shadow: var(--shadow-md); } }
  .navlead-ic, .navlead-chev { flex:none; fill:none; stroke:var(--accent); stroke-linecap:round; stroke-linejoin:round; }
  .navlead-ic { width:20px; height:20px; stroke-width:1.75; }
  .navlead-chev { width:16px; height:16px; stroke-width:2; }
  .navlead-txt { flex:1; min-width:0; }
  .navlead-txt b { display:block; font-size:15px; font-weight:700; line-height:1.3; }
  .navlead-txt i { display:block; font-style:normal; font-size:12px; color:var(--muted); margin-top:1px; line-height:1.4; }
  /* Outlet card on the owner home: name, one line of facts, the punch link,
   * then the two things an owner does with an outlet as named buttons across
   * the foot. Built in js/owner.js. */
  .outlet-card { padding-bottom:0; overflow:hidden; }
  .oc-name { margin:0; font-size:16px; font-weight:700; line-height:1.3; }
  .oc-meta { margin:3px 0 0; font-size:12px; color:var(--muted); line-height:1.45; }
  .oc-link { display:flex; align-items:center; margin-top:8px; padding:2px 0 2px 10px; background:var(--surface2); border-radius:8px; }
  .oc-link .link { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:12px; }
  .oc-ic { width:16px; height:16px; flex:none; fill:none; stroke:currentColor; stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round; }
  .oc-copy { display:inline-flex; align-items:center; gap:5px; flex:none; border:0; background:none; padding:6px 10px 6px 8px; color:var(--accent-dark); font-family:var(--font); font-size:12px; font-weight:600; cursor:pointer; }
  .oc-act { display:grid; grid-template-columns:1fr 1fr; margin:12px -16px 0; border-top:1px solid var(--border); }
  .oc-act button { display:flex; align-items:center; justify-content:center; gap:7px; min-height:46px; border:0; background:none; color:var(--text); font-family:var(--font); font-size:13.5px; font-weight:600; cursor:pointer; }
  .oc-act button + button { border-left:1px solid var(--border); }
  .oc-act .oc-ic { color:var(--muted); }
  .oc-act button:active { background:var(--surface2); }
  @media (hover: hover) { .oc-act button:hover { background:var(--bg); } }
  .ghostbtn, .smbtn, .pillbtn { transition: transform .1s, color .15s, border-color .15s; }
  .ghostbtn:active, .smbtn:active, .pillbtn:active { transform: scale(.97); }
  @media (hover: hover) {
    .ghostbtn:hover, .smbtn:not(.pri):hover, .pillbtn:hover { color: var(--text); border-color: var(--border2); }
  }
  .ghostbtn:disabled, .smbtn:disabled, .pillbtn:disabled { opacity: .5; cursor: not-allowed; transform: none; }
  /* The ⓘ on every section heading (js/help.js) is a 13px glyph, far under a
     thumb. The pseudo-element widens what a tap can hit without moving a pixel;
     any later positioned control (a toggle) still paints, and wins, on top. */
  .help-i { position: relative; }
  .help-i::after { content: ''; position: absolute; inset: -10px -8px; }

  /* ── Key-value rows / tags ── */
  .kv { display:flex; justify-content:space-between; align-items:center; font-size:13px; padding:7px 0; gap:10px; }
  .kv + .kv { border-top:1px solid var(--border); }
  .kv b { font-weight:600; } .kv i { font-style:normal; color:var(--muted); text-align:right; }
  .tag { font-size:11px; font-weight:700; padding:2.5px 8px; border-radius:6px; }
  .t-auto { background:var(--accent-dim); color:var(--accent-dark); }
  .t-off { background:var(--surface2); color:var(--muted); }
  .t-leave { background:var(--leave-dim); color:var(--leave); }
  .t-hol { background:var(--violet-dim); color:var(--violet); }
  .t-pend { background:var(--warning-dim); color:var(--warning-dark); }

  /* ── Stats / tiles ── */
  .stats { display:flex; gap:9px; margin-top:14px; }
  .stat { flex:1; background:var(--surface); border:1px solid var(--border); border-radius:12px; padding:11px 6px; text-align:center; }
  .stat b { display:block; font-size:19px; font-weight:700; }
  .stat i { font-style:normal; font-size:11px; color:var(--muted); }

  /* ── The presence strip: the one signature element ──────────────────────────
   *
   * Everything else on these dashboards is a quiet white card. This is the one
   * thing allowed to be loud, because it is the one thing the app is for. The
   * left edge bar carries the tone, so the state is readable from across a
   * counter without reading a word — and it is the ONLY saturated block of
   * colour in the product, which is what keeps it meaning something.
   *
   * Same shape at every level: owner gets one .pgrid row per outlet, manager gets
   * the single row for theirs. Built by js/presence-strip.js. */
  .pstrip {
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 4px solid var(--muted);
    border-radius: var(--radius);
    padding: 12px 16px;
    margin-top: 14px;
    box-shadow: var(--shadow);
  }
  /* `pt-` (presence tone), not the bare .ok/.warn/.bad at the top of the Chips
   * section — those carry a background and a text colour of their own, so
   * class="pstrip warn" painted the whole strip amber and every word inside it
   * brown. Found the first time the strip was rendered. */
  .pstrip.pt-ok   { border-left-color: var(--success); }
  .pstrip.pt-warn { border-left-color: var(--warning); }
  .pstrip.pt-bad  { border-left-color: var(--danger); }

  .phead { display:flex; align-items:baseline; justify-content:space-between; gap:10px; margin-bottom:2px; }
  .ptitle { font-size:11px; letter-spacing:.08em; text-transform:uppercase; color:var(--faint); font-weight:700; }
  .pdate { font-size:11.5px; color:var(--faint); }

  /* Four fixed columns — arrived, not arrived, late, absent. One outlet is this
   * table; several are the folding .pout rows further down. The cells are
   * washed in their status hue so a column reads by colour before by word; an
   * empty cell drops the wash and goes faint, so "0 late" is quiet.
   * `table-layout:fixed` keeps the four columns equal; names wrap inside their
   * cell rather than widening it. */
  .pgrid { width:calc(100% + 12px); margin:6px -6px 0; border-collapse:separate; border-spacing:6px 0; table-layout:fixed; }
  .pgrid thead th { font-size:11px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--faint); text-align:left; padding:2px 8px 4px; vertical-align:bottom; line-height:1.25; }
  .pdot { display:inline-block; width:7px; height:7px; border-radius:50%; margin-right:5px; vertical-align:1px; }
  .pdot.pt-ok   { background:var(--success); }
  .pdot.pt-bad  { background:var(--danger); }
  .pdot.pt-warn { background:var(--warning); }
  .pdot.pt-mute { background:var(--border2); }
  /* Counts are the headline: big numeral, names beneath it, one per line. */
  .pc { vertical-align:top; padding:6px 8px 8px; border-radius:10px; }
  .pc b { display:block; font-size:22px; font-weight:700; letter-spacing:-.5px; line-height:1.1; font-variant-numeric:tabular-nums; }
  .pc.pt-ok   { background:var(--success-dim); }
  .pc.pt-bad  { background:var(--danger-dim); }
  .pc.pt-warn { background:var(--warning-dim); }
  .pc.pt-mute { background:var(--surface2); }
  .pc.pt-ok b   { color: var(--success); }
  .pc.pt-warn b { color: var(--warning-dark); }
  .pc.pt-bad b  { color: var(--danger); }
  .pc.pt-mute b { color: var(--muted); }
  .pc.is-zero { background:none; }
  .pc.is-zero b { color:var(--faint); font-weight:500; }
  .pn { display:block; margin-top:3px; font-size:12px; line-height:1.35; color:var(--text); overflow-wrap:anywhere; }
  .pn .tag { padding:2px 6px; vertical-align:1px; }
  .pn-more { color:var(--muted); }
  /* Several outlets (js/presence-strip.js outlet()): column heads written once,
   * then each outlet folds to its name over four counts on the same tracks.
   * Opening it drops the names into the cells beneath, squared off where they
   * meet so count and names read as one washed cell. */
  .pcols, .pout > summary, .pbody { display:grid; grid-template-columns:repeat(4, minmax(0, 1fr)); column-gap:6px; }
  /* A fifth, narrow track for the arrow (.pgo). Same 6px gap on both grids so
   * the heads stay over their counts. */
  .pcols { margin-top:6px; grid-template-columns:repeat(4, minmax(0, 1fr)) 32px; }
  .pout-w { display:grid; grid-template-columns:minmax(0, 1fr) 32px; column-gap:6px; }
  .pcols span { font-size:11px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--faint); padding:2px 8px 4px; align-self:end; line-height:1.25; }
  .pout { padding:12px 0; }
  .pout-w + .pout-w { border-top:1px solid var(--border); }
  .pout-w:last-child .pout { padding-bottom:4px; }
  .pout > summary { row-gap:6px; list-style:none; cursor:pointer; -webkit-tap-highlight-color:transparent; }
  .pout > summary::-webkit-details-marker { display:none; }
  .pout > summary:focus-visible { outline:2px solid var(--accent); outline-offset:3px; border-radius:10px; }
  .pout .pname { grid-column:1 / -1; display:flex; align-items:center; gap:6px; font-size:12.5px; line-height:16px; font-weight:600; color:var(--muted); padding-left:6px; min-width:0; }
  .pname-t { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .pout > summary:hover .pname { color:var(--text); }
  .pchev { width:12px; height:12px; flex:none; color:var(--faint); transition:transform .18s cubic-bezier(.2,.8,.2,1); }
  .pout[open] .pchev { transform:rotate(90deg); }
  .pout > summary .pc { padding-bottom:6px; }
  .pout[open] > summary .pc { border-bottom-left-radius:0; border-bottom-right-radius:0; padding-bottom:0; }
  .pbody .pc { padding-top:0; border-top-left-radius:0; border-top-right-radius:0; }
  .pbody .pc:not(:empty) { padding-top:2px; }
  /* Lined up with the counts row: 12px .pout padding + 16px name line + 6px gap. */
  .pgo { margin-top:34px; height:36px; display:grid; place-items:center; padding:0; background:var(--surface); border:1px solid var(--border2); border-radius:10px; color:var(--text); cursor:pointer; -webkit-tap-highlight-color:transparent; transition:border-color .15s, color .15s; }
  .pgo svg { width:16px; height:16px; }
  .pgo:hover { border-color:var(--accent); color:var(--accent-dark); }
  .pgo:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
  /* A phone's column is ~72px: "ARRIVED" fits, "● ARRIVED" does not, and the
   * dot wrapped onto a line of its own. The washed cells already carry the hue,
   * so the dot goes and the header and cells share the tighter inset. */
  @media (max-width: 419px) {
    .pdot { display:none; }
    .pgrid thead th, .pcols span, .pc { padding-left:6px; padding-right:6px; }
  }
  /* A day row inside an expanded report card: "06 Jul   Absent".
   *
   * Was display:flex + justify-content:space-between, which pinned the date to
   * one edge and the label to the other. That read fine while detail panels
   * rendered inside a ~500px column, and became unreadable the moment panels
   * went full width on 2026-07-30 — ~900px of empty space between a date and
   * its own status, thirty rows down the page.
   *
   * A fixed date track plus a max-width keeps the pair together at any container
   * width, which is the property that was missing rather than the particular
   * numbers. */
  .excrow { display:grid; grid-template-columns:62px 1fr; gap:12px; font-size:11.5px; padding:3px 0; max-width:440px; }
  .excrow > :first-child { color:var(--muted); }

  .tiles { display:grid; gap:9px; margin-top:14px; }
  /* The days behind the leave counted in Absent, under the tiles (js/report.js monthTiles). */
  .leave-note { margin:8px 2px 0; font-size:12px; font-weight:500; color:var(--leave); }
  .tile { border-radius:12px; padding:14px 8px; text-align:center; }
  .tile i { font-style:normal; font-size:11px; letter-spacing:.08em; text-transform:uppercase; font-weight:700; }
  .tile b { display:block; font-size:24px; font-weight:700; margin-top:2px; }

  /* ── Calendar ── */
  /* minmax(0,1fr) and .day min-width:0: a plain 1fr track will not shrink below
     its content, and at 11px labels that forced 42px squares and pushed
     Saturday out of the card on a 320px phone. */
  .cal { display:grid; grid-template-columns:repeat(7,minmax(0,1fr)); gap:4px; margin-top:10px; }
  .cal .dh { font-size:11px; color:var(--faint); text-align:center; font-weight:700; padding:2px 0; }
  .day { aspect-ratio:1; min-width:0; border-radius:8px; display:flex; flex-direction:column; align-items:center; justify-content:center; font-size:13px; font-weight:700; cursor:pointer; border:1px solid transparent; }
  /* 11px, the floor (was 8.5px, and 9.5px on the weekday row); the day number
     went to 13px/700 so it still leads. No opacity: at .75 it took the darker
     greens and reds below 4.5:1 again. Widest labels ("leave", "10/12") are
     29-30px against 31px of room in a 320px phone's square; nowrap so a longer
     one overflows visibly rather than silently making its square taller. */
  .day small { font-size:11px; font-weight:600; line-height:1.15; white-space:nowrap; }
  .day.P { background:var(--success-dim); color:var(--success-dark); }
  .day.A { background:var(--danger-dim); color:var(--danger-dark); }
  .day.O { background:var(--surface2); color:var(--muted); }
  .day.H { background:var(--violet-dim); color:var(--violet); }
  .day.W { background:var(--warning-dim); color:var(--warning-dark); }
  .day.L { background:var(--leave-dim); color:var(--leave); }
  .day.F { color:var(--faint); }
  .day.today { border-color:var(--accent); }
  .day.sel { border-color:var(--accent); border-width:2px; }

  /* Calendar legend. Two fixed columns, so every label starts on the same x —
     the old version put the colour NAME inside a coloured pill, and since
     "green" and "violet" are different widths no two labels ever lined up.
     A swatch carries the colour; the words carry only the meaning.
     Built by calLegend() in js/helpers.js — one definition, both calendars. */
  .cal-legend {
    display:grid; grid-template-columns:repeat(2,minmax(0,1fr));
    gap:7px 14px; margin-top:12px; font-size:11px; color:var(--muted);
  }
  .cal-legend > span { display:flex; align-items:center; gap:7px; min-width:0; }
  /* The swatch repeats the day cell's fill AND edges it in the same hue at full
     strength: the fills are 10% alpha, which reads as blank paper at 12px.
     Each state sets the whole border shorthand, so the shared rule above cannot
     out-specify it back to transparent. */
  .cal-legend i { flex:none; width:12px; height:12px; border-radius:4px; display:block; }
  .cal-legend .sw-P { background:var(--success-dim); border:1px solid var(--success); }
  .cal-legend .sw-W { background:var(--warning-dim); border:1px solid var(--warning); }
  .cal-legend .sw-A { background:var(--danger-dim);  border:1px solid var(--danger); }
  .cal-legend .sw-H { background:var(--violet-dim);  border:1px solid var(--violet); }
  .cal-legend .sw-L { background:var(--leave-dim);   border:1px solid var(--leave); }
  .cal-legend .sw-O { background:var(--surface2);    border:1px solid var(--border2); }

  /* ── People / notification rows ── */
  .prow { display:flex; align-items:center; gap:11px; padding:10px 0; border-top:1px solid var(--border); font-size:13px; }
  .prow:first-of-type { border-top:none; }
  .prow .avatar { width:32px; height:32px; font-size:11px; flex-shrink:0; }
  .prow .nm { flex:1; min-width:0; }
  .prow .nm b { font-weight:600; display:block; }
  .prow .nm i { font-style:normal; font-size:11.5px; color:var(--muted); }
  .nrow { display:flex; gap:10px; padding:12px 14px; border-top:1px solid var(--border); font-size:12.5px; }
  .nrow:first-child { border-top:none; }
  .nrow b { font-weight:600; display:block; }
  .nrow i { font-style:normal; color:var(--muted); }

  /* ── Form controls ── */
  .seg { display:flex; border:1px solid var(--border2); border-radius:10px; overflow:hidden; margin-top:8px; }
  .seg button { flex:1; text-align:center; font:600 12px var(--font); padding:9px 2px; color:var(--muted); background:none; border:none; cursor:pointer; }
  .seg button.on { background:var(--accent); color:#fff; }
  .tgl { width:40px; height:23px; border-radius:99px; background:var(--border2); position:relative; cursor:pointer; flex-shrink:0; border:none; transition:background .15s; }
  .tgl::after { content:''; position:absolute; top:2.5px; left:2.5px; width:18px; height:18px; border-radius:50%; background:#fff; transition:left .15s; box-shadow:0 1px 2px rgba(0,0,0,.25); }
  .tgl.on { background:var(--accent); }
  .tgl.on::after { left:19.5px; }
  /* Native checkbox variant — handlers keep reading .checked. */
  input.tgl { appearance:none; -webkit-appearance:none; margin:0; }
  input.tgl:checked { background:var(--accent); }
  input.tgl:checked::after { left:19.5px; }
  .field { margin-top:10px; }
  .field label { font-size:11.5px; font-weight:600; color:var(--muted); display:block; margin-bottom:5px; line-height:1.45; }
  .field input, .field select { width:100%; border:1px solid var(--border2); border-radius:var(--radius-sm); padding:10px 12px; font:600 13.5px var(--font); color:var(--text); background:var(--surface); }

  /* ── Bottom tab nav (fixed) ── */
  .tabs { position:fixed; bottom:0; left:0; right:0; background:var(--surface); border-top:1px solid var(--border); display:flex; z-index:30; }
  .tab { flex:1; text-align:center; padding:10px 2px 14px; font-size:11px; font-weight:600; color:var(--faint); cursor:pointer; border:none; background:none; font-family:inherit; }
  .tab .ico { display:block; font-size:16px; margin-bottom:2px; }
  .tab.on { color:var(--accent); }

  /* ── Tables / pills ── */
  .pill { font-size:11px; font-weight:700; padding:3px 10px; border-radius:99px; }
  .p-ok { background:var(--success-dim); color:var(--success); }
  .p-bad { background:var(--danger-dim); color:var(--danger); }
  /* Amber: approaching a limit, not yet over it — the read meter's warning band. */
  .p-warn { background:var(--warning-dim); color:var(--warning-dark); }
  .link { color:var(--accent-dark); font-weight:600; cursor:pointer; font-size:12px; }
  /* Master's counters. Fill and border match .card and .stat rather than the
   * --surface2 they used to carry: three different card fills across two
   * dashboards read as three kinds of thing, and they are all just cards. The
   * numeral matches the presence strip's .pc scale so the whole product has one stat type. */
  .mgrid { display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:12px; margin-top:14px; }
  .mcard { background:var(--surface); border:1px solid var(--border); border-radius:12px; padding:13px 15px; }
  .mcard i { font-style:normal; font-size:11.5px; color:var(--muted); display:block; }
  .mcard b { font-size:22px; font-weight:700; letter-spacing:-.5px; }
  .co-card { background:var(--surface); border:1px solid var(--border); border-radius:14px; overflow:hidden; margin-top:12px; }
  .co-stats { display:flex; padding:12px 16px 0; }
  .co-stat { flex:1; min-width:0; }
  .co-stat + .co-stat { padding-left:14px; border-left:1px solid var(--border); }
  .co-stat-l { font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.04em; color:var(--muted); }
  .co-stat-v { font-size:16px; font-weight:700; letter-spacing:-0.02em; margin-top:1px; font-variant-numeric:tabular-nums; }
  .co-outlet { display:flex; align-items:center; gap:8px; padding:7px 0; }
  .co-outlet + .co-outlet { border-top:1px solid var(--border); }
  .co-outlet-dot { width:6px; height:6px; border-radius:50%; background:var(--success); flex-shrink:0; }

  /* ── Misc blocks ── */
  /* The line that stands in for a list: Loading… / Could not load… / No X yet.
     .inset is the same message inside a zero-padding card. */
  .empty-msg { color:var(--muted); font-size:13px; padding:24px 0; }
  .empty-msg.inset { padding:12px 16px; font-size:12px; }
  .demo { background:var(--warning-dim); border:1px dashed var(--warning); border-radius:var(--radius-sm); padding:9px 12px; margin-top:12px; font-size:11.5px; color:var(--warning-dark); line-height:1.6; }
  .demo .link { color:var(--warning-dark); text-decoration:underline; }
  .obar { height:6px; background:var(--surface2); border-radius:99px; overflow:hidden; }
  .obar div { height:100%; background:var(--accent); border-radius:99px; }
  .orow { display:flex; align-items:center; gap:10px; padding:10px 2px; border-top:1px solid var(--border); font-size:13px; }
  .orow:first-of-type { border-top:none; }
  .orow .cb { width:19px; height:19px; border-radius:50%; border:2px solid var(--border2); flex-shrink:0; display:flex; align-items:center; justify-content:center; font-size:11px; color:#fff; cursor:pointer; }
  .orow.done .cb { background:var(--success); border-color:var(--success); }
  .orow.done span { color:var(--faint); text-decoration:line-through; }
  .orow .go { margin-left:auto; font-size:11.5px; font-weight:700; color:var(--accent-dark); cursor:pointer; }
  .det { margin-top:10px; border-top:1px solid var(--border); padding-top:8px; }
  .det div { font-size:12px; color:var(--muted); padding:3px 0; }

  /* Punch feedback: brief full-screen flash (pref-gated), fades out. */
  .punch-flash { position:fixed; inset:0; z-index:300; pointer-events:none; animation:flashfade .5s ease both; }
  .punch-flash.okf { background:var(--success); }
  .punch-flash.badf { background:var(--danger); }
  @keyframes flashfade { from { opacity:.45; } to { opacity:0; } }

  /* ── D3: desktop, and only for the two screens that need it ───────────────
   *
   * Every screen was capped at 420px while the master company table needs
   * ~770px, so an owner or super admin on a laptop read their widest data
   * through a phone-sized slot with the rest of the monitor blank.
   *
   * Additive and last in the file on purpose: everything above is untouched,
   * and below 900px not one declaration here applies — phone rendering is
   * byte-identical, which is the constraint that matters on the hardware this
   * app targets.
   *
   * Manager and employee stay phone-width deliberately. Both are used standing
   * up, on a phone, mid-shift; widening them would be change without a reason.
   */
  @media (min-width: 900px) {
    /* The table is the whole screen here, so it takes the width directly. */
    #screen-master { max-width: 1160px; }
    #screen-master .wide-content { max-width: 1160px; }

    #screen-owner { max-width: 1100px; }
    #screen-owner .wide-content { max-width: 1100px; }

    /* A 1100px single column of short cards is worse than 420px, not better —
     * it just stretches each card into a band. So: two tracks.
     *
     * This was `columns: 2` until 2026-07-30, and multi-column was the wrong
     * tool for a dashboard three separate ways. Measured at 1440px with the
     * Company settings panel open:
     *
     *   * Reading order was a byproduct of column balancing, not a decision.
     *     A spanner splits a multicol into column ROWS, and the setup card sat
     *     alone in the first one — 422px of empty page beside it, above the fold.
     *   * A tall element makes its whole column row that tall, so the Managers
     *     and Notifications cards ended up at y=939 and y=1017 in the LEFT
     *     column while the 697px panel held the right one from y=649. Two real
     *     cards, stranded below the fold, sliced at the row boundary. That is
     *     the "white thing getting cut".
     *   * And it forced a scroll: `detail()` had to call scrollIntoViewSafe
     *     because the panel it had just filled opened 649px down the page, in a
     *     column nobody was looking at.
     *
     * `align-items: start` is what actually answers the dead-gap problem the old
     * comment feared from a grid — each track is independently top-aligned, so a
     * short card beside a tall one leaves no gap. Two explicit containers, so
     * which card goes where is a decision in the markup, and nothing can be
     * sliced by a boundary that no longer exists.
     *
     * Header, presence strip and setup card span both tracks. */
    #owner-content {
      display: grid;
      grid-template-columns: 2fr 1fr;
      column-gap: 20px;
      align-items: start;
    }
    #owner-content > .hdr,
    #owner-content > #owner-fence-alert,
    #owner-content > #owner-presence,
    #owner-content > #setup-slot { grid-column: 1 / -1; }

    /* `display: grid` above out-specifies the `display: none` the `hidden`
     * attribute carries, so without this the dashboard stays visible underneath
     * an open detail panel. Both directions stated, because the panel is hidden
     * by the same mechanism when the dashboard is showing. */
    #owner-content[hidden], #owner-detail[hidden] { display: none; }
    /* Both columns start 16px under the strip, which they otherwise touched.
     * The push card (js/push-card.js, shared with the manager and employee
     * screens) carries an inline 8px top margin that would set the left column
     * 8px lower than the right; !important is the only way past inline. */
    #owner-main, #owner-side { margin-top: 16px; }
    #owner-push-slot > .card { margin-top: 0 !important; }

    /* The outlet attendance calendar re-uses phone-width .day cells, which
       become 150px squares at 1100px.  Cap the panel to phone width so
       clicking a date doesn't need a scroll to see the punches below. */
    #owner-detail { max-width: 480px; }

    /* ── Admin panel: desktop 2-column ── */
    #screen-admin { max-width: 960px; }
    #screen-admin .admin-hdr { grid-column: 1 / -1; }
    #admin-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      column-gap: 20px;
      align-items: start;
    }

    /* ── Master dashboard: desktop 2-column ── */
    #master-content {
      display: grid;
      grid-template-columns: 2fr 1fr;
      column-gap: 20px;
      align-items: start;
    }
    #master-content > .hdr,
    #master-content > #master-status,
    #master-content > .mgrid,
    #master-content > #master-addco { grid-column: 1 / -1; }

    /* Cap full-width buttons inside wide desktop columns */
    .wide-content .bigbtn,
    .wide-content .ghostbtn { max-width: 360px; }
  }

  /* Shake, springs, slides and press-scales are decoration. A phone that asks
     for less motion gets none of them. Durations go to ~0 rather than `none`
     so nothing that ends a state on an animation event can hang. The punch
     flash keeps its fade: it is one of the two signals a punch is owed. */
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
    .punch-flash { animation-duration: .5s !important; }
  }