/* =============================================================================
   RumiNest360 — application shell (Phase 16B, re-shaped in Phase 21B)

   The sidebar, the top bar, the breadcrumb row, the content column and the
   footer, in the shape of Vuexy's vertical layout: a white sidebar with
   uppercase menu headers and a gradient active item, and a detached navbar card
   that floats below the top of the window.

   Three shell states, all of them a class on .app-shell that site.js sets — the
   layout renders statically, so the state cannot live in component state, and
   the content policy forbids an inline handler:

     (none)      >= 1280px   sidebar expanded, 260px
     .nav-rail   >= 768px    sidebar collapsed to an 80px icon rail, which
                             expands over the page while hovered or focused
                             (the default between 768px and 1279px)
     .nav-open   <  768px    sidebar off-canvas, opened over the page

   PHASE 13 CONTRACT, DELIBERATELY REPLACED (approved for Phase 16): the sidebar
   is styled at every width, not only below the md breakpoint. What the old
   contract protected — the sidebar never in flow on a narrow screen, and a wide
   table scrolling inside its own box rather than widening the page — is kept and
   is still asserted by WebResponsiveLayoutTests.
   ========================================================================== */

/* =============================================================================
   Frame

   A two-track grid. The sidebar is fixed, so track 1 is a spacer whose width is
   stated explicitly; that lets the rail expand over the content on hover instead
   of pushing it sideways.
   ========================================================================== */
.app-shell {
    display: grid;
    grid-template-columns: var(--sm-sidebar-w) minmax(0, 1fr);
    min-height: 100vh;
    min-height: 100dvh;
    background-color: var(--sm-page);
    transition: grid-template-columns var(--sm-mid) var(--sm-ease);
}

/* min-width: 0 lets this column be narrower than what it holds, so a wide table
   scrolls inside its own .table-responsive box instead of widening the page.
   --sm-gutter is the one side margin the navbar, the breadcrumb, the page and
   the footer all share, so their edges line up. */
.app-content {
    --sm-gutter: 1.5rem;
    grid-column: 2;
    min-width: 0;
    display: flex;
    flex-direction: column;
    background-color: var(--sm-page);
}

.app-main {
    flex: 1 1 auto;
    padding: var(--sm-s-5) var(--sm-gutter) var(--sm-s-6);
    min-width: 0;
}

.app-page {
    width: 100%;
    max-width: var(--sm-content-max);
    margin: 0 auto;
    min-width: 0;
}

/* The pages reached before signing in have the window to themselves: no sidebar to
   fill and nobody to name in a top bar (AuthLayout). */
.auth-shell {
    min-height: 100vh;
    min-height: 100dvh;
    background-color: var(--sm-page);
}

/* =============================================================================
   Sidebar
   ========================================================================== */
.app-sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    z-index: var(--sm-z-sidebar);
    width: var(--sm-sidebar-w);
    display: flex;
    flex-direction: column;
    background-color: var(--sm-nav-bg);
    color: var(--sm-nav-ink);
    box-shadow: var(--sm-shadow-nav);
    transition: width var(--sm-mid) var(--sm-ease),
                transform var(--sm-mid) var(--sm-ease),
                box-shadow var(--sm-mid) var(--sm-ease);
}

/* Inherited switches drive the rail. Keeping them as custom properties means the
   rail and its hover-expanded state are described once here rather than
   duplicated across every label, link, header and chevron below. */
.app-sidebar {
    --sm-nav-label: block;
    --sm-nav-flex: flex;
    --sm-nav-rule: none;
    --sm-nav-just: flex-start;
    --sm-nav-sub: block;
    --sm-nav-brand-pad: 0 0.625rem 0 1.125rem;
}

/* ----------------------------------------------------------------- the brand */
/* In the rail the row is centred on the mark; expanded, it runs from the left
   with the rail control at the far end. */
.app-nav-brand {
    display: flex;
    align-items: center;
    justify-content: var(--sm-nav-just, flex-start);
    gap: var(--sm-s-2);
    height: 64px;
    flex: none;
    padding: var(--sm-nav-brand-pad, 0 0.625rem 0 1.125rem);
}

.app-nav-brand-group {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    min-width: 0;
    flex: 0 1 auto;
}

.app-nav-brand-link {
    display: flex;
    align-items: center;
    gap: var(--sm-s-3);
    min-width: 0;
    color: var(--sm-ink);
    text-decoration: none;
}

.app-nav-brand-link:hover,
.app-nav-brand-link:focus-visible {
    text-decoration: none;
    color: var(--sm-ink);
}

