/* Side global navigation (#global-nav-side).
 *
 * Everything global-nav related lives in THIS file. Every selector is
 * namespaced: #global-nav-side, .gns-*, body[data-global-nav],
 * body.gns-collapsed.
 *
 * The rail is built to work exactly like the workspaces app's
 * .workspace-tabs column: a TRANSPARENT floating icon column,
 * SUPERIMPOSED on top of the app canvas (no panel, no border, no
 * content displacement). #app-space / #portal stay at left: 0; instead
 * the app content clears the rail the same way workspace content
 * clears its tabs column:
 *   - sidebars get the workspace-style left padding
 *     (--sidebar-padding with the 50px rail inset);
 *   - document bodies get --document-body-left-distance, consumed by
 *     the stock .document-body rules in all.css.
 *
 * Mode is a body state: <body data-global-nav="top|side">, rendered
 * server-side so there is no flash of the wrong nav. The rail is
 * expanded (--workspace-tabs-home-width) on Home only: whenever an app
 * is on screen (#app-space.state-on, matched via :has, plus the
 * body.gns-collapsed class KikaronGlobalNav mirrors) it collapses to
 * the 50px icon column — desktop only; under 768px the bottom tab bar
 * takes over.
 */

/* Registered as a real <color> so app-to-app switches interpolate —
 * the collapsed rail's wash then fades between app colours instead of
 * snapping (the var is stamped on <body> by KikaronGlobalNav). */
@property --gns-active-app-colour {
    syntax: '<color>';
    inherits: true;
    initial-value: transparent;
}

body[data-global-nav="side"] {
    transition: --gns-active-app-colour var(--gns-transition);
}

:root {
    /* Favourites runs as a rail app but has no App registry row, so its
     * colour isn't in the generated apps.css — give it the accent. */
    --app-colour-favourites: var(--colour-accent, #ea6c1a);
    --gns-width: var(--workspace-tabs-home-width, 250px);
    --gns-width-collapsed: 50px;
    --gns-mobile-height: 64px;
    /* Corner radius of the active-app tile and the Home brandmark tile on
     * the mobile rail — shared so the two stay visually identical. */
    --gns-mobile-active-tile-radius: 13px;
    /* same x/y inset the workspace-tabs column uses */
    --gns-left: calc(var(--sidebar-margin-left, 15px) + 0.5 * var(--sidebar-padding-right, 25px));
    --gns-top: calc(var(--sidebar-margin-top, 15px) + 0.5 * var(--sidebar-padding-top, 25px));
    --gns-background: #fff;
    --gns-hairline: var(--colour-separation-lines, #eae2d6);
    --gns-ink: var(--colour-text, #1a1a1a);
    --gns-muted: var(--colour-discreet, #6b6560);
    --gns-accent: var(--colour-accent, #ea6c1a);
    /* One speed/curve for every rail transition (width morph, logo, labels,
     * fades, colour washes) so expand/collapse and all their parts move
     * together. */
    --gns-transition: var(--base-animation-duration, 0.3s);
}

/* ------------------------------------------------------------------ */
/* The rail itself — workspace-tabs look: transparent floating column  */
/* ------------------------------------------------------------------ */

#global-nav-side {
    display: none;
    position: fixed;
    top: var(--gns-top);
    bottom: calc(var(--sidebar-margin-bottom, 15px) + 0.5 * var(--sidebar-padding-bottom, 25px));
    left: var(--gns-left);
    width: var(--gns-width);
    /* transparent while expanded; the collapsed state paints it white */
    border: 1px transparent solid;
    border-radius: calc(0.7 * var(--border-radii, 8px));
    padding-top: 22px;
    box-sizing: border-box;
    flex-direction: column;
    background: transparent;
    /* zero-size, zero-alpha shadow so the collapsed state's
     * --default-box-shadow fades in instead of popping */
    box-shadow: 0 0 0 rgba(0, 0, 0, 0);
    color: var(--gns-ink);
    z-index: 999; /* above #portal (10) / #app-space (11) and any sidebar chrome, below modals/tooltips */
    transition: all var(--gns-transition);
    will-change: width;
    overflow-x: hidden;
    overflow-y: auto;
}

body[data-global-nav="side"] #global-nav-side {
    display: flex;
}

@media only screen and (min-width: calc(768px + 1px)) {
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side {
        width: var(--gns-width-collapsed);
        /* Fixed semi-transparent white, the same regardless of which app
         * is active (previously a 10% wash of the active app/space colour). */
        background: rgba(255, 255, 255, 0.5);
        box-shadow: var(--default-box-shadow);
        border-color: white;
    }
}

/* Header: platform identity + the user (personal-menu trigger).
 * interpolate-size lets the auto height animate to the collapsed 31px. */
#global-nav-side .gns-header {
    flex: 0 0 auto;
    height: 120px;
    padding: 0px 0 6px 10px;
    interpolate-size: allow-keywords;
    transition: all var(--gns-transition);
}
/* Platform logo: full wordmark expanded, brandmark (swirl) collapsed —
 * the two images sit stacked and crossfade with the width animation */
#global-nav-side .gns-platform-logo {
    display: block;
    position: relative;
    height: 67px;
    cursor: default;
    /* The logo tile box morphs between the full and collapsed states —
     * transition everything on the same rail speed so it eases instead of
     * snapping while the wordmark img morphs. */
    transition: all var(--gns-transition);
}
#global-nav-side .gns-platform-logo img {
    display: block;
    /* On the base (not just the collapsed state) so the morph uses the same
     * origin collapsing and expanding — keeps the two directions symmetric. */
    transform-origin: left;
    transition: all var(--gns-transition);
}
#global-nav-side .gns-logo-full {
    width: 240px;
}
#global-nav-side .gns-logo-mark {
    position: absolute;
    top: 0;
    left: 0;
    height: 28px;
    width: 28px;
    opacity: 0;
}
#global-nav-side .gns-platform-name {
    display: block;
    font-weight: 700;
    font-size: 1.1em;
    color: var(--gns-ink);
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: all var(--gns-transition);
}
#global-nav-side .gns-hub {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    margin-top: 6px;
    margin-left: -8px;
    padding: 6px 8px;
    border-radius: 12px;
    box-sizing: border-box;
    max-height: 56px;
    overflow: hidden;
    min-width: 0;
    text-decoration: none;
    cursor: default;
    /* Collapse via opacity (linear fade) + max-height (linear clip) rather
     * than zoom: scaling to ~0 reads as "gone" almost instantly one way but
     * grows in gradually the other, so collapse and expand looked
     * lopsided. A fade + clip looks the same in both directions. */
    transition: all var(--gns-transition);
}
/* Hover wash + rounded corners matching the app rows (.gns-link:hover). */
#global-nav-side .gns-hub:hover {
    background: color-mix(in srgb, var(--gns-ink) 5%, transparent);
}
body[dir="rtl"] #global-nav-side .gns-hub {
    margin-left: 0;
    margin-right: -8px;
}
/* The small round avatar left of the name — stock .pat-avatar shape
 * (50% radius, initials fallback), just sized down */
#global-nav-side .gns-user-avatar {
    flex: 0 0 auto;
    width: 29px;
    height: 29px;
    margin-left: -2px;
    margin-right: 1px;
    padding: 0;
    font-size: 0.68em;
    font-weight: 700;
}
/* Name stacked above the current-hub byline, to the right of the avatar. */
#global-nav-side .gns-hub-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
    transition: all var(--gns-transition);
}
/* Name + down-arrow on one line; the arrow (the personal-menu dropdown
 * affordance) sits right after the name and never gets clipped. */
#global-nav-side .gns-hub-name {
    display: flex;
    align-items: center;
    min-width: 0;
    font-size: 0.85em;
    line-height: 1.2;
    color: var(--gns-ink);
    transition: all var(--gns-transition);
}
#global-nav-side .gns-hub-name-text {
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
#global-nav-side .gns-hub-name::after {
    flex: 0 0 auto;
    font-family: fontello;
    content: var(--glyph-down-open);
    font-size: 0.8em;
    color: var(--gns-muted);
    margin-left: 4px;
    transition: all var(--gns-transition);
}
/* The hub the user is currently in — a small muted byline under the name. */
#global-nav-side .gns-hub-byline {
    font-size: 0.7em;
    line-height: 1.2;
    color: var(--gns-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: all var(--gns-transition);
}
@media only screen and (min-width: calc(768px + 1px)) {
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-platform-name {
        zoom: 0.001;
        opacity: 0;
    }
    /* The user line shrinks away rather than popping out */
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-hub {
        margin-top: 0;
        opacity: 0;
    }
    /* Morph the full wordmark into the brandmark: scale down + slide left so
     * the orange swirl (on the right of the wordmark) arrives roughly where
     * the black mark sits, fading out as the mark fades in — reads as one
     * mark transforming rather than a hard crossfade. */
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-logo-full {
        opacity: 0;
        transform: scale(0.30) translate(-125px, -22px);
    }
    /* RTL: the rail (and mark) sit on the right, so slide the wordmark the
     * other way. */
    body[dir="rtl"][data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-logo-full {
        transform: scale(0.9) translateX(82px);
    }
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-logo-mark {
        opacity: 1;
        top: 5px;
        left: 6px;
        /* ink-coloured brandmark, slightly inset like the app glyphs */
        filter: grayscale(1) brightness(0.08);
        transform: scale(0.92);
    }
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-header {
        padding-left: 0;
        padding-right: 0;
        height: 35px;
    }
    /* Collapsed: the logo behaves like an icon tile — same size and
     * radius as the app icons, with the same light hover wash */
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-platform-logo {
        width: 38px;
        height: 38px;
        margin-left: 6px;
        border-radius: 10px;
    }
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-platform-logo:hover {
        background-color: color-mix(in srgb, var(--gns-ink) 7%, transparent);
    }
    /* Home is the active screen while the rail is collapsed (only the
     * tablet-portrait case — on desktop the rail collapses only once an
     * app is on screen). The logo tile then reads as the "current"
     * destination, exactly like a current app icon: solid accent fill,
     * brandmark inverted to white. */
    body[data-global-nav="side"].gns-collapsed:not(:has(#app-space.state-on)) #global-nav-side .gns-platform-logo,
    body[data-global-nav="side"].gns-collapsed:not(:has(#app-space.state-on)) #global-nav-side .gns-platform-logo:hover {
        background-color: var(--colour-accent, var(--gns-accent));
    }
    body[data-global-nav="side"].gns-collapsed:not(:has(#app-space.state-on)) #global-nav-side .gns-logo-mark {
        /* invert the brandmark to solid white (vs the dark-ink filter
         * the inactive collapsed state uses above) */
        filter: brightness(0) invert(1);
    }
}

/* Item lists */
#global-nav-side ul {
    list-style: none;
    margin: 0;
    padding: 0;
}
#global-nav-side .gns-apps {
    flex: 0 0 auto;
}
#global-nav-side .gns-spaces {
    /* parked at the bottom of the rail */
    flex: 0 1 auto;
    margin-top: auto;
    padding: 8px 0;
    min-height: 0;
    overflow-y: auto;
}
#global-nav-side .gns-section-title {
    display: none;
    margin: 4px 4px 6px;
    font-size: 0.7em;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--gns-muted);
    transition: all var(--gns-transition);
}
@media only screen and (min-width: calc(768px + 1px)) {
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-section-title {
        zoom: 0.001;
        opacity: 0;
    }
    /* Updates is the dashboard's home surface on desktop — only the
     * mobile bottom bar gets its own Updates tab */
    body[data-global-nav="side"] #global-nav-side .gns-item.gns-app-updates {
        display: none;
    }
    /* The utility apps (preferences / administrator / password / help)
     * ride the avatar/personal menu, not the desktop rail — they stay
     * available on the mobile bottom bar's more-apps popover. */
    body[data-global-nav="side"] #global-nav-side .gns-apps > .gns-item:is(
        .gns-app-preferences,
        .gns-app-administrator,
        .gns-app-password,
        .gns-app-help
    ) {
        display: none;
    }
}

