/* ═══════════════════════════════════════════════════════════════════════════
   RESPONSIVE — phones and tablets
   ───────────────────────────────────────────────────────────────────────────
   The shell was written desktop-only: a permanently visible 220px sidebar,
   two-column form grids, 680px modals. Everything here adapts that for narrow
   screens.

   NOTHING IN THIS FILE APPLIES ABOVE 900px. That is a guarantee, and it is why
   this file is last in the load order and why every rule sits inside a media
   query. If you need a rule that applies at all widths, it belongs in the file
   that owns the component, not here.

   Two breakpoints, and only two:
     900px  tablet / small laptop — the sidebar becomes a drawer
     640px  phone — single column, bottom-sheet modals
   A third breakpoint is a smell. A page that genuinely needs its own width
   (vehicle intake's 520px VIN row) keeps it in pages.css beside the component.

   CSS cannot use var() in a media query, so these numbers cannot be tokenised.
   If you change one, change it everywhere in this file and in DESIGN_SYSTEM.md.
   ═══════════════════════════════════════════════════════════════════════════ */


/* ── TABLET / SMALL LAPTOP ─────────────────────────────────────────────── */
@media (max-width: 900px) {
  .nav-toggle { display: inline-flex; }
  .sidebar-backdrop.open { display: block; }

  /* Sidebar leaves the flex row and becomes an overlay drawer, handing its
     220px back to the content. visibility keeps it out of the tab order
     while closed. */
  .sidebar {
    position: fixed;
    top: var(--header-h); left: 0; bottom: 0;
    width: var(--sidebar-w-drawer);
    max-width: 80vw;
    z-index: var(--z-drawer);
    border-right: none;
    box-shadow: var(--shadow-drawer);
    transform: translateX(-100%);
    visibility: hidden;
    transition: transform var(--transition-slow) ease, visibility var(--transition-slow);
  }
  .sidebar.open { transform: translateX(0); visibility: visible; }
  body.nav-open { overflow: hidden; }

  .header { padding: 0 var(--space-5); gap: var(--space-4); }
  .main   { padding: var(--space-7); }

  /* A platform admin's header carries two controls nobody else sees, the
     "Viewing as" switcher and Change Password, and it stopped fitting long
     before the phone breakpoint: at 641px the row ran 119px past the edge and
     cut off both Change Password and Sign Out. The "?" button widened an
     overflow that was already there. This band needs less than the phone block
     does — there is still room here for the account name and for both buttons
     at full size, so the switcher is the only thing asked to give. The label
     goes (the dropdown reads as what it is next to the account name) and the
     dropdown takes the same ceiling it takes on a phone. Either one alone
     still overflows 641px; it takes both. */
  .acting-tenant-label { display: none; }
  .acting-tenant       { max-width: 118px; }

  /* -3 was the one rung of the ladder that never collapsed, so the access-area
     legend — its only caller — stayed three columns all the way down to a
     phone, shredding each description into one word per line. */
  .auto-grid-3 { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .auto-grid-4 { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .auto-grid-5 { grid-template-columns: repeat(3, 1fr); }

  /* iOS Safari zooms the whole page whenever a focused field is under 16px,
     and then leaves it zoomed. Some controls still carry an inline
     font-size, which is the only reason !important is needed here. */
  input:not([type=checkbox]):not([type=radio]), select, textarea { font-size: var(--text-lg) !important; }

  /* The panel is a fixed 340px anchored right:0 to .header-user, whose right
     edge is exactly one header padding in from the viewport. At 320px that put
     its left edge at 320 - 12 - 340 = -32px: 32px of every row off-screen and
     unreachable. Left overflow creates no scrollbar, which is why it was never
     visible as a symptom. Sizing against the same padding this block sets on
     .header lands the left edge at exactly var(--space-5), so the gutters
     match; the 340px ceiling keeps a tablet where it already was. */
  .notif-panel { width: calc(100vw - var(--space-5) * 2); max-width: 340px; }
}


/* ── PHONE ─────────────────────────────────────────────────────────────── */
@media (max-width: 640px) {
  .main   { padding: var(--space-5); }

  /* The header row grew a "?" button that cannot shrink, and on a platform
     admin's login nothing else in the row can shrink either — at 320px the
     last thing in it, Sign Out, was being cut off. Two pixels off each gap is
     the whole cost, and it buys the button its place back. */
  .header { gap: var(--space-2); }

  /* nowrap: the logo wrapping to a second line breaks out of the fixed 56px
     header. It shrinks and the identity block gives up space instead. */
  .header-logo { font-size: var(--text-base); white-space: nowrap; flex-shrink: 0; }
  .header-user { gap: var(--space-2); min-width: 0; }
  #user-name   { display: none; }

  /* The "Viewing as" switcher is platform-admin-only, and a platform admin's
     header is the widest case there is — the switcher plus the account trigger
     overflowed a 375px header and made the whole page scroll sideways. The
     label goes (the dropdown reads as what it is next to the account name) and
     the control itself gives up width before anything else does. */
  .acting-tenant-label { display: none; }
  /* Giving up width has to mean the dropdown itself narrows. It used to keep a
     62px floor while its wrapper had none, so the floor was paid for by the
     wrapper collapsing under it and the dropdown drawing across whatever came
     next — the "?" button, once that arrived. Hidden overflow makes the
     wrapper's edge the one that counts at the narrowest widths, where there is
     no room left to give the switcher at all. */
  .acting-tenant      { flex: 1 1 auto; min-width: 0; max-width: 118px; padding: 5px var(--space-2); }
  .acting-tenant-wrap { min-width: 0; flex: 1 1 auto; overflow: hidden; }

  .login-card { padding: var(--space-10) 22px; }

  .page-header   { flex-direction: column; align-items: stretch; gap: var(--space-5); }
  .page-title h2 { font-size: var(--text-xl); }

  .toolbar    { gap: var(--space-3); }
  .search-box { flex: 1 1 100%; min-width: 0; max-width: none; }
  /* The Store column is off the right edge here; put the store on the number. */
  .store-hint { display: inline; }

  /* Single column — side-by-side fields are unusable at this width. */
  .form-row, .form-row.cols-3,
  .auto-grid-2, .auto-grid-3, .auto-grid-4, .auto-grid-5 { grid-template-columns: 1fr; }
  .photo-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }

  /* Modals become bottom sheets: full width, anchored where thumbs are, and
     never taller than the visible viewport. */
  .modal-overlay { padding: 0; align-items: flex-end; }
  .modal, .modal-lg {
    width: 100%;
    max-width: 100%;
    max-height: 92vh;
    max-height: 92dvh;
    border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
  }
  .modal-header { padding: var(--space-7) var(--space-7) var(--space-5); }
  .modal-body   { padding: var(--space-7); }
  .modal-footer { padding: var(--space-5) var(--space-7); flex-direction: column-reverse; gap: var(--space-3); }
  .modal-footer .btn { width: 100%; justify-content: center; }

  /* ── Burst camera, phone-only ────────────────────────────────────────────
     Every rule below is scoped to #burst-camera-modal, so nothing here can
     touch the VIN or part-tag scanners (.scan-overlay, a different
     component) or any other modal. Above 640px none of this applies and the
     modal renders exactly as it always has.

     The 92vh bottom sheet above is a 760px-capped card with the video at a
     fixed 52vh — fine until the operator has taken a few photos, at which
     point the thumbnail grid pushes Done below the fold. This makes the
     modal full height instead, flexes the video into whatever room is left,
     and turns the shutter/Sharp-shot/Done row into one bar pinned at the
     bottom, gathered from three DOM positions that stay exactly where they
     always were (grid-area, not a moved wrapper) so nothing here can drift
     the desktop layout it also renders under. */
  #burst-camera-modal .modal {
    height: 100dvh;
    max-height: 100dvh;
    border-radius: 0;
  }
  #burst-camera-modal .modal-body {
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-rows: auto minmax(0, 1fr) auto auto auto auto auto auto auto;
    grid-template-areas:
      "error      error      error"
      "stage      stage      stage"
      "zoom       zoom       zoom"
      "hint       hint       hint"
      "sharphint  sharphint  sharphint"
      "thumbs     thumbs     thumbs"
      "footnote   footnote   footnote"
      "diag       diag       diag"
      "sharp      controls   done";
    row-gap: 0;
    column-gap: var(--space-2);
    min-height: 0;
  }
  #burst-camera-modal #bc-error     { grid-area: error; }
  #burst-camera-modal .cam-stage    { grid-area: stage; min-height: 0; }
  #burst-camera-modal .cam-stage > video { max-height: none; height: 100%; }
  #burst-camera-modal .cam-zoom-row { grid-area: zoom; }
  #burst-camera-modal #bc-hint      { grid-area: hint; }
  #burst-camera-modal .cam-sharp-hint { grid-area: sharphint; display: block; }
  #burst-camera-modal #bc-thumbs    { grid-area: thumbs; }
  /* Shown, not hidden: this is the one line that says the shots upload on
     Done. Hidden, a thumbnail and "1 photo" read as saved and the header's
     x read as close — and x discarded them (098 cancelBurstCamera). */
  #burst-camera-modal .cam-footnote { grid-area: footnote; }
  #burst-camera-modal .cam-diag     { grid-area: diag; }
  #burst-camera-modal .cam-action-row { grid-area: sharp; }
  #burst-camera-modal .cam-controls   { grid-area: controls; }
  #burst-camera-modal .cam-done-row   { grid-area: done; }

  /* The pinned bar itself: three grid cells, each still the same real
     element with its own existing layout rules (.cam-controls' shutter/
     torch flex row is untouched), just aligned into one sticky row instead
     of three stacked ones. Sticky, not fixed, so it never overlaps the
     safe-area notch math .modal-body already accounts for and stays inside
     the modal's own rounded/full-height box. */
  #burst-camera-modal .cam-controls,
  #burst-camera-modal .cam-action-row,
  #burst-camera-modal .cam-done-row {
    margin-top: 0;
    align-self: center;
    position: sticky;
    bottom: 0;
    z-index: 1;
    background: var(--color-surface);
    padding-bottom: max(var(--space-3), env(safe-area-inset-bottom));
  }

  /* Sharp shot: icon only, per the design — the label moves to
     .cam-sharp-hint above. aria-label on the button (already in the
     fragment) keeps its accessible name either way. */
  #burst-camera-modal .cam-action-row .btn {
    flex: 0 0 auto;
    min-width: 48px;
    min-height: 48px;
    padding: 0;
    font-size: 20px;
  }
  #burst-camera-modal .cam-btn-label { display: none; }

  /* Cancel is redundant with the modal's own ✕ once Done is always on
     screen; dropping it here, not with display:none in the fragment,
     because nothing ever toggles it from script — the ✕ already calls the
     same cancelBurstCamera(). */
  #burst-camera-modal .cam-cancel-btn { display: none; }
  #burst-camera-modal #bc-done {
    min-width: 0;
    padding: 9px var(--space-3);
    white-space: nowrap;
    font-size: var(--text-sm);
  }
  /* The shutter's default horizontal padding (--space-7, 16px each side) is
     sized for the desktop row, where it shares only the torch button. In the
     three-way phone bar it is squeezed to a third of the row and wraps its
     label to two lines with that much padding; --space-4 gives the text room
     without shrinking the tap target itself (min-height stays 56px). */
  #burst-camera-modal .cam-controls .btn-shutter {
    padding-left: var(--space-4);
    padding-right: var(--space-4);
  }

  /* Thumbnails: one horizontally-scrolling strip at a fixed height, instead
     of vi-thumbs' wrapping grid, which grows without bound and is what
     pushed Done off screen in the first place. Scoped to #bc-thumbs only —
     the part-intake and vehicle-intake photo grids keep the wrapping .vi-
     thumbs layout, which is correct for a page that scrolls. */
  #burst-camera-modal #bc-thumbs {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    gap: var(--space-3);
  }
  #burst-camera-modal #bc-thumbs .vi-thumb { flex: 0 0 auto; }
  #burst-camera-modal #bc-thumbs .vi-thumb img { width: 64px; height: 64px; }

  /* Diagnostics: collapsed behind its own <summary>, reversing the
     always-open, no-summary desktop rule in components.css. */
  #burst-camera-modal .cam-diag > summary {
    display: list-item;
    cursor: pointer;
    color: var(--color-text-muted);
    font-size: var(--text-xs);
    padding: var(--space-2) 0 0;
  }
  #burst-camera-modal .cam-diag:not([open]) > #bc-diag { display: none; }

  /* ── VIN / part-tag scanner, phone-only ──────────────────────────────────
     The other camera surface: .scan-overlay, the fullscreen viewfinder both
     intake pages open over the page. Everything below is scoped to it, so
     the burst camera above and every other modal are untouched, and above
     640px the overlay renders exactly as it does on the desktop.

     Sizing the stage to its picture is NOT here — it is the base rule in
     pages.css, because the bug it fixes (a stage wider than the picture,
     which hangs .vi-scan-guide out over dead black) is worst ABOVE this
     breakpoint, not below it. Scoping it to phones was the first attempt
     and it missed every landscape phone and every "Request Desktop Site"
     session, which land at 844px and ~980px and got a 720px-wide stage over
     a 304px picture. Read the comment there before touching either file.

     What is genuinely phone-only is the full-bleed: the overlay drops its
     16px side padding so the picture may use the entire width, which is the
     only size win available to it. The room the picture cannot use goes to
     putting the controls under a thumb rather than stranding them in the
     middle of the screen. */
  .scan-overlay { padding-left: 0; padding-right: 0; }
  .scan-overlay .cam-stage {
    /* Square, because a rounded corner against the screen edge reads as a
       card that failed to fit rather than as a viewfinder. */
    border-radius: 0;
  }
  /* The one sizing override: pages.css budgets for the overlay's 16px side
     padding, which the rule above has just given up, so hand those 32px
     back. Everything else about this box — the auto/auto under two maxima
     that keeps it equal to the picture — comes from pages.css unchanged. */
  .scan-overlay .cam-stage > video { max-width: 100vw; }
  /* The gutter the overlay just gave up, handed back to everything that is
     not the picture. These already carry width: min(100%, 720px) from
     pages.css, so the padding comes out of that width rather than adding to
     it and starting a horizontal scroll. */
  .scan-overlay .vi-scan-actions,
  .scan-overlay .cam-zoom-row,
  .scan-overlay .vi-note,
  .scan-overlay .vi-mono-note,
  .scan-overlay #vi-diag-out {
    padding-left: var(--space-5);
    padding-right: var(--space-5);
    box-sizing: border-box;
  }
  /* An auto margin in a flex column eats the free space before
     justify-content sees it, so this alone splits the overlay in two: the
     picture stays at the top where the phone is aimed, and the buttons and
     sliders sit at the bottom in thumb reach. It costs nothing when the
     content is tall enough to fill the screen — there is no free space to
     absorb, and the layout falls back to the plain centred column. */
  .scan-overlay .vi-scan-actions { margin-top: auto; }
  /* Two per row. The VIN scanner's four buttons across a 390px screen give
     each about 84px, which wraps "Stop camera" and "Read characters" onto
     two ragged lines; half the row fits every label on one. The part
     scanner has only two and so gets a single row from the same rule. */
  .scan-overlay .vi-scan-actions .btn {
    flex: 1 1 40%;
    min-width: 0;
    min-height: 44px;
    justify-content: center;
  }
  /* The hint carries the camera-permission error, the aiming instruction
     and "Read <VIN> — confirming…" — the most important words on the
     screen, and in source order they sit below the button row, which the
     auto margin above has just sent to the bottom of the phone. `order`
     lifts it back under the picture WITHOUT moving the node, so the
     desktop layout (source order, no ordering rules) is untouched and no
     script that reads or writes #vi-scan-hint has to know. */
  .scan-overlay .cam-stage      { order: 1; }
  .scan-overlay .vi-note        { order: 2; }
  .scan-overlay .vi-scan-actions { order: 3; }
  .scan-overlay .cam-zoom-row   { order: 4; }
  .scan-overlay .vi-mono-note   { order: 5; }
  .scan-overlay #vi-diag-out    { order: 6; }

  /* Denser cells fit more columns before horizontal scrolling kicks in. */
  table    { font-size: var(--text-xs); }
  thead th { padding: var(--space-3) var(--space-4); }
  tbody td { padding: var(--space-3) var(--space-4); }
  .parts-table table { min-width: 560px; }
  .table-footer { flex-direction: column; gap: var(--space-4); align-items: flex-start; }
  .pagination   { flex-wrap: wrap; }

  /* Full-bleed toasts; the 280px minimum overflowed a 320px screen. */
  #toast-container { top: var(--space-4); left: var(--space-4); right: var(--space-4); }
  .toast { min-width: 0; width: 100%; }
  /* A notification card shares that container and needs the same reset, or the
     two stack at different widths in one column — and its own 280px minimum
     overflows the container below a ~312px viewport. */
  .notif-card { min-width: 0; width: 100%; max-width: none; }
}