.app-nav-brand-link:focus-visible {
    outline: 2px solid var(--sm-focus);
    outline-offset: 3px;
}

.app-nav-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    flex: none;
    border-radius: var(--sm-r-sm);
    background: var(--sm-primary-gradient);
    color: #fff;
    box-shadow: var(--sm-shadow-primary);
}

/* The product name is configuration (Branding:ProductName). It is sized to fit
   beside the mark and the rail control, and ellipsed rather than overlapping
   if a longer one is chosen. */
.app-nav-product {
    display: var(--sm-nav-label, block);
    min-width: 0;
    overflow: hidden;
}

.app-nav-product-name {
    display: block;
    font-size: 0.9375rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.25;
    color: var(--sm-ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.app-nav-product-meta {
    display: block;
    font-size: var(--sm-text-xs);
    color: var(--sm-ink-3);
    white-space: nowrap;
}

/* The vendor's name, linked to its website (VendorLink). It keeps the colour of the
   line it sits in — sidebar, footer, sign-in panel — and underlines only on hover. */
.sm-vendor-link {
    color: inherit;
    text-decoration: none;
}

.sm-vendor-link:hover,
.sm-vendor-link:focus-visible {
    color: inherit;
    text-decoration: underline;
}

/* The rail control, at the far end of the brand row as Vuexy's is. */
.app-nav-collapse {
    display: var(--sm-nav-flex, flex);
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    flex: none;
    margin-left: auto;
    padding: 0;
    border: 0;
    border-radius: var(--sm-r-pill);
    background: none;
    color: var(--sm-ink);
    transition: background-color var(--sm-fast) var(--sm-ease),
                color var(--sm-fast) var(--sm-ease);
}

.app-nav-collapse:hover {
    background-color: var(--sm-nav-hover);
}

.app-nav-collapse .sm-icon {
    transition: transform var(--sm-mid) var(--sm-ease);
}

/* The close button belongs to the off-canvas drawer and is shown only there. */
.app-nav-close {
    display: none;
}

/* --------------------------------------------------------- the scrolling list */
.app-nav-scroll {
    flex: 1 1 auto;
    overflow-y: auto;
    overflow-x: hidden;
    padding: var(--sm-s-1) 0.875rem var(--sm-s-6);
    overscroll-behavior: contain;
}

.app-nav-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* --------------------------------------------------------------- menu header
   Vuexy's uppercase section header. In the rail it becomes a short rule, so the
   groups are still told apart when the words are gone. */
.app-nav-header {
    list-style: none;
    margin: var(--sm-s-4) 0 var(--sm-s-1);
    padding: 0 var(--sm-s-3);
    height: 1.5rem;
    display: flex;
    align-items: center;
    justify-content: var(--sm-nav-just, flex-start);
}

.app-nav-list > .app-nav-header:first-child {
    margin-top: var(--sm-s-2);
}

.app-nav-header-text {
    display: var(--sm-nav-label, block);
    font-size: var(--sm-text-sm);
    font-weight: 400;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--sm-ink-3);
    white-space: nowrap;
}

.app-nav-header::after {
    content: "";
    display: var(--sm-nav-rule, none);
    width: 1.125rem;
    height: 1px;
    background-color: var(--sm-ink-4);
}

/* ----------------------------------------------------------------- nav links */
.app-nav-item,
.app-nav-group {
    list-style: none;
}

.app-nav-link {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--sm-s-2);
    justify-content: var(--sm-nav-just, flex-start);
    width: 100%;
    min-height: 38px;
    padding: 0.4375rem var(--sm-s-3);
    margin-top: 0.1875rem;
    border: 0;
    border-radius: var(--sm-r-sm);
    background: none;
    color: var(--sm-ink);
    font-size: var(--sm-text);
    font-weight: 400;
    line-height: 1.4;
    text-align: left;
    text-decoration: none;
    transition: background-color var(--sm-fast) var(--sm-ease),
                color var(--sm-fast) var(--sm-ease);
}

.app-nav-link:hover {
    background-color: var(--sm-nav-hover);
    color: var(--sm-ink);
    text-decoration: none;
}

.app-nav-link:focus-visible {
    outline: 2px solid var(--sm-focus);
    outline-offset: -2px;
}

.app-nav-link > .sm-icon:first-child {
    width: 1.375rem;
    height: 1.375rem;
    flex: none;
    color: var(--sm-ink);
}

