The whole site sat 36px to the right on phones and tablets, with a narrow empty
gutter down the left that held nothing but the hamburger.
Sidebar rendered the mobile trigger as a plain child of the content flex row in
the root layout. Below lg the desktop rail is display:none and the drawer is
position:fixed, so that button was the only sibling still taking width before
<main> — and main is flex-1, so it filled only what was left.
Measured on the dev server: at 375px main was x=36 w=339; at 768px x=36 w=732.
1280px was correct (x=256 w=1024), because the button is lg:hidden there. This
was never a horizontal-scroll bug — scrollWidth equalled clientWidth at both
widths. It was a permanent indent, and the content was 36px narrower than the
screen.
The side menu itself was fine: the drawer is fixed, so it contributed nothing to
layout and opened at x=0, 288x812, with scroll lock working. Only its trigger
was in the wrong place.
- Sidebar now exports the desktop rail on its own — hidden lg:block, so it can
never take width on mobile — plus MobileNav, which owns the trigger and drawer.
- The trigger moved into the full-bleed header, so the button costs no content
width, and it is w-11 h-11 (44x44) with -ml-3, which puts its icon's left edge
on the logo's 16px edge without moving the logo.
- The drawer and overlay are portalled to document.body. The header carries
backdrop-blur, and a backdrop-filter makes an element the containing block for
its position:fixed descendants — inline, the drawer would have sized itself to
the 64px header box. Commented in place so the portal is not removed later.
- The desktop rail and the drawer now render one shared NavList, so the two navs
cannot drift apart.
- Header row gap is gap-2 on mobile: the 44px trigger narrows the search field
from 247px to 215px at 375px, and the search is the only thing that yields.
Verified in the browser: main is x=0 w=375 at 375px and x=0 w=768 at 768px, with
no page overflow at either. The drawer is still full-viewport height and paints
correctly above Leaflet on /plant/map; tapping a drawer link navigates and closes
it; the scroll lock releases. /income/invoices, the widest table, has no element
wider than the viewport. Typecheck and build green.