/* One nav entry — workspace-tabs row metrics: 50px rows, 42px icon
 * block, bold label */
#global-nav-side .gns-link {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 0 10px 0 0;
    height: 44px;
    border-radius: 12px;
    box-sizing: border-box;
    color: #6B6560;
    font-weight: bold;
    text-decoration: none;
    white-space: nowrap;
    cursor: default;
    position: relative;
    transition: all var(--gns-transition);
}
#global-nav-side .gns-link:hover {
    background: color-mix(in srgb, var(--gns-ink) 5%, transparent);
}
/* The active item's link has no background at all — its state shows
 * through the coloured icon tile and accent label, not a row wash */
#global-nav-side .gns-item.current > .gns-link:hover,
#global-nav-side .gns-link[aria-current="page"]:hover {
    background: transparent;
}
#global-nav-side .gns-item.current > .gns-link,
#global-nav-side .gns-link[aria-current="page"] {
    color: var(--gns-accent);
    font-weight: 600;
}
#global-nav-side .gns-icon {
    flex: 0 0 auto;
    width: 38px;
    height: 38px;
    margin: 3px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
#global-nav-side .gns-icon svg {
    width: 100%;
    height: 100%;
}
/* App icons: borderless transparent tiles, every SVG shape painted in
 * the muted ink (#6B6560), matching the label colour. !important beats
 * fill attributes baked into the source SVGs. */
#global-nav-side .gns-icon.app-icon {
    border: none;
    box-shadow: none;
    border-radius: 10px;
    background-color: transparent;
    box-sizing: border-box;
    width: 38px;
    height: 38px;
    padding: 7px;
    overflow: hidden;
    transition: all var(--gns-transition);
}
#global-nav-side .gns-icon.app-icon * {
    fill: #6B6560 !important;
}
#global-nav-side .gns-icon.app-icon svg #dots {
    fill: transparent !important;
}
/* Active app: icon tile fills with the app's own colour, glyph in
 * white — the 42px rounded square the workspace tabs paint behind
 * their active icon */
#global-nav-side .gns-item.current .gns-icon.app-icon,
#global-nav-side .gns-link[aria-current="page"] .gns-icon.app-icon {
    background-color: var(--app-colour, var(--gns-accent));
}
#global-nav-side .gns-item.current .gns-icon.app-icon *,
#global-nav-side .gns-link[aria-current="page"] .gns-icon.app-icon * {
    fill: #fff !important;
}
#global-nav-side .gns-item.current .gns-icon.app-icon svg #dots,
#global-nav-side .gns-link[aria-current="page"] .gns-icon.app-icon svg #dots {
    fill: transparent !important;
}
/* Rail line icons (.rail-icon, sourced from assets/.../rail-icons): the
 * glyph is an outline, so the lines are COLOURED via stroke and the fill
 * stays empty. Each rule mirrors the filled-icon rule above at equal
 * specificity, later in source, so it wins for .rail-icon elements while
 * leaving fallback (filled) app icons on their fill rules. */
#global-nav-side .gns-icon.rail-icon * {
    fill: none !important;
    stroke: #6B6560 !important;
}
#global-nav-side .gns-icon.rail-icon svg #dots {
    fill: transparent !important;
    stroke: transparent !important;
}
/* Active app, line icons: the glyph rides the app-colour tile (from the
 * .app-icon rule above) with a white outline — the coloured-outline
 * treatment is reserved for the tooltip menu. */
#global-nav-side .gns-item.current .gns-icon.rail-icon *,
#global-nav-side .gns-link[aria-current="page"] .gns-icon.rail-icon * {
    fill: none !important;
    stroke: #fff !important;
}
#global-nav-side .gns-label {
    flex: 1 1 auto;
    max-width: 12rem;
    overflow: hidden;
    text-overflow: ellipsis;
    /* flex-grow is NOT transitioned — animating it forced a layout pass
     * per frame on every row; it snaps while the fade carries. The label
     * collapses via max-width (a cheap clip) rather than zoom — zoom
     * re-rasterised the label text every frame and was the main collapse
     * stutter on a rail full of rows. */
    transition: all var(--gns-transition);
}
@media only screen and (min-width: calc(768px + 1px)) {
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-label {
        flex-grow: 0;
        max-width: 0;
        opacity: 0;
    }
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-link {
        justify-content: center;
        padding: 0;
        gap: 0;
    }
    /* Collapsed rail: hover feedback moves from the row to the icon
     * tile itself (active icons keep their app-colour fill) */
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-link:hover {
        background: transparent;
    }
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-item:not(.current) .gns-link:hover .gns-icon.app-icon {
        background-color: color-mix(in srgb, var(--gns-ink) 7%, transparent);
    }
    /* Collapsed rail: glyphs go full black (the active app keeps its
     * white-on-app-colour tile, untouched by the :not(.current) scope) */
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-item:not(.current) .gns-link:not([aria-current="page"]) .gns-icon.app-icon * {
        fill: #000 !important;
    }
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-item:not(.current) .gns-link:not([aria-current="page"]) .gns-icon.app-icon svg #dots {
        fill: transparent !important;
    }
    /* Rail line icons: colour the stroke black instead of the fill */
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-item:not(.current) .gns-link:not([aria-current="page"]) .gns-icon.rail-icon * {
        fill: none !important;
        stroke: #000 !important;
    }
}

/* Team-space avatar — the space's first letter on its own colour
 * (--gns-space-colour, set inline per entry; accent fallback).
 * Inactive: 10% tint background, 1px border + letter in the colour.
 * Active workspace: solid colour, white letter. */
#global-nav-side .gns-space-avatar {
    background: color-mix(in srgb, var(--gns-space-colour, var(--gns-accent)) 10%, white);
    color: var(--gns-space-colour, var(--gns-accent));
    border: 1px solid var(--gns-space-colour, var(--gns-accent));
    box-sizing: border-box;
    border-radius: 50%;
    font-size: 0.75em;
    font-weight: 700;
    line-height: 1;
}
/* The current workspace stays remembered (li.current) but only reads
 * as highlighted while the Workspaces pane is in the foreground — on
 * Home or in any other app it renders like the rest. */
#global-nav-side .gns-item.gns-space.current > .gns-link {
    color: #6B6560;
    font-weight: bold;
}
body:has(#app-space.state-on:is(.active-app-workspaces, .active-app-team-spaces)) #global-nav-side .gns-item.gns-space.current > .gns-link {
    color: var(--gns-accent);
    font-weight: 600;
}
body:has(#app-space.state-on:is(.active-app-workspaces, .active-app-team-spaces)) #global-nav-side .gns-item.gns-space.current .gns-space-avatar {
    background: var(--gns-space-colour, var(--gns-accent));
    color: #fff;
}

/* The "+" entry above the space list (opens the New team space modal):
 * a dashed neutral tile so it reads as an action, not another space. */
#global-nav-side .gns-space-add .gns-space-avatar {
    background: transparent;
    border-style: dashed;
    border-color: #6B6560;
    color: #6B6560;
    font-size: 1.1em;
    font-weight: 600;
}
#global-nav-side .gns-space-add .gns-link:hover .gns-space-avatar {
    border-color: var(--gns-ink);
    color: var(--gns-ink);
}

/* Too many spaces to fit without scrolling: KikaronGlobalNav flags the
 * rail with .gns-spaces-overflow and the per-space list collapses into
 * the single Team spaces app icon (the .gns-spaces-all entry). */
#global-nav-side .gns-spaces .gns-spaces-all {
    display: none;
}
#global-nav-side.gns-spaces-overflow .gns-spaces .gns-spaces-all {
    display: block;
}
#global-nav-side.gns-spaces-overflow .gns-spaces > ul:not(.gns-spaces-all),
#global-nav-side.gns-spaces-overflow .gns-spaces .gns-section-title {
    display: none;
}

/* Unread badge — keeps working in the collapsed rail (rides the icon) */
#global-nav-side .gns-badge {
    flex: 0 0 auto;
    background: var(--gns-accent);
    color: #fff;
    border-radius: 999px;
    font-size: 0.7em;
    line-height: 1;
    padding: 3px 6px;
    top: 0;
}
@media only screen and (min-width: calc(768px + 1px)) {
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-badge {
        position: absolute;
        top: 4px;
        right: 2px;
    }
}

