/* xCRUD Pro app shell - see pages/app.php's own header comment for how
   this pairs with the Tailwind CDN theme (themes/tailwind/assets.php,
   already loaded by the page before this file). Only the layout
   mechanics Tailwind's utility classes don't cover inline (fixed 3-column
   positioning, the collapsed/mobile/slide-in-panel STATE transitions,
   RTL mirroring) live here - everything else is styled directly with
   Tailwind classes in XcrudPro.php's own renderMenu()/renderProfile()/
   renderNotifications() output. Written with logical properties
   (margin-inline-*, inset-inline-*) throughout specifically so RTL
   support (a later pass) is a `dir="rtl"` attribute + a small mirror-rule
   set, not a rewrite. */

:root {
    /* Single source of truth for both .app-sidebar-header's and
       .app-header's own row height, so their bottom borders always line
       up across the full page width - see each rule's own note. */
    --app-header-height: 92px;
    /* Read by each theme's own pagination CSS (themes/{bootstrap,tailwind}/
       assets.php, themes/none/xcrud.css) so a sticky-bottom pagination bar
       sits right above .app-bottom-tabs instead of underneath/behind it -
       0px here (and everywhere above the 640px breakpoint) since neither
       this variable's consumer nor .app-bottom-tabs itself exist outside
       that breakpoint. A plain CSS custom property, not a build-time
       constant, specifically so a widget's own stylesheet (loaded and
       compiled independently of this shell file, and reused as-is by
       pages that never load this shell at all - the Basic tier, a
       decoupled frontend) can read it without needing this file's own
       640px value hardcoded a second time anywhere else. */
    --app-bottom-tabs-space: 0px;
}

.app-layout {
    display: flex;
    min-height: 100vh;
}

/* ---- Sidebar (left) ---- */
.app-sidebar {
    width: 260px;
    flex-shrink: 0;
    background: #fff;
    border-inline-end: 1px solid #e2e8f0;
    display: flex;
    flex-direction: column;
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    z-index: 40;
    transition: transform .2s ease, width .2s ease;
}
.app-sidebar-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 16px;
    border-block-end: 1px solid #f1f5f9;
    flex-shrink: 0;
    /* Shared with .app-header's own min-height below (--app-header-height,
       declared on :root further down) - a page WITHOUT ->sub_label() has a
       shorter .app-header than one with it, so pinning THIS row to a bare
       "16px padding + logo height" would only ever line its own bottom
       border up with SOME pages' header border, not every one - direct
       feedback ("border line below logo is not same level and border
       line below title"), reproduced on a page that HAS a sub_label
       (taller .app-header than this row's own natural content height). */
    min-height: var(--app-header-height);
    box-sizing: border-box;
}
/* flex:1/min-width:0 (was .app-title's own, removed - the <img>'s own
   alt text is the fallback now, no separate text element next to it) so
   the logo fills the whole row instead of sitting at its own intrinsic
   width; object-fit/object-position keep it from stretching or
   distorting while it grows. logo.png's own real aspect ratio is 3:1 -
   at the previous 28px height that only ever reached ~84px wide inside a
   much wider flex:1 box (direct feedback: "occupying very small space") -
   44px reaches a visibly fuller width without the row itself needing to
   grow past --app-header-height to fit it. */
.app-logo {
    height: 44px;
    max-height: 44px;
    flex: 1;
    min-width: 0;
    max-width: 100%;
    object-fit: contain;
    object-position: left center;
}
/* app-logo-full/app-logo-mini: two separate <img> elements (not one
   swapped via JS) - the collapsed rail is only 76px wide (see
   .app-sidebar-collapsed .app-sidebar below), too narrow for logo.png's
   own wide 3:1 wordmark to stay legible, so it swaps to logo_mobile.png
   (a square/icon-shaped mark) instead of just shrinking the same image.
   Expanded by default; collapsed flips which one is visible. */
