/* ============================================================================
 * mobile.css — ALL mobile styling for the mobile viewport (shorter side ≤600px).
 *
 * Loaded via <link rel="stylesheet" href="css/mobile.css?v=N"
 *                  media="(max-width: 600px), (max-height: 600px)"> in index.php
 *                  — the comma is OR, so this applies when EITHER dimension is
 *                  ≤600px, i.e. the shorter side is ≤600 (a phone in BOTH
 *                  orientations; a landscape S22 is ~915×412). Mirrors
 *                  UIMode.isMobile() in js/ui-mode.js. Desktop browsers download
 *                  it but never apply a rule, so desktop rendering is untouched.
 *                  No @media wrappers needed here; the <link media> attr is the gate.
 *
 * Companion behavior lives in js/gallery-mobile.js. Plan + audit in
 * docs/mobile.md. The old @media block in app.css migrated here 2026-06.
 * ========================================================================= */

/* ── APP FRAME ──
   dvh instead of vh so the layout tracks the real visible viewport when
   mobile browser chrome (URL bar / bottom bar) expands and collapses —
   with 100vh the statusbar sat behind Chrome's bottom UI. Statusbar is
   dropped entirely on mobile; the pagination row already carries counts. */
#app {
  height: 100vh;   /* fallback for old browsers */
  height: 100dvh;
  grid-template-rows: var(--toolbar-h) 1fr;
  /* NO top safe-area padding on purpose. With viewport-fit=cover (index.php) the
     page paints into the former status-bar / cutout strip; we want the toolbar to
     fill that strip in fullscreen, not reserve it. A padding-top:env(safe-area-
     inset-top) here re-creates the very gap we're trying to kill (the inset is
     non-zero in fullscreen on a punch-hole/notch phone). Non-fullscreen is
     unaffected — the system status bar sits above the viewport there regardless.
     This also matters as an installed PWA (standalone), where the same strip is
     in play. Reclaim is intentional even though the header row then sits level
     with a center punch-hole; the side controls (☰ / account) stay clear. */
}
#statusbar { display: none; }
#main { grid-template-columns: 1fr; }
#resize-handle { display: none; }

/* No bleed on narrow screens — every horizontal pixel counts. */
:root { --tile-bleed: 0px; }

/* ── TOOLBAR ──
   Mobile header = ☰ + title + active folder, nothing else. Every other
   toolbar control either relocates into the drawer footer (see below) or
   waits for the Phase 2 admin action bar. !important is required: the
   shuffle/screensaver/login/logout buttons have JS-managed INLINE display
   values that would otherwise win. */
#toolbar {
  gap: 6px;
  /* Pull content off the screen edges. max() reads the fixed value normally
     (insets 0) and auto-respects a curved/cut edge now that viewport-fit=cover
     is set (index.php) — the top inset is handled by #app's padding-top. */
  padding: 0 max(8px, env(safe-area-inset-right)) 0 max(12px, env(safe-area-inset-left));
}
/* Gallery name: shrink to fit a tight header (fluid down to 11px) and
   ellipsize as a last resort instead of pushing the folder label off-screen.
   Desktop keeps flex-shrink:0; here we let the title give way. */
#toolbar h1 {
  font-size: clamp(11px, 3.4vw, 14px);
  /* Grow to absorb leftover space so the view/like buttons stay pinned to the
     right. basis:0 (not auto) is deliberate: with auto the flex basis = the
     title's own content width, which depends on the font size the JS fitter is
     changing — a circular, unstable measurement. basis:0 makes this box's width
     purely the leftover space, so the fitter measures reliably. */
  flex: 1 1 0;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  /* Never ellipsize the title — the JS fitter shrinks it to fit instead. clip
     (not ellipsis) is the deliberate fallback if anything ever overflows. */
  text-overflow: clip;
}
#toolbar .divider { display: none; }
/* Active-view label is a tappable button on mobile — a bordered pill with a ▾
   chevron, signalling it opens navigation (wiring comes later). The chevron is
   a ::after pseudo-element, NOT a child: the JS view dispatchers overwrite the
   button's textContent/innerHTML on every view change, which would wipe a real
   child but leaves a pseudo-element intact. */