/* Collapsed rail: labels become tooltips (CSS-only, from data-gns-label) */
@media only screen and (min-width: calc(768px + 1px)) {
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-link[data-gns-label]:hover::after,
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-hub[data-gns-label]:hover::after,
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-platform-logo[data-gns-label]:hover::after {
        content: attr(data-gns-label);
        position: fixed;
        left: calc(var(--gns-left) + var(--gns-width-collapsed) + 6px);
        background: var(--gns-background);
        color: var(--gns-ink);
        font-size: 0.8em;
        font-weight: 400;
        padding: 4px 10px;
        border-radius: 4px;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
        white-space: nowrap;
        z-index: 1000;
        pointer-events: none;
    }
    /* The platform logo is a tall 67px block, so the fixed pseudo's static
     * vertical position falls to the block's bottom — well below the small
     * 38px collapsed brandmark tile. Anchor it to the rail top instead so it
     * centres on the swirl tile (rail padding-top 22px + half the 38px tile). */
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-platform-logo[data-gns-label]:hover::after {
        top: calc(var(--gns-top) + 30px);
    }
}

/* ------------------------------------------------------------------ */
/* Chrome swap: what Side mode does to the rest of the page            */
/* ------------------------------------------------------------------ */

/* The header is gone in Side mode, so every layout offset derived
 * from its height collapses to zero (0px, not 0 — unitless zero is
 * invalid inside calc() additions). The dashboard's top margin made
 * room under that header, so it goes too. */
body[data-global-nav="side"] {
    --global-header-height: 0px;
    --dashboard-margin-top: 0px;
}
/* The dashboard sits flush to the top in Side mode — no header to
 * clear. dashboard.css declares --dashboard-margin-top ON
 * .quaive-dashboard itself at large widths (80px, or 40px via
 * #portal:not(:has(#portal-tabs))), which out-ranks the inherited
 * body-level reset, so re-zero it on the element with matching/higher
 * specificity. The second selector beats the (2,1,0) 40px rule;
 * together they cover every width. */
body[data-global-nav="side"] #portal .quaive-dashboard,
body[data-global-nav="side"] #portal:not(:has(#portal-tabs)) .quaive-dashboard {
    --dashboard-margin-top: 0px;
}

/* Side-mode dashboard goes full-bleed: drop the 1280px container cap so
 * the portlet grid uses the whole canvas (the apps/favourites portal
 * sections keep their cap — this is scoped to .section-dashboard). The
 * content box also becomes a size container (gns-dash) so the rules below
 * react to the REAL available width, not the viewport — the rail inset
 * varies (expanded on Home, the 50px strip in tablet-portrait), so a
 * viewport breakpoint would mis-judge how many columns actually fit. */
body[data-global-nav="side"] #portal.section-dashboard #portal-content {
    max-width: none;
    --pat-container-width: none;
    container: gns-dash / inline-size;
}
/* Stop capping the column count: instead of a fixed 2fr/3-track template,
 * auto-fill spawns one more ~320px lane every time another fits, so the
 * portlet grid keeps gaining columns as the canvas widens. */
body[data-global-nav="side"] #portal.section-dashboard .quaive-dashboard {
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    grid-auto-flow: row dense;
}
/* The Apps portlet is redundant in Side mode — the rail already lists the
 * apps — so omit it at every size. (Sizes 1–2 hide it via their essentials
 * filter anyway; this covers size 3+.) */
body[data-global-nav="side"] #portal.section-dashboard .quaive-dashboard > #portlet-apps {
    display: none;
}
/* The activity stream always lives in the first column — pin its column
 * line to 1 so the masonry/auto-flow algorithm can't drift it into
 * another lane (it would otherwise seek the shortest one). The base
 * span-every-row pin is dropped (no fixed track count to span). */
body[data-global-nav="side"] #portal.section-dashboard .quaive-dashboard > .quaive-portlet#portlet-updates {
    grid-column: 1;
    grid-row: auto;
    align-self: start;
}
/* Native masonry where the browser has it: portlets pack up into the
 * shortest lane rather than aligning to row tracks, removing the ragged
 * vertical gaps a plain auto-flow grid leaves. Falls back to the grid
 * above wherever display: grid-lanes is unsupported. */
@supports (display: grid-lanes) {
    body[data-global-nav="side"] #portal.section-dashboard .quaive-dashboard {
        display: grid-lanes;
    }
}
/* The lane width (320px) + gutter (20px) sets the breakpoints:
 *   1 lane  : <660px    (size 1, "mobile")
 *   2 lanes : 660–999   (size 2)
 *   3 lanes : 1000–1339 (size 3) …and +340px per lane after that.
 *
 * Two lanes or more: the activity stream is the prominent wide column,
 * spanning the first two lanes. In size 2 that makes it full-width; from
 * size 3 on it's the wide left column with span-1 portlets to its right
 * — and it always starts at lane 1 ("stays in the first column"). */
@container gns-dash (width >= 660px) {
    body[data-global-nav="side"] #portal.section-dashboard .quaive-dashboard > .quaive-portlet#portlet-updates {
        grid-column: 1 / span 2;
    }
}

/* Size 1 (one lane, "mobile"): only the activity stream. Everything else,
 * Zmanim and News included, is hidden — and the lone activities portlet
 * sheds its card chrome to read as the bare feed (full-width column, no
 * title, no panel background/shadow/border/padding). Container-query
 * based, so it holds whenever the dashboard column is this narrow — the
 * mobile bottom-bar layout AND a desktop side-nav with the rail expanded. */
@container gns-dash (width < 660px) {
    body[data-global-nav="side"] #portal.section-dashboard .quaive-dashboard > .quaive-portlet:not(#portlet-updates) {
        display: none;
    }
    body[data-global-nav="side"] #portal.section-dashboard .quaive-dashboard {
        grid-template-columns: 1fr;
    }
    body[data-global-nav="side"] #portal.section-dashboard .quaive-dashboard > .quaive-portlet {
        grid-column: 1 / -1;
        background: transparent;
        box-shadow: none;
    }
    body[data-global-nav="side"] #portlet-updates .well-title-group,
    body[data-global-nav="side"] #portlet-activity-stream .well-title-group {
        display: none;
    }
    body[data-global-nav="side"] #portlet-updates .panel-content,
    body[data-global-nav="side"] #portlet-activity-stream .panel-content {
        background: transparent;
        box-shadow: none;
        border: none;
        padding: 0;
    }
}

/* Size 2 (exactly two lanes): only News (Journal), Zmanim and the activity
 * stream. Zmanim and News sit on the top row side by side (one lane each);
 * the activity stream spans both lanes underneath (its span comes from the
 * ≥660 rule above, so it drops to the next row). `order` fixes the stack:
 * Zmanim, News, then Activities. */
@container gns-dash (660px <= width < 1000px) {
    /* Plain grid here, never grid-lanes: this tier relies on `order` +
     * row placement to stack Zmanim/News over the spanning activity
     * stream, which the masonry algorithm wouldn't honour. Overrides the
     * @supports (display: grid-lanes) rule above (same selector, later in
     * source). */
    body[data-global-nav="side"] #portal.section-dashboard .quaive-dashboard {
        display: grid;
    }
    body[data-global-nav="side"] #portal.section-dashboard .quaive-dashboard > .quaive-portlet:not(#portlet-zmanim, #portlet-journal, #portlet-updates) {
        display: none;
    }
    body[data-global-nav="side"] #portal.section-dashboard .quaive-dashboard > #portlet-zmanim {
        order: 1;
    }
    body[data-global-nav="side"] #portal.section-dashboard .quaive-dashboard > #portlet-journal {
        order: 2;
    }
    body[data-global-nav="side"] #portal.section-dashboard .quaive-dashboard > #portlet-updates {
        order: 3;
    }
    /* No Zmanim (Jewish add-on off): News takes the full top row, the
     * activity stream below it. */
    body[data-global-nav="side"] #portal.section-dashboard .quaive-dashboard:not(:has(#portlet-zmanim)) > #portlet-journal {
        grid-column: 1 / span 2;
        order: 1;
    }
    body[data-global-nav="side"] #portal.section-dashboard .quaive-dashboard:not(:has(#portlet-zmanim)) > #portlet-updates {
        order: 2;
    }
}

/* Size 3 and up (three+ lanes): every portlet is back. The activity
 * stream holds the first two lanes and the span-1 portlets fill the lanes
 * to its right, gaining one more column per +340px. Native masonry packs
 * those narrow portlets into the shortest lane on its own. The plain-grid
 * fallback can't — without help a row-grid aligns every lane to shared
 * row tracks, so the tall activity stream would blow open huge gaps in
 * the narrow lanes. The classic fix: make it span every row (1 / span
 * 100) so the span-1 portlets pack row-by-row down lanes 3+ instead. Only
 * needed where grid-lanes is absent. */
@supports not (display: grid-lanes) {
    @container gns-dash (width >= 1000px) {
        body[data-global-nav="side"] #portal.section-dashboard .quaive-dashboard > .quaive-portlet#portlet-updates {
            grid-row: 1 / span 100;
        }
    }
}
/* From size 3 on, Zmanim is a normal span-1 portlet — but it is the only
 * one that precedes the activity stream in source order, so under masonry
 * (which places in document order) it would otherwise settle into lane 1
 * ABOVE the stream. Pin it into the second column (lane 3 — the first
 * narrow lane, since the stream spans lanes 1–2) so it sits beside the
 * stream, not above it. Harmless in the plain-grid fallback, where it
 * already lands there. */
@container gns-dash (width >= 1000px) {
    body[data-global-nav="side"] #portal.section-dashboard .quaive-dashboard > #portlet-zmanim {
        grid-column: 3;
    }
}

/* Favourites-as-app pane visibility — no App registry row means no
 * generated show-rule in automations/apps.css, so it lives here. */
#app-space.active-app-favourites.state-on #application-body-favourites {
    display: block;
}

/* App sidebars span the full app-body height — the header is gone, so
 * the height calc that subtracted it no longer applies. */
body[data-global-nav="side"] #app-space .sidebar.left {
    height: 100%;
}

