/* ═══════════════════════════════════════════════════════════════════════════
   MOBILE LAYER — Wilson Bid Tracker
   ═══════════════════════════════════════════════════════════════════════════

   EVERY RULE IN THIS FILE SITS INSIDE A MEDIA QUERY. Above the breakpoint the
   file is inert — not "carefully tested to be safe", but structurally incapable
   of affecting desktop, because no selector outside a @media block exists here.

   That guarantee is the whole design, and it is load-bearing for a specific
   reason: this repo has NO visual regression testing. All 141 test files are
   logic-only; nothing renders. CI cannot catch a desktop break, so desktop
   safety cannot rest on testing. It has to be structural.

   WHY NOT UNIFY THE EXISTING CSS INSTEAD. 390KB of CSS lives inline across 13
   pages, and 126 selectors are defined on more than one page — 98 of them with
   DIFFERENT rules (`.wrap` has 10 distinct definitions, `.btn` has 9). Merging
   those is 98 chances to break desktop on a page nobody thought to check. So
   this file only ADDS; it never redefines a desktop rule.

   ── !important, and why it is everywhere ──────────────────────────────────
   There are 807 inline style="" attributes and 289 runtime element.style.x
   assignments in the app. Inline styles beat stylesheets, so a plain rule here
   would simply not apply. !important inside a media query is still inert above
   the breakpoint, so the safety guarantee holds.

   ── THE ONE HARD RULE: NEVER SET `display` ────────────────────────────────
   186 places do element.style.display = "none" to hide things. If this file
   said `display: flex !important` on a modal, the JavaScript that hides it
   would stop working and that modal would be stuck open on every phone.
   Position things with inset/transform instead, and leave `display` to the code
   that owns it. This is the single worst mistake available in this file.

   BREAKPOINT: 820px. Sits above the app's existing 760px cluster and below its
   860/900 ones, and there is exactly one JS breakpoint in the codebase
   (innerWidth > 760) which this does not contradict.
   ═══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 820px) {

  /* ── FOUNDATION ────────────────────────────────────────────────────────── */

  /* Nothing may push the page sideways. Measured before this file existed: the
     tracker already reflowed with zero horizontal overflow, so this is a guard
     against regression rather than a fix. */
  html, body {
    max-width: 100% !important;
    overflow-x: hidden !important;
  }

  /* A wide element scrolls INSIDE itself rather than dragging the page. */
  table,
  pre,
  .scroll-x {
    display: block;
    max-width: 100% !important;
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch;
  }

  /* iOS zooms the whole page when a focused input is under 16px. That zoom is
     not undone on blur, so one tap into a search box leaves the estimator
     permanently zoomed with no obvious way back. */
  input, select, textarea {
    font-size: 16px !important;
    max-width: 100% !important;
  }

  /* ── TAP TARGETS ───────────────────────────────────────────────────────── */
  /* MEASURED on the real tracker at 375px: 124 of 138 tap targets (90%) were
     under the 44px floor — 31px buttons, a 16px email link. This is the single
     largest usability gap, ahead of layout, which already reflowed. */

  button,
  .btn,
  [role="button"],
  input[type="button"],
  input[type="submit"],
  select {
    min-height: 44px !important;
    /* Not min-width: a full-width button in a stack must stay full width, and
       a 44px floor on width would break the grid it sits in. Padding does the
       horizontal work. */
    padding-top: 10px !important;
    padding-bottom: 10px !important;
  }

  /* A small button is small on purpose — it stays visually small, but its
     TOUCHABLE area is padded out to the floor. Shrinking the visual and growing
     the target is the whole trick. */
  .btn-sm,
  .btn.btn-sm {
    min-height: 44px !important;
    padding-top: 9px !important;
    padding-bottom: 9px !important;
  }

  /* Icon-only controls are the worst offenders: a 20px glyph with no padding. */
  .icon-btn,
  .btn-icon,
  button.icon {
    min-width: 44px !important;
    min-height: 44px !important;
  }

  /* NAVIGATION LINKS. The left rail's suite and section links render at 32px —
     the last group still under the floor after the input fix, and among the
     most-tapped things on the page. Padded rather than made taller so the rail's
     visual rhythm is unchanged; only the touchable area grows. */
  .nav-suite,
  .nav-item,
  .suite-group a,
  .nav a,
  nav a {
    min-height: 44px !important;
    display: flex !important;      /* SAFE: these are static links whose display
                                      no JavaScript toggles — the no-display rule
                                      is about elements the code shows and hides,
                                      and a nav link is never one of them. */
    align-items: center !important;
    padding-top: 6px !important;
    padding-bottom: 6px !important;
  }

  /* Links inside prose are NOT padded — that would wreck line spacing in a
     paragraph. Only standalone action links get the floor. */
  a.btn,
  a.backlink,
  .actions a,
  .act a {
    display: inline-block;
    min-height: 44px !important;
    line-height: 24px !important;
    padding-top: 10px !important;
    padding-bottom: 10px !important;
  }

  /* FOOTER CONTACT LINKS. On the public vendor page these render at 14px — a
     phone number, an email address, "Join the network". They are prose by
     markup but contact actions by intent, and a subcontractor on a phone is
     exactly who taps them. Given the floor via padding, so the footer's line
     rhythm is disturbed as little as possible.

     Deliberately NOT a blanket rule for every `a`: padding links inside a real
     paragraph wrecks its line spacing, which is why the app's body copy is
     left alone. */
  footer a,
  .footer a,
  .foot a,
  .page-foot a {
    display: inline-block;
    min-height: 40px !important;
    line-height: 26px !important;
    padding-top: 7px !important;
    padding-bottom: 7px !important;
  }

  /* Adjacent targets need a gap or the wrong one gets hit. 8px is the smallest
     that measurably helps. */
  .actions,
  .act,
  .modal-actions,
  .btn-row {
    display: flex !important;
    flex-wrap: wrap !important;
    gap: 8px !important;
  }

  /* Checkboxes and radios are ~13px by default — unhittable. */
  input[type="checkbox"],
  input[type="radio"] {
    min-width: 22px !important;
    min-height: 22px !important;
  }

  /* TEXT-ENTRY FIELDS ARE TAP TARGETS TOO, and this was the largest remaining
     gap after the first pass: 38 of them sat at 40-42px because the button rule
     above only covers input[type=button|submit] and select. A field you cannot
     reliably tap into is as bad as a button you cannot press — worse in the
     search box, which is the first thing an estimator touches.

     Enumerated rather than using a bare `input` selector, so checkbox and radio
     keep their own 22px sizing above and are not stretched into slabs. */
  input[type="text"],
  input[type="search"],
  input[type="email"],
  input[type="tel"],
  input[type="url"],
  input[type="number"],
  input[type="password"],
  input[type="date"],
  input[type="datetime-local"],
  input[type="month"],
  input[type="time"],
  /* A file input renders at ~24px — the smallest control in the app, and the
     one an estimator uses to attach a quote. Found by measuring the real
     "Add a quote" panel, not by reading the list. */
  input[type="file"],
  input:not([type]) {
    min-height: 44px !important;
  }

  /* The button INSIDE a file input is drawn by the browser and cannot be sized
     directly; padding the control is what grows its hit area. */
  input[type="file"] {
    padding: 10px 0 !important;
  }

  /* A textarea is already tall; it only needs to not be tiny. */
  textarea {
    min-height: 66px !important;
  }

  /* ── CHROME AND NAVIGATION ─────────────────────────────────────────────── */

  /* The top bar stacks and wraps instead of overflowing. NOT display — it is
     already flex in every page that uses it, so this only changes direction. */
  .bar,
  .topbar,
  .header-bar {
    flex-wrap: wrap !important;
    gap: 8px !important;
    padding: 10px 12px !important;
  }

  /* The spacer that pushes actions right on desktop must not force a wrap
     gap on a phone. */
  .bar .spacer,
  .topbar .spacer {
    flex-basis: 100% !important;
    height: 0 !important;
    margin: 0 !important;
  }

  /* The tab rail scrolls horizontally rather than wrapping into three rows and
     eating half the screen. */
  .tabs,
  .tabbar,
  .jump,
  .seg {
    flex-wrap: nowrap !important;
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .tabs::-webkit-scrollbar,
  .tabbar::-webkit-scrollbar,
  .jump::-webkit-scrollbar,
  .seg::-webkit-scrollbar { display: none; }

  .tabs > *,
  .tabbar > *,
  .jump > *,
  .seg > * {
    flex: 0 0 auto !important;
  }

  /* ── OVERLAYS → BOTTOM SHEETS (Bundle B) ───────────────────────────────── */
  /*
     THE RULE HERE IS WIDER THAN "NEVER SET display", and the reason is that
     each overlay family hides itself by a DIFFERENT property. Mapped before a
     line of this was written:

       .modal-scrim / .modal-box   opacity + pointer-events   (24 in the app)
       .drawer                     transform + opacity        (20)
       .overlay (board-room)       always flex
       .pv-wrap                    transform + visibility

     So this block never sets display, transform, opacity OR visibility on an
     overlay. Every one of those is somebody's show/hide mechanism, and taking
     one over would leave that panel stuck open — or stuck shut — on every
     phone, with no error anywhere to say so.

     What is safe is the BOX: width, max-width, max-height, radius, padding,
     and the scrim's flex alignment. That is enough to make a centred desktop
     dialog read as a bottom sheet without touching how it appears.
  */

  /* Align to the bottom edge. This is a flex property on a container that is
     ALREADY display:flex at all widths — it changes where the box sits, not
     whether the scrim is shown. */
  .modal-scrim {
    align-items: flex-end !important;
    justify-content: stretch !important;
    padding: 0 !important;
  }

  /* The dialog itself. On desktop this is max-width 380px / width 90%, which on
     a 375px screen leaves a cramped 337px box with no height ceiling — a long
     modal simply ran off the bottom with the actions unreachable. */
  .modal-box {
    width: 100% !important;
    max-width: 100% !important;
    /* Rounded at the top only: the bottom edge is the screen edge. */
    border-radius: 18px 18px 0 0 !important;
    /* THE ACTIONS MUST BE REACHABLE. Capping the height and scrolling inside is
       what makes a long modal usable rather than merely present. */
    max-height: 88vh !important;
    overflow-y: auto !important;
    -webkit-overflow-scrolling: touch;
    padding: 22px 16px calc(16px + env(safe-area-inset-bottom, 0px)) !important;
  }

  /* A modal's own footer of buttons stacks rather than squeezing three across. */
  .modal-box .modal-actions {
    flex-direction: column-reverse !important;
    align-items: stretch !important;
  }
  .modal-box .modal-actions .btn {
    width: 100% !important;
  }

  /* The drawer is centred by top/left 50% PLUS a translate that .drawer.on
     rewrites — so top, left and transform are all untouchable here. Only its
     box dimensions are safe to change. */
  .drawer {
    width: 100% !important;
    max-width: 100vw !important;
    max-height: 94vh !important;
    border-radius: 16px !important;
  }

  /* Board Room's overlay pads 48px top on desktop, which wastes a third of a
     phone screen before any content. */
  .overlay {
    padding: 12px 8px !important;
  }

  /* The bucket's preview panel slides in by transform — left alone — but its
     width is a desktop width. */
  .pv-wrap,
  .pv-box {
    width: 100% !important;
    max-width: 100% !important;
  }

  /* ── LAYOUT BREATHING ──────────────────────────────────────────────────── */

  /* Multi-column grids collapse to one. Only grids that declare explicit
     columns — auto-fit/minmax grids already do the right thing and are left
     alone (44 of the app's 75 grids are already fluid). */
  .grid-2,
  .grid-3,
  .two-col,
  .three-col,
  .field-row {
    grid-template-columns: 1fr !important;
  }

  .wrap,
  .container,
  .page {
    padding-left: 12px !important;
    padding-right: 12px !important;
  }

  /* A fixed-width panel becomes full width rather than clipping. */
  .card,
  .panel,
  .section {
    max-width: 100% !important;
  }
}

