.sidemenu {
    /* no explicit height -- it sizes to its own content (header + items),
    which is normally much shorter than a full screen. .sidemenu_container
    (its sticky containing block) actually stretches to span the whole
    scrollable page height via the flex-stretch chain in home_base.html /
    page_layout.css, so a short sticky element here can stay reachable the
    entire time you're scrolling; a height:100vh sidebar couldn't fit in
    the shrinking remaining space near the very bottom of the page and got
    visibly squeezed/pushed as a result, right where the footer begins.
    No background-color here either -- it's on .sidemenu_container instead
    (see home_layout.css), so the rail color fills that full container
    height while this stays short; transparent lets that show through. */
    position: sticky;
    top: 0;
    width: 60px;
    padding: 1rem;
    transition:
        width 0.2s ease,
        opacity 0.3s ease;

    display: grid;
    grid-auto-rows: min-content;
    overflow: hidden;
}

/* grid items default to min-width:auto, meaning their content's natural
width (e.g. .sidemenu-item's "Info Articles" text) can silently force the
grid's implicit column wider than .sidemenu's own explicit 60px/56px --
overflow:hidden above only clips what renders, it doesn't stop the grid
from actually being laid out wider internally. That wider-than-visible
column is what made centering the icon (below) land it outside the visibly
clipped area instead of the true collapsed width. */
.sidemenu > * {
    min-width: 0;
}


.sidemenu-item {
    margin: 1rem;
    opacity: 0;
    transition: opacity 0.2s ease;

}

.sidemenu .sidemenu-option {
    border-bottom: 1px solid var(--color-border);
    width: 100%;
    /* without this, a label like "Info Articles" wraps onto multiple lines
    at the narrower widths partway through the open/close transition (the
    same min-width:0 that lets .sidemenu-item shrink to fit the collapsed
    column -- see the grid comment above -- also lets its text wrap there).
    Since .sidemenu is display:grid with grid-auto-rows:min-content, that
    row-height change during the transition reflowed the whole item stack,
    reading as the items squeezing together. Same fix already applied to
    .icon-text below for the identical reason; overflow:hidden on .sidemenu
    itself clips the nowrap text while collapsed instead of forcing the
    grid column wider (same min-width:0 interaction as the icon-centering
    fix above). */
    white-space: nowrap;
}

.sidemenu-item a {
    color: var(--color-text);
    text-decoration: none;
    font-size: large;
    transition: opacity 0.15s ease, transform 0.15s ease;
}

/* same hover treatment as the home nav's dropdown links (.dropdown-menu
.drop-content a:hover, dropdownmenu.css) -- covers both .sidemenu-option
(the item's own top-level label) and every link inside its .drop-content,
since both are plain <a> descendants of .sidemenu-item. Opacity, not a
color swap -- lets whatever's behind (the rail's own background-color)
show through slightly instead of picking one fixed muted color. */
.sidemenu-item a:hover {
    opacity: 0.7;
    transform: translateY(-1px);
}

.sidemenu-header {
    display: flex;
    align-items: center;
    justify-content: center; /* centers the icon while collapsed */
    transition: justify-content 0.2s ease;
}

.icon {
  width: 24px;
  height: 24px;
  object-fit: contain;
  display: block;
  transition: opacity 0.1s ease;
}

/* text (hidden when collapsed). max-width:0 + overflow:hidden, not just
opacity:0 -- otherwise the invisible text still occupies space in the
.sidemenu-header flex row, which throws off centering the icon in the
collapsed bar (it'd be centered together with this invisible text, not by
itself). min-width:0 is required too: flex items default to min-width:auto,
which for white-space:nowrap text means "at least the full unwrapped text
width" -- that silently overrides max-width:0, so without this the text
was still taking up its full (invisible) width, overflowing the row and
pushing the icon out past .sidemenu's overflow:hidden edge. */
.icon-text {
    font-size: larger;
    font-weight: bold;
    text-align: left;
    text-decoration: underline;

    opacity: 0;
    min-width: 0;
    max-width: 0;
    overflow: hidden;
    white-space: nowrap;
    transition: opacity 0.1s ease, max-width 0.2s ease;
}

/* .is-open is the JS-driven equivalent of :hover below, for touch devices
(tapping .sidemenu-header expands it, .sidemenu-close collapses it back) --
see dropdown_menu.js. Kept unscoped (unlike :hover) since it's only ever
added by JS, which already gates itself on hover-capability, so there's no
risk of it firing on a hover device. */
.sidemenu.is-open {
    width: 250px;
    padding: 1rem;
    border-right: 1px solid color-mix(in srgb, var(--color-border) 15%, transparent);
}

.sidemenu.is-open .sidemenu-item {
    opacity: 1;
}

.sidemenu.is-open .icon {
    opacity: 0;
}

.sidemenu.is-open .icon-text {
    opacity: 1;
    max-width: 200px;
}

.sidemenu.is-open .sidemenu-header {
    justify-content: flex-start; /* matches the left-aligned items below it */
}