/* Rail mode: paint the app sidebars with the light app colour — a pale wash of
 * the active app's hue — so they read as part of the app, not the chrome.
 * Overriding the shared --sidebar-background-colour recolours both the sidebar
 * and its content (both consume that variable). */
body[data-global-nav="side"] .application-body {
    --sidebar-background-colour: color-mix(in srgb, var(--app-colour, white) 15%, white);
}

/* Legacy top chrome disappears while Side is active */
body[data-global-nav="side"] #global-header,
body[data-global-nav="side"] #portal-tabs,
body[data-global-nav="side"] #tabs-bar {
    display: none !important;
}

/* The apps grid is the rail's job in Side mode, so it must never render.
 * base.html's default #portal-content lazy-loads the apps portal (grid +
 * its launcher toolbar) as the fallback landing, so on any full-page app
 * load — e.g. the redirect after saving language prefs — the grid would
 * otherwise flash in #portal behind the open app. Hide it outright; Home
 * in Side mode is the dashboard, reached from the rail. */
body[data-global-nav="side"] #portal .quaive-apps-grid,
body[data-global-nav="side"] #portal #app-launcher-toolbar {
    display: none !important;
}
/* The header drop-shadow #app-space paints along its top edge belongs
 * to the hidden header — without this it floats as a stray gradient. */
body[data-global-nav="side"] #app-space:not(.injecting)::before {
    opacity: 0 !important;
}

/* Content stays at left: 0 — the rail is superimposed on top of it,
 * exactly like the workspace-tabs column floats over workspace pages.
 * top: 0 because the hidden header no longer needs headroom. */
body[data-global-nav="side"] #app-space,
body[data-global-nav="side"] #portal {
    top: 0;
    margin-left: 0;
}

@media only screen and (min-width: calc(768px + 1px)) {
    /* The portal content clears the full rail */
    body[data-global-nav="side"] #portal {
        padding-left: calc(var(--gns-left) + var(--gns-width));
        box-sizing: border-box;
    }
    /* …but a collapsed rail (tablet-portrait Home, or any open app)
     * shrinks to the 50px icon strip — the portal reclaims that width
     * and only clears the strip, so it never sits narrow beside a gap
     * where the expanded rail used to be. Tracks the same trigger as the
     * rail-width rule above. */
    body[data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #portal {
        padding-left: calc(var(--gns-left) + var(--gns-width-collapsed));
    }
    /* Apps (collapsed rail): every app body adopts the workspaces-app
     * layout recipe — the sidebar's content padding and the document
     * body's left distance both reserve the 50px rail, exactly as
     * #application-body-workspaces does for its own tabs column. The
     * stock aside.sidebar/.document-body rules in all.css consume
     * these vars; nothing else is touched. */
    body[data-global-nav="side"] #app-space .application-body {
        /* same width the workspaces app gives its sidebar (475px vs the
         * 400px platform default) */
        --sidebar-width: 475px;
        /* re-declared HERE (like #application-body-workspaces does) so
         * the calc picks up the 475px width — the :root default already
         * resolved against 400px and inherits as that fixed value */
        --document-body-left-distance-sidebar-open: calc(var(--sidebar-width) + var(--sidebar-margin-left, 15px));
        --sidebar-padding:
            var(--sidebar-padding-top)
            var(--sidebar-padding-right)
            var(--sidebar-padding-bottom)
            calc(var(--gns-width-collapsed) + 1.5 * var(--sidebar-padding-left));
        --document-body-left-distance: calc(var(--gns-width-collapsed) + var(--sidebar-padding-left));
        /* Closed sidebars keep a visible strip under the rail, exactly
         * like the workspaces app: its -397px against a 475px sidebar
         * leaves ~78px = tabs column + inset. Here that strip is the
         * rail width + the rail's left inset, whatever the app's
         * sidebar width. */
        --sidebar-margin-left-closed: calc(var(--gns-width-collapsed) + var(--gns-left) - var(--sidebar-width, 400px));
    }
    /* Large sidebars get the bigger left padding the workspaces app
     * uses (#application-body-workspaces.sidebar-large:
     * --sidebar-padding-left-large = 85px + padding) so the content
     * clears the rail. */
    body[data-global-nav="side"] #app-space .application-body.sidebar-large {
        --sidebar-padding-left-large: calc(85px + var(--sidebar-padding-left, 25px));
    }
    /* Switching to an app with an open sidebar: the sidebar slides and
     * fades in from outside the left edge. CSS animations restart when
     * the app body flips from display:none to rendered (pane switch)
     * and when a fresh body is pat-injected, so every arrival animates. */
    body[data-global-nav="side"] #app-space .application-body.sidebar-left-open aside.sidebar.left {
        animation: gns-sidebar-slide-in var(--base-animation-duration, 0.3s) ease-out;
    }
    /* The visible strip of a closed sidebar is chrome, not content —
     * fade its contents out and let clicks fall through (the stock
     * .sidebar-content transition animates the fade). */

    body[data-global-nav="side"] aside.sidebar.left .sidebar-content {
        transition: all var(--gns-transition);
        
        > * {
            transition: all var(--gns-transition);
        }
    }

    body[data-global-nav="side"] #app-space .application-body.sidebar-left-closed aside.sidebar.left #sidebar-content,
    body[data-global-nav="side"] #app-space .application-body.sidebar-left-closed aside.sidebar.left .sidebar-content {
        /* opacity: 0; */
        pointer-events: none;

        > * {
            opacity: 0;
        }
    }
    /* Dashboard updates portlet: no well header (the rail/logo is the
     * navigation), just the feed with a little breathing room on top.
     * Covers the lean id (portlet-updates) and classic
     * (portlet-activity-stream). */
    body[data-global-nav="side"] #portlet-updates .well-title-group,
    body[data-global-nav="side"] #portlet-activity-stream .well-title-group {
        display: none;
    }
    body[data-global-nav="side"] #portlet-updates .panel-content,
    body[data-global-nav="side"] #portlet-activity-stream .panel-content {
        padding-top: 20px;
    }
    /* Messages Element iframe — same insets as the calendar canvas
     * below (its stock rule uses the 15px sidebar margins). */
    body[data-global-nav="side"] #app-space .application-body.application-messages .pat-element {
        top: calc(var(--sidebar-margin-top, 15px) + 0.5 * var(--sidebar-padding-top, 25px));
        right: calc(var(--sidebar-margin-top, 15px) + 0.5 * var(--sidebar-padding-top, 25px));
        bottom: calc(var(--sidebar-margin-top, 15px) + 0.5 * var(--sidebar-padding-top, 25px));
        left: var(--sidebar-padding-left, 25px);
    }
    /* Calendar canvas — same inset recipe as the workspace-chat
     * Element iframe (#application-body-workspaces .pat-element
     * iframe): 27.5px top/right/bottom, 25px left, card chrome. */
    body[data-global-nav="side"] #app-space .application-calendar .pat-calendar#calendar-app {
        position: absolute;
        top: calc(var(--sidebar-margin-top, 15px) + 0.5 * var(--sidebar-padding-top, 25px));
        right: calc(var(--sidebar-margin-top, 15px) + 0.5 * var(--sidebar-padding-top, 25px));
        bottom: calc(var(--sidebar-margin-top, 15px) + 0.5 * var(--sidebar-padding-top, 25px));
        left: var(--sidebar-padding-left, 25px);
        border-radius: var(--border-radii, 8px);
        box-shadow: var(--default-box-shadow);
    }
    /* The workspaces host reserves the SAME 50px for its own tabs
     * column — push its tabs right of the global rail and widen the
     * insets so both columns fit side by side. */
    body[data-global-nav="side"] #app-space #application-body-workspaces {
        --workspace-tabs-left: calc(var(--gns-left) + var(--gns-width-collapsed));
        --workspace-tabs-left-closed: calc(var(--gns-left) + var(--gns-width-collapsed));
        --sidebar-padding:
            var(--sidebar-padding-top)
            var(--sidebar-padding-right)
            var(--sidebar-padding-bottom)
            calc(2 * var(--gns-width-collapsed) + 1.5 * var(--sidebar-padding-left));
        --document-body-left-distance: calc(2 * var(--gns-width-collapsed) + var(--sidebar-padding-left));
    }
}

/* RTL (Hebrew/Arabic): rail rides the right edge; the var-driven
 * margins/distances are consumed by all.css's own RTL swaps, but the
 * physical insets declared above (padding shorthands, left/right
 * coordinates) need explicit mirrors. */
body[dir="rtl"] #global-nav-side {
    left: auto;
    right: var(--gns-left);
}
/* Header/logo: the 10px inline inset moves to the right edge (the
 * wordmark <img> right-aligns by itself — block replaced element under
 * direction: rtl) and the stacked brandmark anchors right too. */
body[dir="rtl"] #global-nav-side .gns-header {
    padding: 0 10px 10px 0;
}
body[dir="rtl"] #global-nav-side .gns-logo-mark {
    left: auto;
    right: 0;
}
/* Row insets: the 10px end-padding sits before the label, not the icon,
 * so the icons line up under the logo like they do in LTR. */