.app-logo-mini { display: none; }
.app-layout.app-sidebar-collapsed .app-logo-full { display: none; }
.app-layout.app-sidebar-collapsed .app-logo-mini {
    display: block;
    height: 32px;
    max-height: 32px;
    flex: 0 0 auto;
    object-fit: contain;
}
.app-collapse-btn,
.app-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 8px;
    color: #64748b;
    background: transparent;
    border: none;
    cursor: pointer;
    flex-shrink: 0;
}
.app-collapse-btn:hover,
.app-close:hover { background: #f1f5f9; color: #1e293b; }
/* Base chevron-right icon rotated 180deg by default so it POINTS LEFT
   while the sidebar is expanded (toward the edge it collapses to) -
   collapsing flips it back to its own natural right-pointing state
   (toward the content it will reveal on expand). The un-rotated icon
   alone would show the opposite, backwards pairing. */
.app-collapse-icon { transition: transform .2s ease; transform: rotate(180deg); }
.app-close { display: none; }

.app-sidebar-body {
    flex: 1;
    overflow-y: auto;
    padding-block: 8px;
}

/* Desktop icon-only collapse - see XcrudMenu.php's own
   xcrudToggleSidebarCollapse()/xcrudToggleMenuGroup(). Marker classes
   (xcrud-menu-label/-chevron/-badge/-search/-group-panel) come from
   XcrudPro::renderMenu()'s own output. */
.app-layout.app-sidebar-collapsed .app-sidebar { width: 76px; }
/* .app-logo is flex:1 normally (fills the whole header row - see its
   own rule above) - in the 76px collapsed rail that would stretch/
   squish a wide logo into a sliver next to the collapse button, so it
   drops back to its own intrinsic width here instead, same as the icon-
   only treatment .app-title's now-removed text used to leave it with. */
.app-layout.app-sidebar-collapsed .app-logo { flex: 0 0 auto; }
.app-layout.app-sidebar-collapsed .xcrud-menu-label,
.app-layout.app-sidebar-collapsed .xcrud-menu-chevron,
.app-layout.app-sidebar-collapsed .xcrud-menu-badge,
.app-layout.app-sidebar-collapsed .xcrud-menu-search,
.app-layout.app-sidebar-collapsed .xcrud-menu-group-panel { display: none; }
.app-layout.app-sidebar-collapsed .app-collapse-icon { transform: rotate(0deg); }
.app-layout.app-sidebar-collapsed .xcrud-menu a,
.app-layout.app-sidebar-collapsed .xcrud-menu button { justify-content: center; }

/* RTL: the sidebar itself already moves to the right edge for free
   (inset-inline-start/margin-inline-start below both flip automatically
   with the <html dir="rtl"> attribute - no rule needed for that part).
   The collapse icon's OWN two rotation states above still need
   explicit mirrored values though - "collapse" now means shrinking
   TOWARD the right edge, the opposite physical direction from LTR, so
   the un-rotated ("expanded") vs rotated ("collapsed") pairing flips too. */
[dir="rtl"] .app-collapse-icon { transform: rotate(0deg); }
[dir="rtl"] .app-layout.app-sidebar-collapsed .app-collapse-icon { transform: rotate(180deg); }

/* ---- Main (center) ---- */
.app-main {
    flex: 1;
    min-width: 0;
    margin-inline-start: 260px;
    margin-inline-end: 64px;
    transition: margin-inline-start .2s ease;
    display: flex;
    flex-direction: column;
}
.app-layout.app-sidebar-collapsed .app-main { margin-inline-start: 76px; }

.app-header {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 18px 24px;
    background: #fff;
    border-block-end: 1px solid #e2e8f0;
    position: sticky;
    top: 0;
    z-index: 20;
    /* See .app-sidebar-header's own note on --app-header-height (:root,
       top of this file) - a page with no ->sub_label() would otherwise
       be shorter than one with it, and either way shorter than the
       sidebar header next to it. min-height (not height) so a genuinely
       long sub_label that wraps to 2 lines still grows the row instead
       of clipping - the two borders just won't stay perfectly level on
       THAT one page, which is the better failure mode of the two. */
    min-height: var(--app-header-height);
    box-sizing: border-box;
}
.app-header h1 { margin: 0; font-size: 20px; font-weight: 700; color: #1e293b; }
/* ->sub_label() (Xcrud.php): stacks the h1 and its own optional second
   line vertically - this block is itself just ONE flex item within
   .app-header's own horizontal row (gap/align-items above), unaffected
   either way. min-width:0 lets a long sub_label ellipsis/wrap instead of
   forcing the whole header row wider than its own available space. gap
   (4px, was 2px) gives the sub_label line a bit more visible breathing
   room under the title instead of crowding it. */
.app-page-title-block { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.app-page-sub-label { margin: 0; font-size: 12.5px; font-weight: 400; color: #64748b; }
.app-toggle {
    display: none;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: 8px;
    border: 1px solid #e2e8f0;
    background: #fff;
    color: #475569;
    cursor: pointer;
    flex-shrink: 0;
}
/* Pushes the two mobile-only rail-mirror buttons below to the header's
   own far end, same edge .app-rail itself sits at on desktop - without
   this they'd sit right after <h1>, crowding the (often long) page
   title instead of anchoring to the opposite side the way an icon rail
   reads naturally. Only matters once those buttons are actually visible
   (the mobile breakpoint below) - harmless empty flex space otherwise. */
.app-header-spacer { flex: 1; }
/* core/xcrud.js's own render() drops "Total entries"/View Code into this -
   see XcrudPro::renderShell()'s own note on why (moved out of the
   toolbar, which they used to crowd) and how multiple widgets/an AJAX
   in-place nav share this one persistent slot safely. */
.app-header-widget-slot {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    font-size: 12.5px;
    color: #64748b;
}
/* Hidden on desktop - .app-rail itself is the real trigger there. Shown
   (see the mobile breakpoint below) as its REPLACEMENT once the rail
   itself is hidden, not an addition alongside it - same
   xcrudTogglePanel() calls/panels either way, just a different trigger
   element for a screen too narrow to spare a permanent side column. */
.app-header-rail-btn {
    display: none;
    position: relative;
    width: 36px;
    height: 36px;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    border: none;
    background: transparent;
    color: #64748b;
    cursor: pointer;
    flex-shrink: 0;
}
.app-header-rail-btn:hover { background: #f1f5f9; color: #1e293b; }
.app-content { padding: 16px;
    padding-top: 16px;
  flex: 1;
  padding-top: 5px; }

/* ---- Right icon rail ---- */
.app-rail {
    width: 64px;
    flex-shrink: 0;
    background: #fff;
    border-inline-start: 1px solid #e2e8f0;
    position: fixed;
    inset-block: 0;
    inset-inline-end: 0;
    z-index: 40;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding-block-start: 16px;
}
.app-rail-btn {
    position: relative;
    width: 42px;
    height: 42px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 10px;
    border: none;
    background: transparent;
    color: #64748b;
    cursor: pointer;
}
.app-rail-btn:hover { background: #f1f5f9; color: #1e293b; }
/* Gmail-style: the Account panel own trigger (both the desktop rail
   button above and its mobile header mirror) is this circle instead of a
   generic icon - see XcrudPro::renderShell()'s own $userAvatarHtml. */
.app-avatar-circle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 999px;
    background: #4468e0;
    color: #fff;
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1;
    flex-shrink: 0;
}
.app-rail-badge {
    position: absolute;
    top: 2px;
    inset-inline-end: 2px;
    min-width: 16px;
    height: 16px;
    border-radius: 999px;
    background: #ef4444;
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding-inline: 3px;
    line-height: 1;
}

/* ---- Bottom tab bar (phone-width only - see the 640px media query at
   the bottom of this file for where it actually becomes visible; hidden
   here by default so tablet/desktop never reserve space for it). */
.app-bottom-tabs {
    display: none;
    position: fixed;
    inset-inline: 0;
    inset-block-end: 0;
    z-index: 40;
    background: #fff;
    border-block-start: 1px solid #e2e8f0;
    box-shadow: 0 -6px 20px rgba(15, 23, 42, .06);
    /* env(safe-area-inset-bottom): the home-indicator gesture bar on a
       notched phone (iOS Safari/the Android WebView shell alike) sits
       UNDER a fixed bottom bar unless padding actively clears it - falls
       back to 0 on anything that doesn't support env(), same as this
       codebase's own dir="rtl" logical-property convention degrading
       gracefully elsewhere. */
    padding-block: 6px calc(6px + env(safe-area-inset-bottom, 0px));
}
.app-bottom-tab {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding-block: 4px;
    border: none;
    background: transparent;
    color: #64748b;
    font-size: 11px;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
}
.app-bottom-tab svg,
.app-bottom-tab .app-avatar-circle { flex-shrink: 0; }
.app-bottom-tab-active,
.app-bottom-tab:hover { color: #4468e0; }
.app-bottom-tab-icon-wrap { position: relative; display: inline-flex; }

/* ---- Mobile home screen (phone-width only, pages/app.php - see the
   640px media query at the bottom of this file for where this actually
   shows/the desktop cards actually hide). One tile per top-level sidebar
   group (XcrudPro::loadMenu() - the exact same data renderMenu() itself
   uses, so this never drifts out of sync with the real sidebar the way a
   separately hand-maintained list would), each a large icon over a
   pastel background, cycling through a small fixed palette by position
   rather than anything content-aware - sys_menu has no "color" column of
   its own to read a real per-group color from. */
.mobile-home-tiles { display: none; }
.mobile-home-greeting { margin-block-end: 18px; }
.mobile-home-greeting h2 { font-size: 19px; font-weight: 700; color: #0f172a; margin: 0 0 2px; }
.mobile-home-greeting p { font-size: 13px; color: #64748b; margin: 0; }
.mobile-home-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}
.mobile-home-tile {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px;
    background: #fff;
    border: 1px solid #e2e8f0;
    border-radius: 14px;
    text-decoration: none;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .04);
}
.mobile-home-tile-icon {
    flex-shrink: 0;
    width: 42px;
    height: 42px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 12px;
    color: #fff;
    font-size: 18px;
}
.mobile-home-tile-label {
    font-size: 13px;
    font-weight: 600;
    color: #1e293b;
    line-height: 1.3;
}
/* Fixed palette cycling by position (nth-child), not per-group meaning -
   see this section's own opening note on why (no color column to read a
   real one from). */
.mobile-home-tile:nth-child(6n+1) .mobile-home-tile-icon { background: #4468e0; }
.mobile-home-tile:nth-child(6n+2) .mobile-home-tile-icon { background: #8b5cf6; }
.mobile-home-tile:nth-child(6n+3) .mobile-home-tile-icon { background: #16a34a; }
.mobile-home-tile:nth-child(6n+4) .mobile-home-tile-icon { background: #f97316; }
.mobile-home-tile:nth-child(6n+5) .mobile-home-tile-icon { background: #ec4899; }
.mobile-home-tile:nth-child(6n+6) .mobile-home-tile-icon { background: #0891b2; }

/* ---- Slide-in panels (Notifications / Account) ---- */
.app-panel-overlay {
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, .35);
    opacity: 0;
    pointer-events: none;
    transition: opacity .2s ease;
    z-index: 49;
}
.app-panel-overlay-open { opacity: 1; pointer-events: auto; }
.app-panel {
    position: fixed;
    inset-block: 0;
    inset-inline-end: 0;
    width: min(360px, 100vw);
    background: #fff;
    box-shadow: -8px 0 24px rgba(15, 23, 42, .12);
    transform: translateX(100%);
    transition: transform .25s ease;
    z-index: 50;
    display: flex;
    flex-direction: column;
}
[dir="rtl"] .app-panel { transform: translateX(-100%); box-shadow: 8px 0 24px rgba(15, 23, 42, .12); }
/* .app-panel-open alone (specificity 0,1,0) is LESS specific than the
   [dir="rtl"] rule above (0,2,0) - the RTL closed-state transform would
   otherwise always win and the panel could never actually open while
   dir="rtl" is set, regardless of source order. Matching the RTL rule's
   own specificity here (.app-panel.app-panel-open, 0,2,0) - placed AFTER
   it - is what makes the open state correctly win in both directions. */
.app-panel.app-panel-open { transform: translateX(0); }
.app-panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px;
    border-block-end: 1px solid #f1f5f9;
    font-weight: 700;
    color: #1e293b;
    flex-shrink: 0;
}
.app-panel-header button {
    border: none;
    background: transparent;
    color: #64748b;
    cursor: pointer;
    width: 32px;
    height: 32px;
    border-radius: 8px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.app-panel-header button:hover { background: #f1f5f9; color: #1e293b; }
.app-panel-body { flex: 1; overflow-y: auto; }

/* ---- Sidebar mobile overlay ---- */
.app-sidebar-overlay {
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, .35);
    opacity: 0;
    pointer-events: none;
    transition: opacity .2s ease;
    z-index: 39;
}

@media (max-width: 1024px) {
    .app-sidebar { transform: translateX(-100%); width: 280px; }
    [dir="rtl"] .app-sidebar { transform: translateX(100%); }
    .app-layout.app-sidebar-open .app-sidebar { transform: translateX(0); }
    .app-layout.app-sidebar-open .app-sidebar-overlay { opacity: 1; pointer-events: auto; }
    .app-layout.app-sidebar-collapsed .app-sidebar { width: 280px; }
    /* The desktop-only icon-rail collapse is moot below this breakpoint
       (see the 280px full-width override just above) - revert the logo
       swap too, or a stale "collapsed" class carried over from a desktop
       session would show the tiny mini logo inside this full-width mobile
       sidebar instead of the real wordmark. */
    .app-layout.app-sidebar-collapsed .app-logo-full { display: block; }
    .app-layout.app-sidebar-collapsed .app-logo-mini { display: none; }
    .app-layout .app-main,
    .app-layout.app-sidebar-collapsed .app-main { margin-inline-start: 0; }
    .app-main { margin-inline-end: 56px; }
    .app-rail { width: 56px; }
    .app-close { display: inline-flex; }
    .app-collapse-btn { display: none; }
    .app-toggle { display: inline-flex; }
}

/* True mobile (phone-width, not just a narrower desktop/tablet - the
   1024px breakpoint above already turned the sidebar into an off-canvas
   drawer at that width, this one goes further): the right rail goes from
   "56px but still permanently there" to genuinely GONE - direct user
   feedback that a fixed-width column reserved for two icons is real
   space a small screen can't spare, not just something to shrink a
   little. Its two functions (Notifications/Account) - and the hamburger
   menu-open button - all live in .app-bottom-tabs now (see that section
   above), so none of them need a header seat either; .app-header-rail-btn
   was their first stop (a mobile mirror of the rail, moved into the
   header) before the bottom tab bar existed to hold them properly - kept
   hidden here rather than removed outright since renderShell() still
   renders those two buttons unconditionally (harmless dead markup, one
   fewer PHP-side branch to keep in sync with this CSS). */
@media (max-width: 640px) {
    .app-rail { display: none; }
    .app-main { margin-inline-end: 0; }
    .app-header-rail-btn { display: none; }
    .app-toggle { display: none; }
    /* #appHeaderWidgetSlot only ever holds the View Code button today
       (see core/xcrud.js's own render() note - "Total entries" was
       deliberately pulled back OUT of this slot a while ago) - sitting
       inline in the header row on a phone left it competing with the
       page title/sub_label for the SAME narrow row, squeezing both.
       Floated out of flow instead - .app-header is already position:
       sticky, which (same as relative/fixed/absolute) qualifies as a
       containing block for an absolutely-positioned descendant, so this
       anchors to the header's own box without needing a new wrapper. */
    .app-header-widget-slot {
        position: absolute;
        inset-inline-end: 16px;
        inset-block-start: 14px;
        font-size: 11px;
    }
    .app-header-spacer { display: none; }
    /* Extra top padding (was 12px all round, matching the other three
       sides) specifically so the header title doesn't sit flush against
       the true top edge of the viewport on a phone - direct feedback
       that used to apply to .app-toggle (the hamburger, hidden now) but
       equally applies to the title taking its place at the header's own
       leading edge. */
    .app-header { padding: 18px 16px 12px; gap: 10px; }
    .app-header h1 { font-size: 17px; }
    .app-page-sub-label { font-size: 11.5px; }
    /* :root override, not a fresh declaration - this IS the same custom
       property declared at :root above (0px there), redefined here to its
       real height now that .app-bottom-tabs actually shows. Every other
       rule in this file that needs that height (this selector's own
       padding-bottom just below) reads the variable instead of repeating
       the calc() a second time, and so does each theme's own pagination
       CSS - see that variable's own top-of-file note. */
    :root { --app-bottom-tabs-space: calc(58px + env(safe-area-inset-bottom, 0px)); }
    /* padding-bottom clears .app-bottom-tabs' own fixed height so the
       last bit of a long page's content isn't hidden underneath it - the
       bar is position:fixed, so it takes no space of its own in normal
       flow otherwise. */
    .app-content { padding: 14px 14px var(--app-bottom-tabs-space); }
    .app-bottom-tabs { display: flex; }

    /* A modal/drawer's own overlay (core/xcrud.js - every openOverlay()-
       style caller now adds this marker class alongside its own theme's
       overlay class) is normally a full `inset:0` viewport cover with the
       panel flex-centered inside it - on a long form, that let the
       panel's own max-height (~90vh) extend UNDER .app-bottom-tabs,
       hiding the Save/Cancel footer behind it with no way to scroll
       further and reach them (found via a genuinely tall form - Base
       Fields - on a short-viewport phone). Shrinking the overlay's own
       box by the tab bar's height, rather than the panel's max-height
       directly, works the same way regardless of which theme's own
       overlay/panel sizing rules are in play - the panel still centers
       itself within whatever box it's given, just a shorter one now.
       height:auto is needed alongside bottom - Bootstrap's own overlay
       class sets an explicit h-100 (top+height, not top+bottom), which
       would otherwise make the bottom override below a no-op. */
    .xcrud-mobile-safe-overlay {
        bottom: var(--app-bottom-tabs-space, 0px) !important;
        height: auto !important;
    }
    /* Shrinking the overlay's own box (above) alone isn't enough - each
       theme's own panel/drawer class caps its height with a VIEWPORT-
       relative unit (e.g. max-h-[90vh]), which is measured against the
       true viewport regardless of how short its own flex-centering
       container is, so the panel could still be taller than the
       shrunken overlay and spill back into the tab bar. The panel is
       always the overlay's own direct/only child (every openOverlay()-
       style caller does overlay.appendChild(panel) with nothing between
       them), so this reaches it without needing its own marker class
       across every panel/drawer variant (tailwind/bootstrap/none). */
    .xcrud-mobile-safe-overlay > * {
        max-height: calc(90vh - var(--app-bottom-tabs-space, 0px)) !important;
    }

    /* Home screen only (pages/app.php) - swap the desktop "Getting
       Started + Browse by feature" cards for the large-icon tile grid,
       one tile per top-level sidebar group, matching the bottom tab
       bar's own "Home" tab landing here. */
    .desktop-home-content { display: none; }
    .mobile-home-tiles { display: block; }
}