/* :hover only, not touch -- scoped so a tap's simulated/sticky :hover state
(a well-known mobile quirk, since hover has no real meaning on touch) can't
keep the rail expanded after dropdown_menu.js has already toggled .is-open
off. Touch devices rely purely on the JS-driven class above.

Triggered off .sidemenu_container:hover rather than .sidemenu:hover --
.sidemenu itself is only as tall as its own content (see the comment at the
top of this file), while .sidemenu_container is the one that actually
stretches to the full rail height the user sees (background-color lives
there too, in home_layout.css). Hovering .sidemenu directly meant moving the
cursor into the lower part of the visibly-colored rail -- still inside
.sidemenu_container, but below the short .sidemenu box -- fell outside the
real hover target and snapped the rail shut. Submenus (.sidemenu-item) stay
click-only even here -- only the rail itself opens on hover. */
@media (hover: hover) and (pointer: fine) {
    .sidemenu_container:hover .sidemenu {
        width: 250px;
        padding: 1rem;
        border-right: 1px solid color-mix(in srgb, var(--color-border) 15%, transparent);
    }

    .sidemenu_container:hover .sidemenu-item {
        opacity: 1;
    }

    .sidemenu_container:hover .icon {
        opacity: 0;
    }

    .sidemenu_container:hover .icon-text {
        opacity: 1;
        max-width: 200px;
    }

    .sidemenu_container:hover .sidemenu-header {
        justify-content: flex-start;
    }
}

.sidemenu-close {
    display: none;
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    width: 1.5rem;
    height: 1.5rem;
    border: none;
    background: transparent;
    font-size: 1.25rem;
    line-height: 1;
    cursor: pointer;
    color: var(--color-text);
}

.sidemenu.is-open .sidemenu-close {
    display: block;
}

.sidemenu-item .drop-content {
    display: grid;
    grid-auto-rows: min-content;
    gap: 0.5em;
    padding-left: 1rem;

    max-height: 0;             /* collapsed */
    opacity: 0;
    overflow: hidden;
    transition:
        max-height 0.25s ease,
        opacity 0.25s ease,
        transform 0.05s ease;
}

.sidemenu-item .drop-content a:first-child {
    margin-top: 0.5em;
}

/* dropdown links inside */
.sidemenu-item .drop-content .drop-option {
    padding: 0.25rem 0.75rem;
}

/* is-open is JS-driven, on every device -- see dropdown_menu.js's
unconditional setupTapToggle(".sidemenu-item", ...). No more CSS :hover
reveal here at all. */
.sidemenu-item.is-open .drop-content {
    max-height: 500px;               /* big enough to fit children */
    opacity: 1;
}

/* same caret pattern as the top navbar's .dropdown-menu (see
dropdownmenu.css) -- a small triangle after the trigger link that flips to
point the other way once its submenu is open, the visual cue that tapping
(or hovering) reveals a submenu rather than navigating away immediately.
Every .sidemenu-option always has a sibling .drop-content across all three
sidemenu partials (home/article/study), so it's unconditional here, same as
the navbar's .link::after. */
.sidemenu-item .sidemenu-option::after {
    content: "";
    display: inline-block;
    margin-left: 0.35em;
    border: 0.35em solid transparent;
    border-top-color: currentColor;
    vertical-align: middle;
    transition: transform 0.15s ease;
}

.sidemenu-item.is-open .sidemenu-option::after {
    transform: rotate(180deg);
}

/* the top-level link is toggle-only on every device (see dropdown_menu.js's
unconditional setupTapToggle(".sidemenu-item", ...)) -- "All" is always the
way to actually navigate to it, not just on touch. */
.sidemenu-item .drop-option--all {
    display: block;
}

/* phone-sized screens: the default font-size/margins are tuned for a
   desktop hover panel and end up oversized and spaced-out on a small
   screen -- tighten both so more of the menu is visible at once */
@media (max-width: 480px) {
    /* stays position:sticky (inherited from the base rule) rather than
    switching to fixed -- sticky already adapts correctly to the header
    scrolling away (it just settles at top:0 once you've scrolled past its
    natural position, the same mechanism the desktop sidebar already
    relies on), which a fixed element with a hardcoded top offset cannot
    do without extra JS to track the header's height.
    "overlay, don't push .maincontent" comes from a different mechanism:
    .sidemenu_container's width is locked to the collapsed 56px in
    home_layout.css regardless of .sidemenu's own width, and stays at its
    default overflow:visible -- so when .sidemenu widens to 200px below,
    it visually spills out past its parent's boundary and over
    .maincontent instead of growing the parent (which is what pushed
    content before). z-index keeps it stacked above .maincontent while
    doing so. Own background here (not .sidemenu_container's, that's for
    desktop) since this is visually a self-contained overlay panel. */
    .sidemenu {
        width: 56px;
        padding: 0.5rem;
        z-index: 100;
        background-color: var(--color-accent-hover);
    }

    /* full height only while actively open as a touch-driven overlay -- the
    collapsed rail above stays content-sized (same sticky-squeeze reasoning
    as the base .sidemenu rule). Without this, .is-open only grew the width,
    not the height, so the overlay stopped partway down the screen instead
    of covering it, exposing page content underneath. 100vh first as a
    fallback for browsers without dvh support; 100dvh overrides it where
    available so mobile browser chrome doesn't over/undershoot the panel.
    Going back to auto on close is handled in dropdown_menu.js, not here --
    height going to/from 'auto' isn't reliably interpolable, so a
    transition-delay on it (tried first) wasn't consistently honored across
    mobile browsers; JS locks the pixel height in place until the width
    collapse actually finishes instead. */
    .sidemenu.is-open {
        width: 200px;
        height: 100vh;
        height: 100dvh;
    }

    .sidemenu-item {
        margin: 0.5rem 0.75rem;
    }

    .sidemenu-item a {
        font-size: 15px;
    }

    .icon-text {
        font-size: 15px;
    }
}

@media (max-width: 480px) and (hover: hover) and (pointer: fine) {
    .sidemenu_container:hover .sidemenu {
        width: 200px;
        padding: 0.5rem;
    }
}