body[dir="rtl"] #global-nav-side .gns-link {
    padding: 0 0 0 10px;
}
@media only screen and (min-width: calc(768px + 1px)) {
    /* Collapsed-rail label tooltips flip to the rail's left side (match
     * the LTR selector breadth incl. the :has() collapse variant). */
    body[dir="rtl"][data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-link[data-gns-label]:hover::after,
    body[dir="rtl"][data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-hub[data-gns-label]:hover::after,
    body[dir="rtl"][data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-platform-logo[data-gns-label]:hover::after {
        left: auto;
        right: calc(var(--gns-left) + var(--gns-width-collapsed) + 6px);
    }
    /* Collapsed logo tile insets from the right edge; the brandmark
     * keeps its 5px inset measured from the right. */
    body[dir="rtl"][data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-platform-logo {
        margin-left: 0;
        margin-right: 6px;
    }
    body[dir="rtl"][data-global-nav="side"]:is(.gns-collapsed, :has(#app-space.state-on)) #global-nav-side .gns-logo-mark {
        left: auto;
        right: 5px;
    }
    /* The portal clears the rail on the right instead */
    body[dir="rtl"][data-global-nav="side"] #portal {
        padding-left: 0;
        padding-right: calc(var(--gns-left) + var(--gns-width));
    }
    /* The --sidebar-padding shorthand is physical (top right bottom
     * left): the rail inset moves from the left to the right component,
     * mirroring the workspaces-app RTL recipe in _sidebar.scss. */
    body[dir="rtl"][data-global-nav="side"] #app-space .application-body {
        --sidebar-padding:
            var(--sidebar-padding-top)
            calc(var(--gns-width-collapsed) + 1.5 * var(--sidebar-padding-left))
            var(--sidebar-padding-bottom)
            var(--sidebar-padding-right);
    }
    body[dir="rtl"][data-global-nav="side"] #app-space #application-body-workspaces {
        --sidebar-padding:
            var(--sidebar-padding-top)
            calc(2 * var(--gns-width-collapsed) + 1.5 * var(--sidebar-padding-left))
            var(--sidebar-padding-bottom)
            var(--sidebar-padding-right);
    }
    /* Open sidebars slide in from the right edge */
    body[dir="rtl"][data-global-nav="side"] #app-space .application-body.sidebar-left-open aside.sidebar.left {
        animation-name: gns-sidebar-slide-in-rtl;
    }
    /* Messages Element iframe + calendar canvas: mirror the wide rail
     * inset onto the right side. */
    body[dir="rtl"][data-global-nav="side"] #app-space .application-body.application-messages .pat-element,
    body[dir="rtl"][data-global-nav="side"] #app-space .application-calendar .pat-calendar#calendar-app {
        left: calc(var(--sidebar-margin-top, 15px) + 0.5 * var(--sidebar-padding-top, 25px));
        right: var(--sidebar-padding-left, 25px);
    }
}

/* The mobile bottom bar's leading Home button (the brandmark) — the
 * desktop rail uses the .gns-header logo instead, so hide it here. */
#global-nav-side .gns-mobile-home {
    display: none;
}

/* "More apps" overflow entry — mobile bar only; the desktop rail lists
 * every app vertically, so it has no use for it. */
#global-nav-side .gns-more-item {
    display: none;
}
/* The trigger is a <button popovertarget> — strip native chrome so it
 * reads as a plain rail icon; default cursor (it's a control). */
#global-nav-side .gns-more-trigger {
    background: none;
    border: none;
    font: inherit;
    color: inherit;
    cursor: default;
    width: 100%;
}
/* While the overflow popover is open, the trigger reads as active: the tile
 * stays transparent and the glyph is painted in the accent colour (fill for
 * filled icons, stroke for line icons). The popover is a DOM descendant of
 * the rail, so :has() matches from #global-nav-side; the ID prefix outranks
 * the rail's base `.gns-icon.app-icon`/`.rail-icon *` rules. */
#global-nav-side:has(.gns-more-popover:popover-open) .gns-more-trigger .gns-icon {
    background-color: transparent;
}
#global-nav-side:has(.gns-more-popover:popover-open) .gns-more-trigger .gns-icon.app-icon * {
    fill: var(--colour-accent) !important;
}
#global-nav-side:has(.gns-more-popover:popover-open) .gns-more-trigger .gns-icon.rail-icon * {
    fill: none !important;
    stroke: var(--colour-accent) !important;
}

/* The overflow popover (HTML Popover API) — a floating app launcher above
 * the bar. UA styles centre popovers in the viewport; override the inset/
 * margin to dock it just over the bar, spanning the bar's width. */
.gns-more-popover {
    position: fixed;
    inset: auto;
    margin: 0;
    bottom: calc(
        var(--workspace-tabs-bar-height, var(--gns-mobile-height))
        + 0.5 * var(--small-screen-padding, 10px)
        + env(safe-area-inset-bottom, 0px)
        + 10px
    );
    left: calc(0.5 * var(--small-screen-padding, 10px));
    right: calc(0.5 * var(--small-screen-padding, 10px));
    width: auto;
    max-width: none;
    box-sizing: border-box;
    padding: 16px;
    border: none;
    border-radius: var(--border-radii, 8px);
    background-color: var(--colour-floating-ui-elements-background, rgba(255, 255, 255, 0.9));
    -webkit-backdrop-filter: blur(10px) saturate(4) brightness(1.2);
    backdrop-filter: blur(10px) saturate(4) brightness(1.2);
    box-shadow: var(--pat-toolbar-box-shadow, var(--default-box-shadow));
    color: var(--gns-ink);
    /* Fade + slide in; allow-discrete animates the display/overlay flips */
    opacity: 0;
    transform: translateY(8px);
    transition:
        opacity var(--gns-transition),
        transform var(--gns-transition),
        overlay var(--base-animation-duration, 0.2s) allow-discrete,
        display var(--base-animation-duration, 0.2s) allow-discrete;
}
.gns-more-popover:popover-open {
    opacity: 1;
    transform: translateY(0);
}
@starting-style {
    .gns-more-popover:popover-open {
        opacity: 0;
        transform: translateY(8px);
    }
}
/* The mirrored personal menus stack above the app list; space each block
 * off from the next (and the app list below). */
#global-nav-side .gns-more-popover ul.menu {
    margin-bottom: 15px;
}
.gns-more-popover .gns-more-list {
    list-style: none;
    margin: 8px 0px;
    padding: 0;
    display: grid;
    /* minmax(0, 1fr) — not the default minmax(auto, 1fr) — so the nowrap app
     * labels can't blow the tracks wider than their half of the popover and
     * trigger a horizontal scrollbar; the labels ellipsize instead. */
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 2px;
    max-height: 60vh;
    overflow-x: hidden;
    overflow-y: auto;
}
.gns-more-popover .gns-more-link {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 5px;
    border-radius: 8px;
    text-decoration: none;
    color: var(--gns-ink);
    white-space: nowrap;
    overflow: hidden;
}
.gns-more-popover .gns-more-link:hover {
    background: color-mix(in srgb, var(--gns-ink) 6%, transparent);
}
/* #global-nav-side prefix so these outrank the rail's ID-scoped
 * `#global-nav-side .gns-icon.app-icon` base rules — the popover lives
 * inside the rail, so a class-only selector loses to them. */
#global-nav-side .gns-more-popover .gns-more-app .gns-icon {
    flex: 0 0 auto;
    box-sizing: border-box;
    width: 28px;
    height: 28px;
    padding: 5px;
    aspect-ratio: 1 / 1;
    border-radius: 6px;
}
/* Popover icons: line icons keep their stroke, filled icons their fill —
 * both painted in the muted ink, accent for the current app. */
.gns-more-popover .gns-more-app .gns-icon.app-icon * {
    fill: var(--gns-muted) !important;
}
.gns-more-popover .gns-more-app .gns-icon.rail-icon * {
    fill: none !important;
    stroke: var(--gns-muted) !important;
}
.gns-more-popover .gns-more-app.current .gns-more-link {
    color: var(--app-colour, var(--gns-accent));
    font-weight: 600;
}
/* Active app: icon tile fills with the app's colour and the glyph turns
 * white (fill for filled icons, stroke for line icons) — the same
 * white-on-app-colour treatment the rail uses for its current item. */
#global-nav-side .gns-more-popover .gns-more-app.current .gns-icon {
    background-color: var(--app-colour, var(--gns-accent));
}
#global-nav-side .gns-more-popover .gns-more-app.current .gns-icon.app-icon * {
    fill: #fff !important;
}
#global-nav-side .gns-more-popover .gns-more-app.current .gns-icon.rail-icon * {
    fill: none !important;
    stroke: #fff !important;
}
.gns-more-popover .gns-more-label {
    /* min-width: 0 lets the label shrink below its content width inside the
     * flex link, so overflow/ellipsis engages instead of pushing the row wide. */
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The ueber-back-button leads its toolbar-section — it must sit before
 * any sibling view-option icons (e.g. the visibility toggle) regardless
 * of source order. flex order: -1 wins whether the back button is a
 * direct child of the view-options section (Library detail) or is itself
 * wrapped in its own nested view-options section (pat_ueber_back_button). */
body[data-global-nav="side"] .pat-toolbar .toolbar-section .ueber-back-button,
body[data-global-nav="side"] .pat-toolbar .toolbar-section > .toolbar-section.view-options:has(> .ueber-back-button) {
    order: -1;
}

/* The Home dashboard's back-to-landing button is a mobile-only affordance
 * in Side mode: on desktop the rail's Home button already returns there,
 * so hide it there. (On mobile the rule below re-shows it, restyled as a
 * toolbar icon.)
 *
 * The in-app .document-body back button is NOT hidden on desktop: the rail
 * only switches apps, it can't pop a detail view back to the list it lives
 * in (e.g. a Library document back to the Library), so that toolbar
 * ueber-back-button has to stay visible at every width. */
@media only screen and (min-width: calc(768px + 1px)) {
    body[data-global-nav="side"] #portal .pat-toolbar .ueber-back-button {
        display: none !important;
    }
}

/* A toolbar whose ONLY content is the reveal-the-sidebar back button
 * (button.icon.back) is hidden on non-mobile by _canvas-toolbar.scss —
 * the button has nothing to do there, leaving an empty strip. That rule
 * deliberately KEEPS the bar on mobile, where the button is the page's
 * back affordance. But in Top (non-Side) nav mode the legacy mobile
 * back-nav proxy already carries that affordance, so the bar is just an
 * empty strip on mobile too — hide it. Side mode is excluded: there the
 * proxy is dropped and this toolbar back button IS the mobile back.
 * Selector mirrors the _canvas-toolbar.scss rule (flattened :not(:has())
 * — nested :has() is invalid). */
@media only screen and (max-width: 768px) {
    body:not([data-global-nav="side"]) .pat-toolbar:has(.toolbar-section.view-options > button.icon.back):not(:has(.toolbar-section.quick-functions)):not(:has(.toolbar-section.editor)):not(:has(.toolbar-section.navigation)):not(:has(.toolbar-section.view-options > :not(button.icon.back))) {
        display: none;
    }
}