.app-nav-label {
    display: var(--sm-nav-label, block);
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Where you are: Vuexy's gradient pill with white text on its soft primary
   shadow. NavLink adds .is-active and aria-current on the same element. The
   gradient runs from #1e676b (6.5:1) under the words to the brand hue at the
   far end, so a label of normal length never stands on less than 4.5:1. */
.app-nav-link.is-active,
.app-nav-link.is-active:hover {
    background: var(--sm-primary-gradient);
    color: #fff;
    font-weight: 500;
    box-shadow: var(--sm-shadow-primary);
}

.app-nav-link.is-active > .sm-icon:first-child {
    color: #fff;
}

.app-nav-link.is-active:focus-visible {
    outline-color: var(--sm-primary-700);
    outline-offset: 2px;
}

/* --------------------------------------------------------- a titled group */
.app-nav-group-head {
    cursor: pointer;
}

.app-nav-group-chevron {
    display: var(--sm-nav-flex, flex);
    flex: none;
    margin-left: auto;
    color: var(--sm-ink-3);
    transition: transform var(--sm-fast) var(--sm-ease);
}

.app-nav-group-head[aria-expanded="true"] .app-nav-group-chevron {
    transform: rotate(90deg);
}

/* The open group, and the group holding the page, are shaded like a hover. */
.app-nav-group-head[aria-expanded="true"],
.app-nav-group:has(.app-nav-link.is-active) > .app-nav-group-head {
    background-color: var(--sm-nav-hover);
}

/* The accessible state is the styling state: one source of truth for whether a
   group is open, and it is the same thing a screen reader is told. */
.app-nav-group-head[aria-expanded="false"] + .app-nav-group-items {
    display: none;
}

.app-nav-group-items {
    display: var(--sm-nav-sub, block);
}

/* A sub-item: Vuexy's small bullet under the parent's icon column. */
.app-nav-sublink {
    padding-left: var(--sm-s-3);
}

.app-nav-bullet {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.375rem;
    height: 1.375rem;
    flex: none;
}

.app-nav-bullet::before {
    content: "";
    width: 0.375rem;
    height: 0.375rem;
    border-radius: 50%;
    background-color: var(--sm-ink-3);
    transition: box-shadow var(--sm-fast) var(--sm-ease);
}

.app-nav-sublink.is-active .app-nav-bullet::before {
    background-color: #fff;
    box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.3);
}

/* =============================================================================
   The rail (>= 768px)

   The grid track narrows and the sidebar narrows with it. Hovering or tabbing
   into the sidebar expands it back over the content — nothing on the page moves,
   so a pointer heading for a link never has the page shift underneath it.

   In the rail only the top-level rows show, by their icons, as Vuexy's collapsed
   menu does: a group's entries come back when the sidebar expands.
   ========================================================================== */
@media (min-width: 768px) {
    .app-shell.nav-rail {
        grid-template-columns: var(--sm-sidebar-rail-w) minmax(0, 1fr);
    }

    .app-shell.nav-rail .app-sidebar {
        width: var(--sm-sidebar-rail-w);
        --sm-nav-label: none;
        --sm-nav-flex: none;
        --sm-nav-rule: block;
        --sm-nav-just: center;
        --sm-nav-sub: none;
        --sm-nav-brand-pad: 0;
    }

    .app-shell.nav-rail .app-sidebar:hover,
    .app-shell.nav-rail .app-sidebar:focus-within {
        width: var(--sm-sidebar-w);
        box-shadow: var(--sm-shadow-3);
        --sm-nav-label: block;
        --sm-nav-flex: flex;
        --sm-nav-rule: none;
        --sm-nav-just: flex-start;
        --sm-nav-sub: block;
        --sm-nav-brand-pad: 0 0.625rem 0 1.125rem;
    }

    .app-shell.nav-rail .app-nav-collapse .sm-icon {
        transform: rotate(180deg);
    }
}

/* Below 1280px the rail is the default: at that width the expanded sidebar costs
   the content more than it earns. A choice made from a toggle wins over this,
   because site.js then puts the class on the shell itself. */
@media (min-width: 768px) and (max-width: 1279.98px) {
    .app-shell:not(.nav-wide) {
        grid-template-columns: var(--sm-sidebar-rail-w) minmax(0, 1fr);
    }

    .app-shell:not(.nav-wide) .app-sidebar {
        width: var(--sm-sidebar-rail-w);
        --sm-nav-label: none;
        --sm-nav-flex: none;
        --sm-nav-rule: block;
        --sm-nav-just: center;
        --sm-nav-sub: none;
        --sm-nav-brand-pad: 0;
    }

    .app-shell:not(.nav-wide) .app-sidebar:hover,
    .app-shell:not(.nav-wide) .app-sidebar:focus-within {
        width: var(--sm-sidebar-w);
        box-shadow: var(--sm-shadow-3);
        --sm-nav-label: block;
        --sm-nav-flex: flex;
        --sm-nav-rule: none;
        --sm-nav-just: flex-start;
        --sm-nav-sub: block;
        --sm-nav-brand-pad: 0 0.625rem 0 1.125rem;
    }

    .app-shell:not(.nav-wide) .app-nav-collapse .sm-icon {
        transform: rotate(180deg);
    }
}