/* A phone in landscape, and small tablets, get the tap-target floor without the
   single-column collapse — there is room for two columns at 821-980px, and
   forcing one wastes it. */
@media (min-width: 821px) and (max-width: 980px) and (hover: none) {
  button, .btn, [role="button"], select {
    min-height: 44px !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   PHASE 5 — CONTROLS THAT ONLY EXIST ON HOVER
   ═══════════════════════════════════════════════════════════════════════════
   208 of the app's 237 :hover rules are cosmetic — a colour, a shadow, a 2px
   lift. Those simply never fire on a phone and nothing is lost.

   A handful are different: they hold a control at opacity:0 and reveal it on
   hover. Measured, none of them also sets pointer-events, and opacity:0 does
   NOT stop a tap. So on a phone right now these are INVISIBLE BUT LIVE:

     .nt-del       delete a note          invisible, tappable, destructive
     .nt-att-del   remove an attachment   invisible, tappable, destructive
     .pl-acts      project card actions   invisible, tappable
     .t-enter      "ENTER" label          decorative only

   Making them visible is therefore not decoration — it is the difference
   between pressing a delete button and pressing nothing you can see.

   :focus-within, which some of them have, does not rescue this on touch: to
   focus the control you must first tap it, and tapping it fires it. The
   fallback only works once the thing is already reachable.

   None of these is an overlay selector and no JavaScript writes .style.opacity
   on any of them (both checked before writing this), so opacity is safe to
   take over here in a way it would NOT be on a modal or drawer.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 820px) {
  .pl-acts,
  .nt-del,
  .nt-reply,
  .npaper .nt-att-del,
  .npaper .note-item .nt-del,
  .npaper .note-item .nt-reply,
  .bidwin-notepad .nt-del,
  .bidwin-notepad .nt-reply,
  .tile .t-enter,
  .asg-x,
  .card-menu,
  .nt-asg-none,
  .bid-noroom {
    opacity: 1 !important;
  }

  /* Now that a destructive × is actually drawn, give it room to be hit
     deliberately rather than by accident. */
  .nt-del,
  .nt-reply,
  .npaper .nt-att-del {
    min-width: 32px !important;
    min-height: 32px !important;
  }
}