/* The contacts profile/detail back toolbar (#contact-nav-toolbar) is hidden
 * on small screens by the contacts App.css (its ``--screen-small`` rule).
 * That fits Top nav — the legacy mobile nav-proxy carries "back" there and
 * the toolbar's ueber-back-button is hidden anyway. In Side mode the proxy
 * is gone and that ueber-back-button IS the back affordance (restyled as a
 * leading icon above), so keep the toolbar visible. !important + later
 * source beats the App.css container-query rule. */
@media only screen and (max-width: 768px) {
    body[data-global-nav="side"] #contact-nav-toolbar {
        display: block !important;
    }
}

/* ------------------------------------------------------------------ */
/* ≤768px: the rail becomes a bottom tab bar                           */
/* ------------------------------------------------------------------ */

@media only screen and (max-width: 768px) {
    /* Side mode drops the legacy mobile back-nav proxy
     * (quaive-mobile-navigation isn't rendered at all — see base.html), so
     * the app toolbar's own .ueber-back-button has to carry the back
     * affordance on small screens. _toolbar.scss hides it there for the
     * top-nav layout; re-show it while Side is active and restyle it as a
     * plain toolbar .icon (square, label hidden, left-open glyph in
     * :before) — see _canvas-toolbar.scss .icon. */
    body[data-global-nav="side"] #portal .pat-toolbar .ueber-back-button,
    body[data-global-nav="side"] .document-body .pat-toolbar .ueber-back-button {
        display: inline-block !important;
        width: var(--canvas-toolbar-button-height);
        height: var(--canvas-toolbar-button-height);
        padding: 0;
        margin: 0;
        line-height: var(--canvas-toolbar-button-height);
        font-family: fontello;
        font-weight: normal;
        text-indent: -1000em;
        overflow: hidden;
        color: rgba(0, 0, 0, 0.6);
        background-color: var(--pat-toolbar-icon-background-colour);
        border-radius: var(--button-border-radius);
    }
    /* Replace the arrow-triangle :before with the left-open glyph,
     * centred in the square the way a toolbar .icon paints its glyph. */
    body[data-global-nav="side"] #portal .pat-toolbar .ueber-back-button::before,
    body[data-global-nav="side"] .document-body .pat-toolbar .ueber-back-button::before {
        content: var(--glyph-left-open);
        font-family: fontello;
        font-size: 20px;
        text-indent: 0;
        text-align: center;
        line-height: calc(var(--canvas-toolbar-button-height) + 1px);
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        margin: 0;
        border: 0;
        border-radius: var(--button-border-radius);
        box-sizing: border-box;
    }
    /* Drop the rounded-rectangle background pseudo entirely. */
    body[data-global-nav="side"] #portal .pat-toolbar .ueber-back-button::after,
    body[data-global-nav="side"] .document-body .pat-toolbar .ueber-back-button::after {
        display: none;
    }
    body[data-global-nav="side"] #portal .pat-toolbar .ueber-back-button:hover,
    body[data-global-nav="side"] .document-body .pat-toolbar .ueber-back-button:hover {
        color: var(--pat-toolbar-icon-text-colour-hover);
        background-color: var(--pat-toolbar-icon-background-colour-hover);
    }

    /* When a .navigation section is present the mobile toolbar grid
     * squeezes its column down to a single button width (see _toolbar.scss),
     * leaving no room for the title — it shows only as a clipped sliver
     * beside the back icon. Drop it; the back icon carries the context.
     * Toolbars with no navigation section give the title the full first
     * column, so those keep their title. Mirrors the team-spaces/registry
     * small-screen "title shrinks away where there's no room" rule. */
    body[data-global-nav="side"] #portal .pat-toolbar:has(.toolbar-section.navigation) .toolbar-title,
    body[data-global-nav="side"] .document-body .pat-toolbar:has(.toolbar-section.navigation) .toolbar-title {
        display: none;
    }

    /* The ueber-back-button lives in a .view-options section, not the
     * .navigation one. On desktop that section is already the toolbar's
     * leading (first) grid column, but _toolbar.scss bumps view-options to
     * the MIDDLE column on small screens whenever an app is open with a
     * navigation section — pushing the back button off the leading edge.
     * Hand the leading slot back to the section that carries the back
     * button so it's always the first icon, like the native .icon.back.
     * (The title that normally sits first is hidden above, so the column
     * is free.) Other view-options sections keep the middle placement. */
    body[data-global-nav="side"] #portal .pat-toolbar .toolbar-section.view-options:has(.ueber-back-button),
    body[data-global-nav="side"] .document-body .pat-toolbar .toolbar-section.view-options:has(.ueber-back-button) {
        grid-area: first !important;
        justify-self: start !important;
    }

    /* Floating bottom bar — same recipe as the workspace-tabs bar on
     * small screens: inset pill, translucent blur background, shadow,
     * superimposed over the content. */
    body[data-global-nav="side"] #global-nav-side {
        top: auto;
        bottom: 10px;
        left: 10px;
        right: 10px;
        width: auto;
        height: var(--workspace-tabs-bar-height, var(--gns-mobile-height));
        flex-direction: row;
        align-items: stretch;
        /* Items size to content and spread across the bar; this pins the
         * first and last icon to the two ends at a constant distance from
         * the sides, regardless of how many icons are shown. The varying
         * count is absorbed by the gaps between the inner icons. */
        justify-content: space-between;
        background-color: var(--colour-floating-ui-elements-background, rgba(255, 255, 255, 0.8));
        -webkit-backdrop-filter: blur(10px) saturate(4) brightness(1.2);
        backdrop-filter: blur(10px) saturate(4) brightness(1.2);
        box-shadow: var(--pat-toolbar-box-shadow, var(--default-box-shadow));
        border-radius: var(--border-radii, 8px);
        border-top: none;
        overflow: hidden;
        padding: 0 8px;
        /* Auto-hide on scroll: slide down out of sight (see the
         * body.gns-scrolled-away rule below) and glide back on scroll-up. */
        transition: transform var(--base-animation-duration, 0.25s) ease;
    }
    /* Scrolling down pushes the bar fully below the viewport edge (its own
     * height + a buffer that clears the bottom inset and drop shadow);
     * scrolling up removes the class and it slides back into place. */
    body[data-global-nav="side"].gns-scrolled-away #global-nav-side {
        transform: translateY(calc(100% + 40px));
        /* Slide out more slowly than it slides back in (base rule's 0.25s),
         * so hiding feels as graceful as revealing. */
        transition: transform calc(var(--base-animation-duration, 0.25s) * 2) ease;
    }
    /* Collabora full-screen editing: the floating bar is superimposed over
     * the content, so on small screens it overlaps the office editor's own
     * toolbars/handles. Drop the rail entirely while a Collabora iframe is
     * open in the browser — the editor (with its built-in close button)
     * gets the whole viewport. :has() is live, so the bar returns the moment
     * the editor shell leaves the DOM. */
    body[data-global-nav="side"]:has(.collabora-editor-shell) #global-nav-side {
        display: none;
    }
    body[data-global-nav="side"] #global-nav-side .gns-spaces {
        display: none;
    }
    /* The header (logo + user avatar) is dropped on mobile — Home lives
     * in the brandmark button; the personal menu stays in the apps grid. */
    body[data-global-nav="side"] #global-nav-side .gns-header {
        display: none;
    }
    /* Brandmark Home button is the bar's leading item (order 0), pinned to
     * the left end by the bar's space-between. Matches an app tab's box
     * width (44px icon + 2px link padding each side) so the brandmark
     * occupies the same slot as the icons; its 32px image stays centred. */
    body[data-global-nav="side"] #global-nav-side .gns-mobile-home {
        order: 0;
        position: relative;
        display: flex;
        align-items: center;
        justify-content: center;
        flex: 0 0 auto;
        width: 48px;
        min-width: 0;
        padding: 0;
    }
    body[data-global-nav="side"] #global-nav-side .gns-mobile-home img {
        width: 32px;
        height: 32px;
        object-fit: contain;
    }
    /* display:contents dissolves the .gns-apps box so its .gns-item rows
     * become direct flex children of the bar, spread across the row by the
     * bar's space-between alongside the home button. */
    body[data-global-nav="side"] #global-nav-side .gns-apps {
        display: contents;
    }
    /* Messages (Element chat) is hidden from the mobile nav — both the bar
     * icon and its More-popover entry. */
    body[data-global-nav="side"] #global-nav-side .gns-item.gns-app-messages,
    .gns-more-popover .gns-more-app.gns-app-messages {
        display: none;
    }
    /* Cap the app icons on the bar; the rest are reached via the "More
     * apps" popover. Three by default — the cap rises with width below,
     * so narrower screens move more icons into the popover.
     * :not(.gns-more-item) keeps the trailing More entry on the bar. */
    body[data-global-nav="side"] #global-nav-side .gns-apps .gns-item:not(.gns-more-item):nth-child(n+4) {
        display: none;
    }
    /* The More entry is the bar's trailing item, mobile only — pinned to
     * the right end by the bar's space-between. */
    body[data-global-nav="side"] #global-nav-side .gns-more-item {
        display: block;
        flex: 0 0 auto;
        min-width: 0;
    }
    /* …but drop it when nothing overflows (no app past the cap), so it
     * never opens an empty popover. The threshold rises with the cap in
     * the breakpoints below. */
    body[data-global-nav="side"] #global-nav-side .gns-apps:not(:has(.gns-item:not(.gns-more-item):nth-child(4))) .gns-more-item {
        display: none;
    }
    body[data-global-nav="side"] #global-nav-side .gns-item {
        flex: 0 0 auto;
        min-width: 0;
    }
    body[data-global-nav="side"] #global-nav-side .gns-link {
        flex-direction: column;
        justify-content: center;
        gap: 2px;
        padding: 2px;
        height: 100%;
    }
    body[data-global-nav="side"] #global-nav-side .gns-icon {
        width: 44px;
        height: 44px;
        margin: 0;
    }
    body[data-global-nav="side"] #global-nav-side .gns-icon.app-icon {
        padding: 7px;
    }
    /* More-apps popover: tighten the app-icon tile padding on mobile.
     * Class-count beats the base popover rule (padding: 5px) above. */
    body[data-global-nav="side"] #global-nav-side .gns-more-popover .gns-more-app .gns-icon.app-icon {
        padding: 4px;
    }
    /* Active app: rounder tile square on the mobile bar. */
    body[data-global-nav="side"] #global-nav-side .gns-item.current .gns-icon.app-icon,
    body[data-global-nav="side"] #global-nav-side .gns-link[aria-current="page"] .gns-icon.app-icon {
        border-radius: var(--gns-mobile-active-tile-radius, 13px);
    }
    /* Mobile bar iconography is monochrome black — app glyphs and the
     * brandmark Home button alike. */
    body[data-global-nav="side"] #global-nav-side .gns-icon.app-icon * {
        fill: #000 !important;
    }
    /* Rail line icons: colour the stroke black, fill stays empty */
    body[data-global-nav="side"] #global-nav-side .gns-icon.rail-icon * {
        fill: none !important;
        stroke: #000 !important;
    }
    body[data-global-nav="side"] #global-nav-side .gns-mobile-home img {
        filter: brightness(0);
    }
    /* Home screen current (no app on screen): the brandmark sits on an
     * accent tile, mirroring the active app icon. The tile is a ::before
     * (a background on the img would be whitened by the invert filter);
     * the brandmark itself turns white via brightness(0) invert(1). */
    body[data-global-nav="side"]:not(:has(#app-space.state-on)) #global-nav-side .gns-mobile-home::before {
        content: "";
        position: absolute;
        top: 50%;
        left: 50%;
        width: 44px;
        height: 44px;
        transform: translate(-50%, -50%);
        border-radius: var(--gns-mobile-active-tile-radius, 13px);
        background: var(--colour-accent, var(--gns-accent));
    }
    body[data-global-nav="side"]:not(:has(#app-space.state-on)) #global-nav-side .gns-mobile-home img {
        position: relative;
        z-index: 1;
        filter: brightness(0) invert(1);
    }
    /* Icons only on the bar — no tab text (like the workspace tabs) */
    body[data-global-nav="side"] #global-nav-side .gns-label {
        display: none;
    }
    /* No hover tooltips on touch — labels are inline anyway */
    body[data-global-nav="side"] #global-nav-side .gns-link[data-gns-label]:hover::after {
        content: none;
    }
    /* No hover effects on the touch bar — a tap shouldn't leave a wash */
    body[data-global-nav="side"] #global-nav-side .gns-link:hover,
    body[data-global-nav="side"] #global-nav-side .gns-mobile-home:hover {
        background: transparent;
    }
    body[data-global-nav="side"] #global-nav-side .gns-badge {
        position: absolute;
        top: 4px;
        right: 50%;
        margin-right: -20px;
    }
    /* The bar floats OVER the content (like the workspace tabs) — no
     * bottom offset on #app-space/#portal; content scrolls under it. */
    body[data-global-nav="side"] #app-space,
    body[data-global-nav="side"] #portal {
        margin-left: 0;
        margin-right: 0;
    }
    /* …but a scrollable sidebar would otherwise end UNDER the floating
     * bar, hiding its last items. Pad its scroll content by the bar's
     * full footprint (height + its bottom inset + the safe area) plus a
     * little breathing room, so everything can scroll into view. */
    body[data-global-nav="side"] #app-space .sidebar-content,
    body[data-global-nav="side"] #app-space #sidebar-content {
        box-sizing: border-box;
        padding-bottom: calc(
            var(--workspace-tabs-bar-height, var(--gns-mobile-height))
            + 0.5 * var(--small-screen-padding, 10px)
            + env(safe-area-inset-bottom, 0px)
            + 20px
        );
    }
    /* Same story for document bodies: pad the bottom by the bar's full
     * footprint so a page's last lines clear the floating bar instead of
     * hiding behind it. */
    body[data-global-nav="side"] .document-body {
        padding-bottom: calc(
            var(--workspace-tabs-bar-height, var(--gns-mobile-height))
            + 0.5 * var(--small-screen-padding, 10px)
            + env(safe-area-inset-bottom, 0px)
            + 20px
        );
    }
    /* Portal / dashboard content pane: same story — pad its scroll bottom
     * by the floating bar's full footprint (height + bottom inset + safe
     * area) plus breathing room so the last portlets clear the bar. */
    body[data-global-nav="side"] #portal-content-pane {
        padding-bottom: calc(
            var(--workspace-tabs-bar-height, var(--gns-mobile-height))
            + 0.5 * var(--small-screen-padding, 10px)
            + env(safe-area-inset-bottom, 0px)
            + 20px
        );
    }
    /* Calendar canvas: small uniform insets, with the bottom raised clear
     * of the floating bar. (position:absolute lives in the desktop media
     * query, so re-assert it here for the insets to take effect.) */
    body[data-global-nav="side"] #app-space .application-calendar .pat-calendar#calendar-app {
        position: absolute;
        top: 8px;
        right: 8px;
        bottom: 83px;
        left: 8px;
    }
    /* Meetings (Jitsi) canvas: same insets as the calendar above, so it
     * sits clear of the floating bar. */
    body[data-global-nav="side"] #app-space .application-body.application-meetings .pat-jitsi {
        position: absolute;
        top: 8px;
        right: 8px;
        bottom: 83px;
        left: 8px;
    }
    /* NB: the dashboard's bare-feed styling (single full-width column, no
     * title, no panel chrome) is NOT here — it's driven by the Size 1
     * container query (gns-dash width < 660px) above, so it tracks the
     * actual column width rather than the viewport. */
}

