/* ============================================================================
   _sidebar.css — data-driven, collapsible primary-navigation sidebar
   ============================================================================
   Deliberate departure from the app's house RTL style (physical left/right +
   app-rtl.css mirrors): this file is greenfield, so it uses CSS logical
   properties (inset-inline-*, margin-inline-*, padding-inline-*) instead,
   which auto-flip with html[dir="rtl"] and need no app-rtl.css additions.
   ============================================================================ */

:root {
    --sidebar-width: 260px;
    /* 92px, not the 64px icon strip it was until 2026-09-16: the collapsed
       rail prints each section's one-word name under its icon (nav.yml
       shortLabelKey), which is what makes it readable — fifteen unlabelled
       glyphs are a rebus, and the name only appeared on hover, i.e. never for
       a touch user.

       The width is set by the LABEL, not by the icon. 92px leaves ~68px of
       text box inside the link's 0.5rem inline margin and its own padding,
       which fits every common.navShort.* value on ONE line in both locales
       («الصلاحيات», «الإشعارات», "Settings", "Meetings") — and one line per
       item is what keeps fifteen entries plus the brand block at ~780px,
       inside a 916px window. Two lines would be ~995px and scroll on every
       common screen, which is the whole reason the short keys exist rather
       than a clamp on the full label. */
    --sidebar-width-collapsed: 92px;
    --nav-indent: 0.85rem;
}

html[data-sidebar="collapsed"] {
    --sidebar-width: var(--sidebar-width-collapsed);
}

/* ===========================================
   Rail
   =========================================== */
.app-sidebar {
    position: fixed;
    /* FULL HEIGHT, in every design. The rail used to start below the sticky
       navbar (inset-block: var(--navbar-height) 0); now it runs the whole
       window and the top bar sits BESIDE it. On a phone this was already so —
       the drawer has always covered the bar — so the change is the desktop
       rail joining it. */
    inset-block: 0;
    inset-inline-start: 0;
    inline-size: var(--sidebar-width);
    z-index: var(--z-fixed);
    /* DARK in both themes, from the design's own chrome token. This supersedes
       the 2026-08-14 light-rail decision (the rule that moved the light rail
       to --bg-secondary to stop it competing with the content cards): all four
       sheets draw a dark rail beside a light page, and that is the look the
       owner picked. The glare reasoning still holds and is still satisfied —
       the rail is chrome, and the content cards remain the brightest surface
       on screen; it is now the DARKEST instead of the second-brightest. */
    background: var(--sidebar-bg);
    border-inline-end: 1px solid var(--sidebar-line);
    overflow-x: hidden;
    overflow-y: auto;
    /* The scrollbar SKIN (5px --border thumb, transparent track) is global —
       _base.css. Only behaviour lives here:

       scrollbar-gutter reserves the track permanently so the labels do not
       shift sideways the moment the tree crosses the viewport height — which
       it does every time a user collapses or expands a group.

       overscroll-behavior stops a scroll that reaches the end of the tree from
       chaining into the page behind it. */
    overscroll-behavior: contain;
    scrollbar-gutter: stable;
    transition: inline-size var(--transition-slow);
    /* Hold the rail visually static during a cross-page view transition — only
       the page content crossfades, the sidebar doesn't repaint. */
    view-transition-name: app-sidebar;
}

/* The faint lift over the rail's first 240px, which every design defines as
   --sidebar-bg-top. It is a background LAYER rather than a second element, so
   the tree scrolls under it and nothing is added to the markup. */
.app-sidebar {
    background-image: linear-gradient(var(--sidebar-bg-top), var(--sidebar-bg) 240px);
}

.sidebar-tree,
.sidebar-subtree,
.sidebar-flyout-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.sidebar-tree {
    padding-block: 0.5rem;
}

/* ===========================================
   Brand block — the rail's head
   ===========================================
   «ت» + the product name + its tagline, at the top of the rail in every
   design. Exactly --topbar-height tall, so its bottom edge lines up with the
   top bar's beside it.

   STICKY, so the tree scrolls underneath it rather than pushing it away. That
   is also why the placement script in fragments/sidebar.html subtracts this
   block's height when it tests whether the active link is on screen: a link
   hidden UNDER the brand is inside the rail's box and would otherwise count
   as visible.

   It takes the top safe-area inset at EVERY width, not just on mobile. The
   tree used to pay that inset because the desktop rail started below a navbar
   that had already paid it; with the rail at the top in both states, this
   block is what sits under the notch — on a phone AND on an iPad in
   standalone mode. */
