/* A single narrow column: full-bleed on mobile, centered on desktop. No phone-mockup framing (border/shadow) — real phones cover that job. A viewport-height flex column so the app bar can stay fixed while an inner region scrolls (crate/browse), rather than the whole body scrolling. */ .app { width: 100%; max-width: 390px; margin: 0 auto; height: 100vh; height: 100dvh; background: var(--bg); position: relative; display: flex; flex-direction: column; overflow: hidden; } /* The signed-out page scrolls on its own within the shell. */ .app > .signin { flex: 1; min-height: 0; overflow-y: auto; } /* The authed shell: masthead (fixed) over the routed page (fills the rest); the bottom tab bar is viewport-fixed (see .bottom-bar), not a flex child. */ .shell { flex: 1; min-height: 0; display: flex; flex-direction: column; } .shell > *:not(.masthead):not(.bottom-bar) { flex: 1; min-height: 0; } /* A drill-down page (add/scan/record/inbox): its own back+title header over a single scrolling body, same shape as .list-screen below. */ .sub-page { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; } .sub-page > .app-bar { flex-shrink: 0; } .sub-page > *:not(.app-bar) { flex: 1; min-height: 0; } /* A list screen (crate/browse): a fixed hero + toolbar band over a single scrolling content region. */ .list-screen { display: flex; flex-direction: column; overflow: hidden; } .list-body { flex: 1; min-height: 0; display: flex; flex-direction: column; } .list-scroll { flex: 1; min-height: 0; overflow-y: auto; /* Clear the fixed bottom bar so the last items / LOAD MORE aren't hidden. */ padding-bottom: calc(72px + env(safe-area-inset-bottom)); } /* A page that scrolls as a whole (add/settings/record/scan/inbox/browse/ feed); same fixed-bottom-bar clearance as .list-scroll above. */ .page-scroll { overflow-y: auto; padding-bottom: calc(72px + env(safe-area-inset-bottom)); }