/* ── NARROWEST PHONES ──────────────────────────────────────────────────────
   A platform admin carries an extra header button, and the avatar is
   redundant once the name beside it is hidden. */
@media (max-width: 400px) {
  .header-user .avatar { display: none; }
  .header-logo { font-size: var(--text-sm); }
}


/* ── TOUCH TARGETS ─────────────────────────────────────────────────────────
   Keyed to the input device, not the width, so a touch laptop benefits too
   while the desktop mouse layout keeps its tighter spacing. */
@media (pointer: coarse) {
  .nav-item    { padding: 13px var(--space-7); }
  .btn         { min-height: 40px; }
  .btn-sm      { min-height: 34px; }
  .page-btn    { min-height: 34px; min-width: 34px; padding: 7px var(--space-5); }
  .modal-close { min-width: 40px; min-height: 40px; }
  .part-result-item { padding: var(--space-5) var(--space-6); }

  /* Notifications were skipped when this block was written. The dismiss "x" is
     the same job .modal-close does above — it was padding: 0 2px, roughly a
     20px target — so it takes the same 40px, and the bell matches it. Both are
     inline-flex in components.css, so min-height centres the glyph rather than
     stranding it at the top of the box. */
  .notif-bell       { min-width: 40px; min-height: 40px; }
  .notif-card-close { min-width: 40px; min-height: 40px; }
  .notif-panel-row  { min-height: 44px; }

  /* 16px is a mouse-sized checkbox. On the field app these are ticked in a
     yard, often in gloves, and the intake title attestation gates a check-in
     — so the box itself gets 24px here. The ROW stays the real tap target
     (.check-row is a <label> wrapping its input, ~313x75 on a phone); this is
     about the box being visible and hittable, not about creating the target. */
  .checkbox-md { width: var(--space-9); height: var(--space-9); }
}