/* On mobile the pill lives on the SEARCH ROW (relocated into #grid-search-wrap by
   gallery-mobile.js), not the header. It's a slim single-line pill sized close to
   the search input: [.tf-ico] [.tf-name → .tf-name-text · .tf-count(inline)] ▾.
   mobileSyncFolderPill() builds that structure; the count renders inline as "(55)"
   next to the name. flex:0 1 + min-width:0 lets it shrink so the input keeps room. */
#toolbar-folder {
  /* Don't shrink: the search input's width:100% basis would otherwise squeeze the
     pill and (because .tf-name-text is overflow:hidden) collapse the folder name to
     nothing, leaving just the icon + count. The input (flex:1 1 auto) shrinks
     instead; max-width caps the pill so a long name still can't eat the whole row
     (ellipsis then kicks in on .tf-name-text). */
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  max-width: 48vw;
  min-width: 0;
  font-size: 18px;
  color: var(--text);
  /* Height is pinned so the JS font-fit (mobileSyncFolderPill shrinks the font
     when the name would ellipsize) never changes the bar height. */
  min-height: 40px;
  padding: 9px 10px;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: var(--surface2);
}
#toolbar-folder .tf-ico {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  font-size: 15px;
  line-height: 1;
}
/* The cover-thumb / face-crop icons (.tf-cover-thumb / .tf-facecrop) are styled
   in app.css (shared with desktop); .tf-ico just centers them in the pill. */
#toolbar-folder .tf-name {
  display: inline-flex;
  flex-direction: row;
  align-items: baseline;
  gap: 4px;
  line-height: 1.1;
  min-width: 0;
}
#toolbar-folder .tf-name-text {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
#toolbar-folder .tf-count {
  flex-shrink: 0;
  font-size: 14px;
  line-height: 1;
  color: var(--text-dim);
}
#toolbar-folder::after {
  content: '▾';
  flex-shrink: 0;
  font-size: 13px;
  color: var(--text-dim);
}
/* ── View menu — dropdown under the folder pill (non-admin; gallery-mobile.js
   positions it from the pill's rect and wires the rows). ── */
.view-menu {
  position: fixed;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  width: min(92vw, 380px);   /* wide + screen-centered: thumb-reachable rows */
  padding: 5px;
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55);
}
.view-menu[hidden] { display: none; }   /* display:flex would defeat [hidden] */
.vm-item {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  /* Left-aligned icon + label — same stroke icons as the hamburger sidebar
     (heart/book/tree/gallery), not centered emoji. */
  padding: 10px 18px 10px 18px;
  background: none;
  border: none;
  border-radius: 7px;
  color: var(--text);
  font-size: 17px;
  text-align: left;
}
.vm-item .vm-ico {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 1.35em;
  font-size: 1.15em; /* scale 1em stroke SVGs with the row */
  line-height: 1;
  color: inherit;
}
.vm-item .vm-ico .heart-svg,
.vm-item .vm-ico .book-svg,
.vm-item .vm-ico .tree-svg,
.vm-item .vm-ico .gallery-svg {
  width: 1em;
  height: 1em;
}
.vm-item.active { background: rgba(255, 255, 255, 0.09); }
/* Chevron flips while the menu is open. */
#toolbar-folder.vm-open::after { content: '▴'; }

/* My ❤ / My 📖 toggles ride the search row next to the pill — slim them to match
   the pill/input height (the mobile .tb-btn padding is too tall here). */
#btn-stars-mine, #btn-stories-mine {
  flex-shrink: 0;
  padding: 9px 10px;
  min-height: 40px;   /* match the pill's pinned height */
  font-size: 18px;
}
/* ≥44×44 tap target — the old ~24px target (inline font/padding, now stripped
   from the markup) was unreliable, especially near a curved screen edge. */
#btn-hamburger {
  display: flex !important;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  flex-shrink: 0;
}
#btn-app-fullscreen,
#btn-shuffle,
#btn-screensaver,
#btn-admin-login,
#btn-admin-logout,
#user-wrap,
#btn-config,
#btn-help { display: none !important; }
/* Admin file actions are unusable on touch until the Phase 2 selection
   model + bottom action bar exist — hide the whole strip for now. */