/* Wider phones have room for more icons on the bar before spilling into
 * the More popover: raise the cap a notch per breakpoint. Each re-shows
 * the next app (overriding the base nth-child(n+6) hide) and pushes the
 * bar's hide threshold out by one.
 *
 * The popover lists ONLY the apps that didn't fit: it's the same list in
 * the same order, so hiding the first N (= the bar cap) leaves exactly the
 * overflow. Each breakpoint hides one more, mirroring the bar. */
@media only screen and (max-width: 768px) {
    .gns-more-popover .gns-more-app:nth-child(-n+3) {
        display: none;
    }
}
/* 360px covers the narrowest mainstream phones (iPhone 13 mini / SE = 375px,
 * 360px-class Androids): the trimmed icon footprint above leaves room for a
 * 4th app on the bar at that width, where the base band fit only 3. */
@media only screen and (min-width: 360px) and (max-width: 768px) {
    body[data-global-nav="side"] #global-nav-side .gns-apps .gns-item:not(.gns-more-item):nth-child(-n+4) {
        display: block;
    }
    body[data-global-nav="side"] #global-nav-side .gns-apps .gns-item:not(.gns-more-item):nth-child(n+5) {
        display: none;
    }
    .gns-more-popover .gns-more-app:nth-child(4) {
        display: none;
    }
    body[data-global-nav="side"] #global-nav-side .gns-apps:not(:has(.gns-item:not(.gns-more-item):nth-child(5))) .gns-more-item {
        display: none;
    }
}
@media only screen and (min-width: 490px) and (max-width: 768px) {
    body[data-global-nav="side"] #global-nav-side .gns-apps .gns-item:not(.gns-more-item):nth-child(-n+5) {
        display: block;
    }
    body[data-global-nav="side"] #global-nav-side .gns-apps .gns-item:not(.gns-more-item):nth-child(n+6) {
        display: none;
    }
    .gns-more-popover .gns-more-app:nth-child(5) {
        display: none;
    }
    body[data-global-nav="side"] #global-nav-side .gns-apps:not(:has(.gns-item:not(.gns-more-item):nth-child(6))) .gns-more-item {
        display: none;
    }
}
@media only screen and (min-width: 580px) and (max-width: 768px) {
    body[data-global-nav="side"] #global-nav-side .gns-apps .gns-item:not(.gns-more-item):nth-child(-n+6) {
        display: block;
    }
    body[data-global-nav="side"] #global-nav-side .gns-apps .gns-item:not(.gns-more-item):nth-child(n+7) {
        display: none;
    }
    .gns-more-popover .gns-more-app:nth-child(6) {
        display: none;
    }
    body[data-global-nav="side"] #global-nav-side .gns-apps:not(:has(.gns-item:not(.gns-more-item):nth-child(7))) .gns-more-item {
        display: none;
    }
}
@media only screen and (min-width: 670px) and (max-width: 768px) {
    body[data-global-nav="side"] #global-nav-side .gns-apps .gns-item:not(.gns-more-item):nth-child(-n+7) {
        display: block;
    }
    body[data-global-nav="side"] #global-nav-side .gns-apps .gns-item:not(.gns-more-item):nth-child(n+8) {
        display: none;
    }
    .gns-more-popover .gns-more-app:nth-child(7) {
        display: none;
    }
    body[data-global-nav="side"] #global-nav-side .gns-apps:not(:has(.gns-item:not(.gns-more-item):nth-child(8))) .gns-more-item {
        display: none;
    }
}
@media only screen and (min-width: 760px) and (max-width: 768px) {
    body[data-global-nav="side"] #global-nav-side .gns-apps .gns-item:not(.gns-more-item):nth-child(-n+8) {
        display: block;
    }
    body[data-global-nav="side"] #global-nav-side .gns-apps .gns-item:not(.gns-more-item):nth-child(n+9) {
        display: none;
    }
    .gns-more-popover .gns-more-app:nth-child(8) {
        display: none;
    }
    body[data-global-nav="side"] #global-nav-side .gns-apps:not(:has(.gns-item:not(.gns-more-item):nth-child(9))) .gns-more-item {
        display: none;
    }
}
/* Preferences gets its own trailing tab on the mobile bottom bar. It's kept
 * off the desktop rail (see the desktop utility-apps block above) and is
 * normally an overflow-only entry, so on mobile we force it back onto the bar
 * — past the per-width app cap (hence !important, which beats the nth-child
 * hide bands above whatever Preferences' dynamic position is), pin it last
 * with flex order so it reads as the final option, and drop its now-duplicate
 * entry from the more-apps popover. Mobile only — the desktop rail is
 * untouched. */
@media only screen and (max-width: 768px) {
    body[data-global-nav="side"] #global-nav-side .gns-apps .gns-item.gns-app-preferences {
        display: block !important;
        order: 1;
        flex: 0 0 auto;
        min-width: 0;
    }
    .gns-more-popover .gns-more-app.gns-app-preferences {
        display: none;
    }
}