/* =============================================================================
   Top bar — Vuexy's detached navbar

   A white card with the page's gutter on either side, sticky a little below the
   top of the window. The strip above it is frosted, so content scrolling up
   fades out before it slides under the card.
   ========================================================================== */
.app-topbar {
    position: sticky;
    top: var(--sm-topbar-gap);
    z-index: var(--sm-z-topbar);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sm-s-4);
    height: var(--sm-topbar-h);
    flex: none;
    /* The page's gutter, or more once the column is wider than the content, so the
       card lines up with the page beneath it. */
    margin: var(--sm-topbar-gap) max(var(--sm-gutter), calc((100% - var(--sm-content-max)) / 2)) 0;
    padding: 0 var(--sm-s-4) 0 var(--sm-s-5);
    background-color: rgba(255, 255, 255, 0.92);
    backdrop-filter: saturate(200%) blur(6px);
    -webkit-backdrop-filter: saturate(200%) blur(6px);
    border-radius: var(--sm-r);
    box-shadow: var(--sm-shadow-nav);
}

.app-topbar::before {
    content: "";
    position: absolute;
    left: calc(-1 * var(--sm-gutter));
    right: calc(-1 * var(--sm-gutter));
    bottom: 100%;
    height: var(--sm-topbar-gap);
    z-index: -1;
    background: linear-gradient(180deg, rgba(248, 247, 250, 0.7) 44%, rgba(248, 247, 250, 0.43) 73%, rgba(248, 247, 250, 0));
    backdrop-filter: saturate(200%) blur(10px);
    -webkit-backdrop-filter: saturate(200%) blur(10px);
    pointer-events: none;
}

.app-topbar-left,
.app-topbar-right {
    display: flex;
    align-items: center;
    gap: var(--sm-s-3);
    min-width: 0;
}

.app-topbar-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    flex: none;
    border: 0;
    border-radius: var(--sm-r-pill);
    background: none;
    color: var(--sm-ink);
    transition: background-color var(--sm-fast) var(--sm-ease);
}

.app-topbar-toggle:hover {
    background-color: var(--sm-nav-hover);
}

/* From 1280px up the sidebar carries its own rail control, so the top bar's
   toggle steps aside, as Vuexy's hamburger does on a desktop. */
@media (min-width: 1280px) {
    .app-topbar-toggle {
        display: none;
    }
}

.app-society {
    display: flex;
    align-items: center;
    gap: var(--sm-s-3);
    min-width: 0;
}

.app-society-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    flex: none;
    border-radius: var(--sm-r-sm);
    background-color: var(--sm-primary-tint);
    color: var(--sm-primary);
}

.app-society-name {
    font-size: var(--sm-text);
    font-weight: 500;
    color: var(--sm-ink);
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.app-society-meta {
    font-size: var(--sm-text-xs);
    color: var(--sm-ink-3);
    line-height: 1.3;
}

.app-user-trigger {
    display: flex;
    align-items: center;
    padding: 2px;
    border: 0;
    border-radius: var(--sm-r-pill);
    background: none;
    color: var(--sm-ink);
}

.app-user-trigger:hover .sm-avatar,
.app-user-trigger[aria-expanded="true"] .sm-avatar {
    box-shadow: 0 0 0 3px var(--sm-primary-tint);
}

/* The avatar with Vuexy's green "online" dot: decorative — the person looking at
   it is, by definition, signed in. */
.app-user-avatar {
    position: relative;
    display: inline-flex;
    flex: none;
}

.app-user-avatar::after {
    content: "";
    position: absolute;
    right: 0;
    bottom: 1px;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background-color: var(--sm-success);
    box-shadow: 0 0 0 2px var(--sm-surface);
}

/* =============================================================================
   Breadcrumb row
   ========================================================================== */
.app-crumbs {
    display: flex;
    align-items: center;
    min-height: var(--sm-crumbs-h);
    flex: none;
    padding: var(--sm-s-5) var(--sm-gutter) 0;
}

.app-crumbs-inner {
    width: 100%;
    max-width: var(--sm-content-max);
    margin: 0 auto;
}

.sm-breadcrumb {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sm-s-1);
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: var(--sm-text-md);
    color: var(--sm-ink-3);
}