#admin-toolbar { display: none !important; }

/* ── TOUCH TARGETS ── */
.tb-btn { padding: 8px 12px; font-size: 13px; }
.page-btn { padding: 9px 16px; }
.sb-item { padding-top: 9px; padding-bottom: 9px; }
/* Keep nested face/folder rows compact (override the .sb-item bump). */
.sb-face-item { padding-top: 4px; padding-bottom: 4px; }
.sb-face-item.sb-face-top { padding-top: 7px; padding-bottom: 7px; }

/* ── SIDEBAR DRAWER ── */
#sidebar {
  position: fixed;
  top: var(--toolbar-h); bottom: 0;
  left: -340px; width: 80vw; max-width: 320px; z-index: 400;
  transition: left .25s ease;
  box-shadow: 4px 0 24px rgba(0,0,0,.6);
}
#sidebar.mobile-open { left: 0; }

#sidebar-backdrop {
  display: none; position: fixed; inset: 0;
  background: rgba(0,0,0,.5); z-index: 399;
}
#sidebar-backdrop.visible { display: block; }

/* Drawer footer — relocated toolbar controls. Markup lives in index.php
   (default-hidden by app.css); buttons proxy to the real hidden toolbar
   buttons via gallery-mobile.js so existing handlers stay untouched. */
#sb-mobile-footer {
  display: flex; flex-direction: column; gap: 6px;
  padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--border); background: var(--surface);
}
.sbf-btn {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 10px 12px; border-radius: 6px;
  border: 1px solid var(--border); background: var(--surface2);
  color: var(--text); font-size: 13px; cursor: pointer; text-align: left;
}
.sbf-btn:active { background: #333; }
/* Hide the ⛶ Fullscreen drawer button on mobile: the installed PWA is already
   fullscreen, so it's redundant and only summons the browser's native
   "Press Esc to exit full screen" banner (wrong on a phone, and not our text
   to reword). syncFooter() only ever resets this button's inline display to ''
   (never a visible value), so this stylesheet rule holds. */
#sbf-fullscreen { display: none; }

/* ── PAGINATION (sticky row above the grid) ──
   Two-line layout: full-width search on top, then a compact info-only line.
   The desktop 3-zone flex (search | controls | spacer) relies on wide rows;
   on a phone it wraps messily. No Prev/Next buttons on mobile — paging is by
   horizontal swipe on the grid (gallery-mobile.js); the label carries the
   count + page + swipe hint ("48 🖼 · Page 2 · swipe for next page"). */
#pagination-top { flex-wrap: wrap; gap: 4px 10px; transition: transform 0.22s ease; }
/* The sticky top bar auto-hides on scroll-down via this slide (gallery-mobile.js
   toggles .pg-hidden; always on, mobile). Pinned at top:0, so a -100% translate
   lifts it fully off the top edge and the grid (scrolling under the sticky bar)
   gains the space. Independent of `.hidden` (panels' full removal). */
#pagination-top.pg-hidden { transform: translateY(-100%); }

/* ── Header auto-hide ──
   The header (#toolbar — the first #app grid row, --toolbar-h tall) collapses on
   scroll-down alongside #pagination-top, and reappears on scroll-up / at the top.
   gallery-mobile.js toggles .hdr-hidden on #app (both orientations, mobile). We
   collapse the GRID ROW (not a transform): #content's 1fr then grows into the
   reclaimed space. The row animates --toolbar-h → 0 as a smooth roll-up;
   overflow:hidden clips the header content during the collapse. */
#app { transition: grid-template-rows 0.22s ease; }
#toolbar { overflow: hidden; }
#app.hdr-hidden { grid-template-rows: 0px 1fr; }
#grid-search-wrap { flex: 1 1 100%; gap: 6px; }
#grid-search { max-width: none; padding: 9px 10px; font-size: 18px; }
#pagination-top-controls { flex: 1; justify-content: center; }
#pagination-top-spacer { display: none; }
/* Buttons gone → the info line halves in height; trim its vertical padding too. */
#btn-prev-top, #btn-next-top { display: none; }
#pagination-top-controls { padding: 0; }
#page-info-top { font-size: 12px; line-height: 1.2; }
/* Small photo icon stands in for the word "shown"; inherits the dim text color. */
.pg-photo-ico { vertical-align: -2px; opacity: 0.85; }

/* Account banner styles moved to app.css (2026-06-29): the banner now also shows
   on desktop (signed-in non-admin users), and mobile.css only loads ≤600px — so
   its base styles must live in the always-on stylesheet. Nothing needed here. */
/* Fullscreen toggle now sits at the HEADER's right edge (relocated out of the
   search row 2026-07-01). The title's flex-grow pins it right; size it as a
   header tap target (matches the ☰ hamburger) with a plain transparent look so
   it doesn't read as a boxed button next to the borderless hamburger. Desktop
   keeps it display:none (app.css). Pull-to-refresh still covers refresh here. */
#btn-grid-fullscreen-m {
  display: inline-flex;
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-color: transparent;
  background: transparent;
}