.sidebar-brand {
    position: sticky;
    inset-block-start: 0;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: 0.65rem;
    min-block-size: var(--topbar-height);
    padding-inline: 1rem;
    padding-block-start: env(safe-area-inset-top, 0px);
    background: var(--sidebar-bg-top);
    border-block-end: 1px solid var(--sidebar-line);
    color: var(--sidebar-text);
    text-decoration: none;
    white-space: nowrap;
}

/* ONE cached file takes every design's colours: the letter is a MASK over a
   box painted --logo-fg on --logo-bg, so a new design adds no image. */
.sidebar-brand-mark {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    inline-size: 32px;
    block-size: 32px;
    border-radius: var(--radius-icon-btn);
    background: var(--logo-bg);
}

/* The MASK goes on the inner element, never on the box: masking the box would
   punch the --logo-bg fill away along with everything else and leave a letter
   floating on the rail. So the box carries the fill and this carries the
   letter, in --logo-fg. */
.sidebar-brand-mark::before {
    content: "";
    inline-size: 68%;
    block-size: 68%;
    background: var(--logo-fg);
    -webkit-mask: url('/imgs/icons/tibyan-mark.svg') center / contain no-repeat;
    mask: url('/imgs/icons/tibyan-mark.svg') center / contain no-repeat;
}

.sidebar-brand-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.sidebar-brand-name {
    font-weight: 700;
    font-size: 1.05rem;
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
}