/* Installed as a PWA (standalone): float the bar a little further from the
 * screen edges than in a normal browser tab (10px base above). */
@media only screen and (max-width: 768px) and (display-mode: standalone) {
    body[data-global-nav="side"] #global-nav-side {
        bottom: 23px;
        left: 23px;
        right: 23px;
    }
}

@keyframes gns-sidebar-slide-in {
    from {
        transform: translateX(-48px);
        opacity: 0;
    }
    to {
        transform: translateX(0);
        opacity: 1;
    }
}

/* RTL twin — sidebars live on the right, so they arrive from there */
@keyframes gns-sidebar-slide-in-rtl {
    from {
        transform: translateX(48px);
        opacity: 0;
    }
    to {
        transform: translateX(0);
        opacity: 1;
    }
}

/* ------------------------------------------------------------------ */
/* Reduced motion                                                      */
/* ------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
    #global-nav-side,
    #global-nav-side .gns-platform-logo img,
    #global-nav-side .gns-hub,
    #global-nav-side .gns-hub-name,
    #global-nav-side .gns-platform-name,
    #global-nav-side .gns-section-title,
    #global-nav-side .gns-label,
    #global-nav-side .gns-link,
    #global-nav-side .gns-icon.app-icon,
    body[data-global-nav="side"] #app-space,
    body[data-global-nav="side"] #portal {
        transition: none;
    }
    body[data-global-nav="side"] #app-space .application-body.sidebar-left-open aside.sidebar.left {
        animation: none;
    }
}

/* ================================================================== */
/* Compact mode (body[data-global-nav="compact"])                      */
/* ------------------------------------------------------------------ */
/* Based on Tabs mode: keeps the global header + portal, but drops the  */
/* open-app tabs bar and the portal quick-nav. A candy-box launcher in  */
/* the header (next to the personal menu) opens a pat-tooltip whose     */
/* content is the apps grid — the apps inject into the SPA shell like    */
/* the rail, so no tab/pane switching is needed.                        */

body[data-global-nav="compact"] #tabs-bar,
body[data-global-nav="compact"] .portal-quick-nav {
    display: none !important;
}

/* No tabs bar in Compact mode, so pull the app sidebar flush to the top/left
 * of the app area (reclaiming the space the tabs bar used to occupy). */
body[data-global-nav="compact"] .sidebar {
    --sidebar-margin-top: 0;
    --sidebar-margin-bottom: 0;
    --sidebar-margin-left: 0;
}
/* White sidebars in Compact mode. Set the variable (not background-color
 * directly) so the sidebar toggle — which also reads
 * --sidebar-background-colour — picks up the same white.
 * Also zero the sidebar's left margin HERE (on the document-body's ancestor,
 * not just on .sidebar): the document body computes its open-sidebar left
 * offset as calc(--sidebar-width + --sidebar-margin-left) in its own scope, so
 * without this it kept the default 15px margin and sat slightly too far right.
 * Now its left equals the sidebar's effective width. */
body[data-global-nav="compact"] .application-body {
    --sidebar-background-colour: white;
    --sidebar-margin-left: 0;
    /* Override the document body's open-sidebar left offset directly: the base
     * value is calc(--sidebar-width + --sidebar-margin-left), and the inner
     * --sidebar-margin-left doesn't pick up the 0 above at the document body's
     * scope (it keeps the 15px default), so the document body sat 15px too far
     * right. Drop the margin term entirely — left == the sidebar width. */
    --document-body-left-distance-sidebar-open: var(--sidebar-width);
}
/* Events app: white application-body background in Compact mode. */
body[data-global-nav="compact"] .application-body.application-events {
    --application-body-background-colour: white;
}
body[data-global-nav="compact"] aside.sidebar.left {
    height: 100%;
    border-radius: 0;
}
body[data-global-nav="compact"] aside.sidebar.left .sidebar-content {
    border-radius: 0;
}
/* A large sidebar (e.g. the Tasks list) spans the full viewport width in
 * Compact mode. Its base width is calc(100% + …) of the app body, which is
 * narrower than the viewport and left a strip on the right; pin it to the
 * viewport instead. This also pushes the document body fully off-screen, since
 * its open-sidebar left offset tracks --sidebar-width. */
body[data-global-nav="compact"] .application-body.sidebar-large aside.sidebar.left {
    --sidebar-width: 100vw;
}
/* The 100vw above only retargets --sidebar-width, but the OPEN large sidebar's
 * actual `width` is set by a separate, higher-specificity base rule:
 *   @container main (min-width: 769px) {
 *     .sidebar-left-open.sidebar-large aside.sidebar.left {
 *       width: calc(100% - 2 * var(--sidebar-margin-left));
 *     }
 *   }
 * The 2 × --sidebar-margin-left inset is the right thing in Tabs/Rail mode, but
 * in Compact it stops the sidebar short and leaves a strip on the right. The
 * sidebar is absolutely positioned and its containing block is #app-space
 * (left: 0; right: 0 — always the full viewport), so 100% with no inset spans
 * the viewport exactly (and, unlike 100vw, never adds a scrollbar-gutter
 * overflow). The extra body[data-global-nav]/.application-body terms out-rank
 * the base selector. */
body[data-global-nav="compact"] .application-body.sidebar-left-open.sidebar-large aside.sidebar.left {
    width: 100%;
}
/* Pin the document body beside the sidebar from the narrow-desktop breakpoint
 * (769px) in Compact mode. The base only shifts it right at >=1280px; between
 * 769-1280px it falls back to left:0 (full width, sidebar overlapping it) —
 * which read as "document body too wide". Compact has minimal chrome, so the
 * side-by-side layout should kick in earlier. */
@media only screen and (min-width: calc(768px + 1px)) {
    body[data-global-nav="compact"] .sidebar-left-open .document-body,
    body[data-global-nav="compact"] .sidebar-left-open #document-body {
        left: var(--document-body-left-distance-sidebar-open);
    }
    body[dir="rtl"][data-global-nav="compact"] .sidebar-left-open .document-body,
    body[dir="rtl"][data-global-nav="compact"] .sidebar-left-open #document-body {
        left: var(--document-body-right-distance);
        right: var(--document-body-left-distance-sidebar-open);
    }
}

/* Messages (Element) app fills its app body in Compact mode — pin all four
 * insets to 0 (the base rule offsets by the --sidebar-margin-* vars). */
body[data-global-nav="compact"] .application-body.application-messages .pat-element {
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
}
body[data-global-nav="compact"] .pat-element iframe {
    border-radius: 0;
}
/* Collabora office editor fills its app body flush in Compact mode. */
body[data-global-nav="compact"] .document-content .collabora-editor-frame {
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    border-radius: 0;
    box-shadow: none;
}

/* Compact-mode active-app label — pinned to the left of the global header,
 * vertically centred. Show/hide is driven by an inline <style> in the
 * kik_app_label partial (display:none by default, inline-flex for the active
 * app); here we only set its box + appearance. */
kik-app-label {
    position: absolute;
    left: 31px;
    top: 50%;
    transform: translateY(-50%);
    align-items: center;
    gap: 6px;
    color: var(--app-colour);
    z-index: 1;
}
body[dir="rtl"] kik-app-label {
    left: auto;
    right: 31px;
}
/* Square icon tile, a touch larger than the label text and centred against it.
 * Same line/background treatment as the help app's apps-section link-list
 * icons: faint wash, app-colour border and app-colour-filled glyph. */
kik-app-label .kal-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    box-sizing: border-box;
    width: 36px;
    height: 36px;
    padding: 5px;
    border: 1.5px solid var(--app-colour);
    border-radius: 6px;
    background-color: var(--ll-file-bg, rgba(0, 0, 0, 0.02));
    color: var(--app-colour);
}
kik-app-label .kal-icon svg {
    width: 100%;
    height: 100%;
}
kik-app-label .kal-name {
    font-weight: 400;
    font-size: 22px;
    white-space: nowrap;
}
/* Transparent close glyph that resolves to black on hover. */
kik-app-label .kal-close {
    border: 0;
    background: transparent;
    color: transparent;
    cursor: default;
    font-size: 20px;
    line-height: 1;
    padding: 0 2px;
}
kik-app-label .kal-close:hover {
    color: #000;
}

/* The shared .icon class supplies the header-icon box (float, size, centring).
 * We only override the colour so the SVG's currentColor stroke stays visible —
 * .icon sets color: transparent to hide glyph-font text. */
#global-header .candy-box-trigger {
    color: #6B6560;
}
#global-header .candy-box-trigger:hover {
    color: #000;
}
#global-header .candy-box-trigger.tooltip-active-click {
    color: var(--colour-accent);
}
#global-header .candy-box-trigger svg * {
    fill: none !important;
    stroke: currentColor !important;
}

/* Candy-box popover — a definite, viewport-clamped width sized for at most
 * four tiles across (the grid below is pinned to 4 columns), so the launcher
 * stays compact rather than spreading to many columns. */
.candy-box .tippy-box {
    width: min(92vw, 460px);
    max-width: min(92vw, 460px) !important;
}
.candy-box .quaive-apps-grid.candy-box-grid {
    /* Exactly four icons across, max. Overrides the base auto-fill (and the
     * small-screen 3-col rule) so the grid never widens past four. */
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 5px;
    margin-top: 28px;
    padding: 0 15px 10px;
}
.candy-box .quaive-apps-grid .tile .tile-title {
    font-size: 15px;
}
/* Paint the app-grid icons by their outline (stroke) + color rather than the
 * portal grid's solid fill. */
.candy-box .quaive-apps-grid .tile .icon {
    color: var(--app-colour);
}
.candy-box .quaive-apps-grid .tile svg {
    transform: scale(0.6);
}
.candy-box .quaive-apps-grid .tile svg > * {
    fill: none;
    stroke: var(--app-colour);
}
.candy-box .quaive-apps-grid .tile .icon,
.candy-box .quaive-apps-grid .tile svg,
.candy-box .quaive-apps-grid .tile:after {
    border-radius: 22%;
}