.sm-breadcrumb li {
    display: flex;
    align-items: center;
    gap: var(--sm-s-1);
    min-width: 0;
}

.sm-breadcrumb a {
    color: var(--sm-ink-3);
}

.sm-breadcrumb a:hover {
    color: var(--sm-primary-600);
}

.sm-breadcrumb .sm-breadcrumb-sep {
    color: var(--sm-ink-4);
}

.sm-breadcrumb [aria-current="page"] {
    color: var(--sm-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sm-breadcrumb-back {
    display: none;
    align-items: center;
    gap: var(--sm-s-1);
    font-size: var(--sm-text-md);
    font-weight: 500;
    color: var(--sm-ink-2);
}

/* =============================================================================
   Footer
   ========================================================================== */
.app-footer {
    flex: none;
    padding: var(--sm-s-4) var(--sm-gutter);
    font-size: var(--sm-text-md);
    color: var(--sm-ink-3);
}

.app-footer-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--sm-s-2);
    max-width: var(--sm-content-max);
    margin: 0 auto;
}

.app-footer a {
    color: var(--sm-primary-600);
    font-weight: 500;
}

/* =============================================================================
   Off-canvas drawer (< 768px)

   Below the md breakpoint the sidebar leaves the flow entirely and opens over
   the page from the top bar. visibility, not only transform, so a closed drawer
   holds nothing focusable — tabbing can never land inside a menu that is not on
   screen.
   ========================================================================== */
.app-nav-backdrop {
    display: none;
}

@media (max-width: 767.98px) {
    .app-shell {
        grid-template-columns: minmax(0, 1fr);
    }

    :root {
        --sm-topbar-gap: 0.75rem;
    }

    .app-content {
        --sm-gutter: 1rem;
        grid-column: 1;
    }

    .app-sidebar {
        width: min(86vw, 300px);
        transform: translateX(-100%);
        visibility: hidden;
        box-shadow: none;
        transition: transform var(--sm-mid) var(--sm-ease),
                    visibility var(--sm-mid) var(--sm-ease),
                    box-shadow var(--sm-mid) var(--sm-ease);
    }

    .app-shell.nav-open .app-sidebar {
        transform: translateX(0);
        visibility: visible;
        box-shadow: var(--sm-shadow-3);
    }

    .app-shell.nav-open .app-nav-backdrop {
        display: block;
        position: fixed;
        inset: 0;
        z-index: var(--sm-z-backdrop);
        background-color: rgba(var(--sm-shade-rgb), 0.5);
        animation: sm-fade-in var(--sm-mid) var(--sm-ease);
    }

    /* The drawer closes from inside it as well as from the backdrop. */
    .app-nav-close {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 34px;
        height: 34px;
        flex: none;
        margin-left: auto;
        border: 0;
        border-radius: var(--sm-r-pill);
        background-color: var(--sm-nav-hover);
        color: var(--sm-ink);
    }

    /* The rail has no meaning without a sidebar in flow. */
    .app-nav-collapse {
        display: none;
    }

    .app-main {
        padding: var(--sm-s-4) var(--sm-gutter) var(--sm-s-6);
    }

    .app-topbar {
        height: 56px;
        padding: 0 var(--sm-s-3);
        gap: var(--sm-s-2);
    }

    .app-crumbs {
        padding: var(--sm-s-3) var(--sm-gutter) 0;
    }

    .app-footer-inner {
        justify-content: center;
        text-align: center;
    }

    /* A breadcrumb trail is too much on a phone; one way back is enough. */
    .sm-breadcrumb {
        display: none;
    }

    .sm-breadcrumb-back {
        display: inline-flex;
    }

    /* And on a page the menu lists, there is nowhere to go back to — the trail would only
       repeat the title directly beneath it. The band goes rather than standing empty. */
    .app-crumbs:not(:has(.sm-breadcrumb-back)) {
        display: none;
    }

    .app-society-meta,
    .app-society-icon {
        display: none;
    }

    /* A control on a touch screen is a target first. */
    .btn:not(.btn-sm):not(.btn-link),
    .form-control:not(.form-control-sm),
    .form-select:not(.form-select-sm) {
        min-height: 44px;
    }

    .app-topbar-toggle,
    .sm-icon-btn {
        width: 40px;
        height: 40px;
        min-height: 40px;
    }
}

@keyframes sm-fade-in {
    from { opacity: 0; }
    to { opacity: 1; }
}