/* Custom pull-to-refresh owns the top overscroll: contain stops the browser's
   own pull-to-reload (and the rubber-band) from firing underneath ours. */
#content { overscroll-behavior-y: contain; }

/* Grid pinch-zoom → thumb-size setting (gallery-mobile.js). pan-y lets the
   browser keep vertical scrolling but hands two-finger pinches that START on
   the grid to our touch machine instead of zooming the page. Pinch elsewhere
   (header, modals) still page-zooms — intentional. */
#content { touch-action: pan-y; }
/* While a grid-pinch is live, kill ALL browser gestures on #content
   (page zoom especially). JS also preventDefaults multi-touch moves. */
#content.content-pz { touch-action: none; }

/* Live pinch reflow: absolute paint while .pz-live is on (gallery-mobile.js).
   Pure-JS pack sets left/top/width/height + grid height; no flex reflow thrash.
   justifyGrid bails while this class is present. */
#grid.pz-live {
  position: relative !important;
  display: block !important;
  flex-wrap: unset !important;
  grid-template-columns: unset !important;
  gap: 0 !important;
  width: 100% !important;
  /* height set inline from pack totalH */
}
#grid.pz-live .cell {
  position: absolute !important;
  margin: 0 !important;
  flex: none !important;
  max-width: none !important;
  /* left/top/width/height set inline */
}

/* ── GRID ──
   No mobile row-height cap anymore (2026-06-12): justifyGrid() in
   gallery-core.js sizes every cell with inline width/height, and the
   thumb-size slider (max 300 on mobile, set in primePreferencesSection)
   is honored truthfully. The old min(var,150px) cap block also set a
   stylesheet max-width that clamped the justifier's inline widths —
   don't reintroduce it. */

/* ── LIGHTBOX ── */
.modal-box { width: 94vw !important; max-width: none !important; }
.modal-nav { padding: 20px 14px; font-size: 32px; }
#modal-prev { left: 0; border-radius: 0 4px 4px 0; }
#modal-next { right: 0; border-radius: 4px 0 0 4px; }
/* Long paths in the info pill: wrap instead of clipping off-screen
   (desktop keeps nowrap; verified clipped on Pixel 7 during Phase 1 QA).
   Explicit width (not max-width): left:50% + translateX makes the auto width
   shrink-to-fit to ~50vw, wasting horizontal space — a fixed 92vw uses the full
   row and wraps to fewer lines. */
#modal-info { font-size: 11px; width: 92vw; max-width: 92vw; text-align: center; white-space: normal; }
#modal-close { width: 44px; height: 44px; }
#modal-star, #modal-report { width: 40px; height: 40px; }

/* ── SCREENSAVER MODE PICKER ──
   savers/core.js positions #ss-menu under the toolbar button via inline
   top/left — but that button is hidden here (the drawer footer proxies
   it), so the rect is 0,0. Pin the picker as a bottom sheet instead;
   !important beats the stale inline coords. */
#ss-menu.open {
  top: auto !important; left: 8px !important;
  right: 8px; bottom: 12px; min-width: 0;
}