.sidebar-brand-tagline {
    font-size: 0.7rem;
    line-height: 1.3;
    color: var(--sidebar-muted);
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ===========================================
   Content offset — pilot pages carry body.has-sidebar
   =========================================== */
body.has-sidebar .main-content,
body.has-sidebar .footer {
    /* The rail is position:fixed (out of flow): reserve its width with a margin
       AND shrink the content to the remaining space. Both track --sidebar-width,
       so the content fits beside the open rail and stretches to fill when it
       collapses. The width override is required — .main-content's base
       width:100% / max-width:100% plus this margin would overflow the viewport
       by the rail's width (that was the "content doesn't fit" bug). */
    margin-inline-start: var(--sidebar-width);
    width: calc(100% - var(--sidebar-width));
    max-width: calc(100% - var(--sidebar-width));
    transition: margin-inline-start var(--transition-slow), width var(--transition-slow);
}

/* ===========================================
   Toggle button
   =========================================== */
/* The collapse toggle MOVED to the top bar (it is the sheets' «☰» at the
   start of the bar), so it is a .btn-navbar now and needs no box of its own
   here. It keeps its id and its two data-* labels, which is all sidebar.js
   binds — the module looks it up by #sidebarToggle, never by this class, so
   the move needed no JS change.

   What stays is the icon SWAP, because it is keyed off html[data-sidebar]:
   one button carries both chevrons and the attribute decides which shows. A
   script swapping classes instead would repaint after first render, which is
   the whole reason the state lives on <html>. */
.sidebar-toggle-icon-expand {
    display: none;
}

html[data-sidebar="collapsed"] .sidebar-toggle-icon-collapse {
    display: none;
}

html[data-sidebar="collapsed"] .sidebar-toggle-icon-expand {
    display: inline-block;
}

/* ===========================================
   Links & depth indent
   =========================================== */
.sidebar-link {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    padding-block: 0.55rem;
    padding-inline-start: calc(var(--nav-indent) * var(--depth, 1));
    padding-inline-end: 1rem;
    color: var(--sidebar-text);
    text-decoration: none;
    /* The active item is a FILLED, rounded block inset from the rail's edges
       now, so the 3px start border that used to mark it is gone. Its radius is
       the shared --radius-nav (one geometry in every design) and the inline
       margin is what insets it. */
    margin-inline: 0.5rem;
    border-radius: var(--radius-nav);
    transition: background-color var(--transition-fast), color var(--transition-fast);
    white-space: nowrap;
}

.sidebar-link:hover {
    background: var(--sidebar-hover-bg);
    color: var(--sidebar-text);
}

/* The active item is the design's own fill, not a wash of the brand colour.
   That also retires the note this replaced: the label used to be
   --primary-DARK because the row was a 12% tint OF the primary, which lifted
   the background toward the very colour being measured and left --primary
   just under AA in both themes. There is no tint now — the fill is opaque and
   each design measures --nav-active-text and --nav-active-muted against BOTH
   of its gradient stops (violet: 5.84:1 / 6.95:1 and 4.82:1 / 5.73:1).

   The gradient direction is a variable because CSS gradients have no logical
   direction: --nav-active-dir is `to left` by default and `to right` under
   html[dir="ltr"], so the lighter stop stays at the reading start edge in
   both directions. */
.sidebar-link.active {
    background: linear-gradient(var(--nav-active-dir), var(--nav-active-a), var(--nav-active-b));
    color: var(--nav-active-text);
    box-shadow: 0 2px 8px var(--nav-active-shadow);
    font-weight: 600;
}

.sidebar-link.active .sidebar-icon,
.sidebar-link.active .sidebar-desc {
    color: var(--nav-active-muted);
}

.sidebar-icon {
    flex: 0 0 auto;
    width: 1.25em;
    text-align: center;
    color: var(--sidebar-icon);
}

/* The label and its description are ONE flex item, so the caret stays put
   whether or not an entry has a second line. */
.sidebar-text {
    flex: 1 1 auto;
    min-width: 0;
}

.sidebar-label {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The second line under a top-level entry (nav.yml descKey, D6). Shown in
   every design — a design changes colour, never structure — so there is no
   switch here, only the presence of the key. It ellipsises at the rail's
   260px; the phone's drawer is 280px, which is the width the sheets drew. */
.sidebar-desc {
    display: block;
    font-size: 0.72rem;
    line-height: 1.35;
    color: var(--sidebar-muted);
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The one-word section name, rendered for TOP-LEVEL entries only. Hidden
   wherever the full label is already on screen — the expanded rail and the
   phone drawer — so the collapsed desktop rail is its only host. Declared
   here, beside the label it stands in for, rather than in the collapsed
   block below: the DEFAULT for this element is hidden, and only one state
   reveals it. */
.sidebar-short {
    display: none;
}

.sidebar-caret {
    flex: 0 0 auto;
    font-size: 0.75em;
    transition: transform var(--transition-fast);
}

/* ===========================================
   Expand/collapse groups (expanded rail)
   =========================================== */
.sidebar-subtree {
    display: none;
}

.sidebar-node.open > .sidebar-subtree {
    display: block;
}

.sidebar-node.open > .sidebar-link .sidebar-caret {
    transform: rotate(180deg);
}

/* A NESTED group (depth 2+, and the flyout's copy of one) folds with + / -
   instead of the chevron (2026-09-07), so a child group is not mistaken for
   another top-level one. The fragment renders fa-plus; the open state swaps
   the glyph to fa-minus by overriding ::before content — Font Awesome 6.5.1's
   own codepoints (\2b and \f068; check both if the library is upgraded) —
   because open-state is paint-time state: the server renders .open, and a
   script swapping icon classes would repaint every nested group after first
   render. No rotation for these: a + turned 180° is still a +. */
.sidebar-caret-nested {
    transform: none;
}

.sidebar-node.open > .sidebar-link .sidebar-caret.sidebar-caret-nested {
    transform: none;
}

.sidebar-node.open > .sidebar-link .sidebar-caret-nested::before,
.sidebar-flyout-node.open > .sidebar-flyout-group .sidebar-caret-nested::before {
    content: "\f068";
}

/* Flyout is expanded-rail-only in reverse: suppressed unless collapsed */
.sidebar-flyout {
    display: none;
}

/* ===========================================
   Collapsed rail (icons-only) + cascading flyout
   =========================================== */
/* .sidebar-text, not .sidebar-label — the label lives inside that wrapper
   with its description now, and hiding only the label would leave the
   description alone on the icon rail. */
html[data-sidebar="collapsed"] .sidebar-text,
html[data-sidebar="collapsed"] .sidebar-caret,
html[data-sidebar="collapsed"] .sidebar-subtree {
    display: none;
}

/* The collapsed rail is icons only, so the brand shows just its mark. */
html[data-sidebar="collapsed"] .sidebar-brand-text {
    display: none;
}

html[data-sidebar="collapsed"] .sidebar-brand {
    justify-content: center;
    padding-inline: 0;
}

/* A mini rail, not an icon strip: the link is a two-item COLUMN, the icon
   over its section's short name. .sidebar-text (the full label and its
   description) and the caret are already hidden above, so nothing else is in
   the column — which is why this needs no extra hiding rule. */
html[data-sidebar="collapsed"] .sidebar-link {
    flex-direction: column;
    justify-content: center;
    gap: 0.25rem;
    padding-block: 0.45rem;
    padding-inline: 0.2rem;
    text-align: center;
}

/* Bigger here than in the expanded rail, where it sits BESIDE a label at body
   size; leading a 0.68rem label it would otherwise read as the smaller of the
   two. The 1.25em advance stays, so every glyph is centred in the same box
   and the column of icons has one optical axis — the same reason the row
   actions pin theirs. */
html[data-sidebar="collapsed"] .sidebar-icon {
    font-size: 1.05rem;
    line-height: 1;
}

html[data-sidebar="collapsed"] .sidebar-short {
    display: block;
    /* Load-bearing. The link is align-items:center, so in a column a child is
       sized to its own max-content and may simply overrun the 92px rail —
       ellipsis and all, since text-overflow needs a constrained box to fire
       in. This is what constrains it; `overflow: hidden` alone would clip the
       word with no «…» to say it was clipped. nowrap is inherited from
       .sidebar-link. */
    max-inline-size: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 0.68rem;
    line-height: 1.25;
}

html[data-sidebar="collapsed"] .sidebar-node {
    position: relative;
}

/* The rail sets overflow-x:hidden + overflow-y:auto so the *expanded* tree can
   scroll — but that same clipping would hide the collapsed-rail hover flyouts,
   which sit just past the rail's inline edge (inset-inline-start:100%). Let them
   escape on desktop; the icon-only rail has few top-level items, so dropping the
   scroll here is fine. Mobile keeps its overflow (it's a full drawer — no flyout). */
@media (min-width: 768px) {
    html[data-sidebar="collapsed"] .app-sidebar {
        overflow: visible;
    }

    /* Any group the server marked .open (nav.yml `open: true`, or the one
       holding the current page) matches
       .sidebar-node.open > .sidebar-subtree { display:block }, which outranks
       the plain collapsed hide above — so without this the icon rail would show
       those subtrees inline. Keep the collapsed desktop rail strictly
       icons-only; the child menu appears only in the hover flyout. */
    html[data-sidebar="collapsed"] .sidebar-node.open > .sidebar-subtree {
        display: none;
    }

    /* "You are here" for a SECTION. A group never carries .active — only a
       leaf does — and the rule above is what removes every leaf from this
       rail, so on a page inside a group (eleven of the fifteen sections) the
       mini rail marked nothing at all. Survivable while it was an anonymous
       icon strip; a section that now prints its own name has to say when you
       are in it.

       :has() matches a descendant that is display:none, which is exactly
       where the active child sits, at any depth (الفواتير, three levels down
       under الإدارة المالية). Scoped to .sidebar-subtree so the hover
       FLYOUT's own .sidebar-flyout-link.active cannot light a section up
       merely because the pointer is over it.

       INSIDE this min-width block on purpose: on a phone the drawer shows
       the subtree, so the active CHILD marks itself and a heading lit up as
       well would be the same page claimed twice. That is also why this is
       not merged into the .sidebar-link.active rule above — one selector
       list would have reached the drawer. The colours are the same four
       design tokens that rule reads, so the only thing repeated here is the
       shadow offset and the weight; keep the two in step. */
    html[data-sidebar="collapsed"] .sidebar-node:has(.sidebar-subtree .sidebar-link.active) > .sidebar-link {
        background: linear-gradient(var(--nav-active-dir), var(--nav-active-a), var(--nav-active-b));
        color: var(--nav-active-text);
        box-shadow: 0 2px 8px var(--nav-active-shadow);
        font-weight: 600;
    }

    html[data-sidebar="collapsed"] .sidebar-node:has(.sidebar-subtree .sidebar-link.active) > .sidebar-link .sidebar-icon {
        color: var(--nav-active-muted);
    }
}

html[data-sidebar="collapsed"] .sidebar-flyout {
    display: block;
    position: absolute;
    inset-inline-start: 100%;
    inset-block-start: 0;
    min-inline-size: 210px;
    padding-block: 0.25rem;
    z-index: var(--z-tooltip);
    /* The flyout is part of the RAIL, not of the page, so it takes the rail's
       surface. On --card it was a light panel hanging off a dark rail in the
       light theme — the one place the old chrome's colours would have shown
       through. */
    background: var(--sidebar-bg);
    border: 1px solid var(--sidebar-line);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--transition-fast), visibility var(--transition-fast);
    /* Two-level flyouts (الإدارة المالية: four headings + sixteen links) can
       run past the bottom of the viewport from a node partway down the rail.
       sidebar.js shifts the panel up at hover time and, when even that is not
       enough, sets an exact max-block-size inline; this cap is only the no-JS
       floor, and overflow-y is what makes either cap scroll instead of clip. */
    max-block-size: calc(100vh - 1rem);
    overflow-y: auto;
}

html[data-sidebar="collapsed"] .sidebar-node:hover > .sidebar-flyout {
    opacity: 1;
    visibility: visible;
}

/* The flyout mirrors this group as it looks in the OPEN sidebar: a parent
   header row + its children as named, indented links. It uses its own classes
   so the icon-only rail rules (hidden labels, centered icons) never touch it. */
.sidebar-flyout-head {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    padding-block: 0.55rem;
    padding-inline: 1rem;
    color: var(--sidebar-text);
    font-weight: 600;
    white-space: nowrap;
}

/* The head is an <a> for an item that HAS a url (a top-level leaf — otherwise
   its flyout holds nothing clickable at all), a <div> for a group or a
   placeholder. Only the anchor form takes link affordances, and they mirror
   .sidebar-flyout-link below so both rows of a flyout behave identically. */
a.sidebar-flyout-head {
    text-decoration: none;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

a.sidebar-flyout-head:hover {
    background: var(--sidebar-hover-bg);
    color: var(--sidebar-text);
}

a.sidebar-flyout-head.active {
    background: linear-gradient(var(--nav-active-dir), var(--nav-active-a), var(--nav-active-b));
    color: var(--nav-active-text);
}

.sidebar-flyout-caret {
    margin-inline-start: auto;
    font-size: 0.75em;
}

.sidebar-flyout-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.sidebar-flyout-link {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    padding-block: 0.5rem;
    padding-inline: 2.35rem 1rem;
    color: var(--sidebar-text);
    text-decoration: none;
    white-space: nowrap;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

.sidebar-flyout-link:hover {
    background: var(--sidebar-hover-bg);
    color: var(--sidebar-text);
}

.sidebar-flyout-link.active {
    background: linear-gradient(var(--nav-active-dir), var(--nav-active-a), var(--nav-active-b));
    color: var(--nav-active-text);
    font-weight: 600;
}

/* A nested group inside the flyout (the second level the fragment renders):
   a toggle heading in the link column, then its links one indent deeper,
   shown only while the group is .open — the SAME open state as the tree's
   copy of the group (sidebar.js syncs the two through data-nav-key), so a
   user who opened a group before finds it open here, and one who did not
   finds it closed. Mirrors .sidebar-subtree / .sidebar-caret above. The
   heading is a <button>, never a link: a group has no page. */
.sidebar-flyout-group {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    inline-size: 100%;
    padding-block: 0.5rem;
    padding-inline: 2.35rem 1rem;
    border: 0;
    background: none;
    color: var(--sidebar-muted);
    font: inherit;
    font-size: 0.85em;
    font-weight: 600;
    text-align: start;
    white-space: nowrap;
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

.sidebar-flyout-group:hover {
    background: var(--sidebar-hover-bg);
    color: var(--sidebar-text);
}

.sidebar-flyout-sublist {
    display: none;
}

.sidebar-flyout-node.open > .sidebar-flyout-sublist {
    display: block;
}

.sidebar-flyout-link-nested {
    padding-inline-start: 3.7rem;
}

/* ===========================================
   Mobile off-canvas
   =========================================== */
.app-sidebar-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    z-index: calc(var(--z-fixed) - 1);
    background: rgba(0, 0, 0, 0.4);
    opacity: 0;
    transition: opacity var(--transition-normal);
}

@media (max-width: 767.98px) {
    /* Mobile is always an off-canvas drawer with full labels — the icons-only
       collapsed rail is a desktop-only affordance, so it's overridden here
       regardless of the persisted office-sidebar preference. */
    .app-sidebar {
        inset-block-start: 0;
        inline-size: min(85vw, 280px);
        box-shadow: var(--shadow-lg);
        /* Which way the drawer parks is a VARIABLE, and the direction rule
           below sets only that variable — never `transform` itself.

           There is no logical equivalent of translateX (no `translate-inline`),
           so the off-canvas direction has to be flipped by hand for RTL. Doing
           it the obvious way — `html[dir="rtl"] .app-sidebar { transform:
           translateX(100%) }` — is a silent trap: that selector scores (0,2,1)
           against `.app-sidebar.show`'s (0,2,0), so it wins on specificity no
           matter which is written last. The drawer then stays parked off-screen
           *with .show applied*, and since Arabic is the default locale the
           mobile menu button was simply dead for every user who never switched
           to English. Keeping .show the only rule that authors `transform`
           means nothing can outrank it.

           Also transition transform here, not inline-size (the base rule's
           property, for the desktop rail): on mobile the width is fixed and it
           is the slide that moves. */
        --sidebar-offcanvas: -100%;
        transform: translateX(var(--sidebar-offcanvas));
        transition: transform var(--transition-normal);

        /* iOS notch / Dynamic Island — the drawer pads its own START edge.

           head.html sets viewport-fit=cover, so the layout viewport runs
           edge-to-edge under the system UI, and _layout.css pads the navbar and
           the footer for it. The drawer gets nothing from either: it is fixed at
           inset-block-start:0 and covers the navbar outright, so it is the only
           thing between the nav items and the hardware.

           The padding is on the drawer rather than on .sidebar-tree so the
           SURFACE still bleeds to the physical edge — a 280px panel that stops
           44px short of the screen in landscape reads as a rendering fault.
           Only the items inset, which is what has to clear the notch.

           The side inset flips with direction for the same reason the transform
           does, and reuses the same one-variable shape: the drawer is anchored
           inline-start, which is the physical LEFT edge under LTR and the RIGHT
           one under RTL, while env() only ever speaks physical. Padding both
           sides instead would be direction-proof but would spend ~44px of a
           280px drawer on the edge that is nowhere near the notch. */
        --sidebar-safe-inline: env(safe-area-inset-left, 0px);
        padding-inline-start: var(--sidebar-safe-inline);
    }

    /* Deliberately NOT gated on (display-mode: standalone) like the navbar and
       footer rules it mirrors. Every env() below falls back to 0px, so the gate
       could only ever subtract behaviour — and here it would subtract a real
       case: in an ordinary Safari tab the TOP inset is 0, but the landscape SIDE
       inset is not, and landscape is exactly when the notch lands on the
       drawer's start edge. */
    html[dir="rtl"] .app-sidebar {
        --sidebar-offcanvas: 100%;
        --sidebar-safe-inline: env(safe-area-inset-right, 0px);
    }

    /* Only the BOTTOM inset now. The top one moved to .sidebar-brand, which
       is what actually sits under the notch since the rail runs full height —
       on a phone AND on an iPad in standalone mode, which is why that rule is
       not scoped to mobile the way this one is. Paying it here as well would
       push the first nav item down by the notch twice.

       It stays on the tree rather than the drawer for a mechanical reason:
       .app-sidebar is the scroll container (overflow-y:auto), and its
       block-end padding is only reliably part of the scrollable area once the
       tree overflows — which this one does. Padding the scrolled CONTENT
       always lands, and it composes with the 0.5rem already there. */
    .sidebar-tree {
        padding-block-end: calc(0.5rem + env(safe-area-inset-bottom, 0px));
    }

    .app-sidebar.show {
        transform: translateX(0);
    }

    /* The drawer always shows full labels, whatever the desktop rail's stored
       preference is — the icons-only rail is a desktop affordance. It is
       .sidebar-text, not .sidebar-label: the label lives inside that wrapper
       with its description now, so restoring only the label would leave every
       description hidden on a phone. */
    html[data-sidebar="collapsed"] .sidebar-text {
        display: block;
    }

    html[data-sidebar="collapsed"] .sidebar-caret {
        display: inline;
    }

    html[data-sidebar="collapsed"] .sidebar-brand-text {
        display: flex;
    }

    html[data-sidebar="collapsed"] .sidebar-brand {
        justify-content: flex-start;
        padding-inline: 1rem;
    }

    html[data-sidebar="collapsed"] .sidebar-node.open > .sidebar-subtree {
        display: block;
    }

    /* Undo the mini rail's COLUMN, not just its alignment. The drawer shows
       full labels, so the link goes back to icon-beside-label — every property
       the collapsed block sets has to be restored here, or a phone gets a
       drawer of stacked, centred rows. Equal specificity to that block
       (0,2,1), so this wins only by coming later in the file: keep the two
       together if either ever moves. */
    html[data-sidebar="collapsed"] .sidebar-link {
        flex-direction: row;
        justify-content: flex-start;
        gap: 0.65rem;
        padding-block: 0.55rem;
        padding-inline: 1rem;
        padding-inline-start: calc(var(--nav-indent) * var(--depth, 1));
        text-align: start;
    }

    html[data-sidebar="collapsed"] .sidebar-icon {
        font-size: inherit;
        line-height: inherit;
    }

    /* The full label is on screen here, so the short name would be a second
       copy of it under every top-level icon. */
    html[data-sidebar="collapsed"] .sidebar-short {
        display: none;
    }

    html[data-sidebar="collapsed"] .sidebar-flyout {
        display: none;
    }

    body.has-sidebar .main-content,
    body.has-sidebar .footer {
        /* Mobile: the rail is a full off-canvas drawer, not an inline offset —
           content spans the full width (reset the desktop margin + width calc). */
        margin-inline-start: 0;
        width: 100%;
        max-width: 100%;
    }

    /* The bar is offset beside the rail on a desktop; here the rail is a
       closed drawer that covers the bar when opened, so the offset goes.
       Without this the whole bar would sit --sidebar-width off the start edge
       on every phone. */
    .app-topbar {
        margin-inline-start: 0;
    }

    /* ...and the brand comes BACK. _layout.css hides it wherever the rail can
       show it, which is the desktop rail's brand block — but that block is
       inside a closed drawer here, so the bar is the only place the product is
       named. Later in the cascade than that rule (_sidebar.css loads after
       _layout.css) and at equal specificity, so source order decides. */
    body.has-sidebar .app-topbar .navbar-brand {
        display: block;
    }

    .app-sidebar-backdrop.show {
        display: block;
        opacity: 1;
    }

    /* Scoped under .app-topbar on purpose: the button is also a .btn, and
       the touch floor in _utilities.css sets `.btn { display: inline-flex }`
       under (pointer: coarse). That file loads AFTER this one at the same
       (0,1,0), so a bare `.sidebar-toggle` lost to it and the rail's «>>»
       showed on every phone — while a narrowed desktop window, a fine
       pointer, never showed it. (0,2,0) wins whatever the load order. */
    .app-topbar .sidebar-toggle {
        display: none;
    }
}

/* Only ever shown on mobile, and only on pages that render the sidebar.
   Scoped under .app-topbar for the same reason as .sidebar-toggle above: a
   bare (0,1,0) hide lost to the touch floor's `.btn` display, which put this
   «☰» beside the «>>» on an iPad from 768px up. The show rule below is
   (0,2,1), so it still wins on a phone. */
.app-topbar .sidebar-mobile-toggle {
    display: none;
}

@media (max-width: 767.98px) {
    body.has-sidebar .sidebar-mobile-toggle {
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}

/* Navbar-dropdown page links are redundant with the sidebar — hide (not
   remove) them on pages that render it, so non-sidebar pages keep working. */
body.has-sidebar .nav-dropdown-page-link {
    display: none;
}
