/* 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 {
    /* The ink themes pin --app-icon-contrast-colour (black) so space
     * avatars join the ink palette; other brands fall through to the
     * per-space colour. */
    background: color-mix(in srgb, var(--app-icon-contrast-colour, var(--gns-space-colour, var(--gns-accent))) 10%, white);
    color: var(--app-icon-contrast-colour, var(--gns-space-colour, var(--gns-accent)));
    border: 1px solid var(--app-icon-contrast-colour, 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(--app-icon-contrast-colour, 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);
    }
    /* Mail webmail iframe — same inset "card" box as the Messages Element
     * iframe above so the two full-bleed iframe apps line up in hub mode.
     * (Mail has no base absolute rule, so set position here too; the default
     * is the full-bleed width/height:100% from apps.css.) */
    body[data-global-nav="side"] #app-space .application-body.application-mail .mail-webmail-frame {
        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);
        width: -webkit-fill-available;
        height: -webkit-fill-available;
        /* Same card chrome as the generic `.pat-element iframe` (workspace
         * chat / office editors): rounded corners + the standard drop shadow. */
        border-radius: var(--border-radii);
        box-shadow: var(--default-box-shadow);
    }
    /* 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).
 * Applies to Side AND Compact (Cloud) — both drop the legacy top-nav
 * mobile back proxy, so in both the toolbar's own back button is the
 * mobile back affordance (re-shown in the ≤768px block below). */
body:is([data-global-nav="side"], [data-global-nav="compact"]) .pat-toolbar .toolbar-section .ueber-back-button,
body:is([data-global-nav="side"], [data-global-nav="compact"]) .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 and Compact modes are
 * 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"]):not([data-global-nav="compact"]) .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 and Compact
 * modes 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:is([data-global-nav="side"], [data-global-nav="compact"]) #contact-nav-toolbar {
        display: block !important;
    }
}

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

@media only screen and (max-width: 768px) {
    /* Side and Compact (Cloud) modes drop the legacy mobile back-nav proxy
     * (quaive-mobile-navigation renders only for global_nav == 'top' — see
     * global_header.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/Compact is active
     * and restyle it as a plain toolbar .icon (square, label hidden,
     * left-open glyph in :before) — see _canvas-toolbar.scss .icon. */
    body:is([data-global-nav="side"], [data-global-nav="compact"]) #portal .pat-toolbar .ueber-back-button,
    body:is([data-global-nav="side"], [data-global-nav="compact"]) .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:is([data-global-nav="side"], [data-global-nav="compact"]) #portal .pat-toolbar .ueber-back-button::before,
    body:is([data-global-nav="side"], [data-global-nav="compact"]) .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:is([data-global-nav="side"], [data-global-nav="compact"]) #portal .pat-toolbar .ueber-back-button::after,
    body:is([data-global-nav="side"], [data-global-nav="compact"]) .document-body .pat-toolbar .ueber-back-button::after {
        display: none;
    }
    body:is([data-global-nav="side"], [data-global-nav="compact"]) #portal .pat-toolbar .ueber-back-button:hover,
    body:is([data-global-nav="side"], [data-global-nav="compact"]) .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:is([data-global-nav="side"], [data-global-nav="compact"]) #portal .pat-toolbar:has(.toolbar-section.navigation) .toolbar-title,
    body:is([data-global-nav="side"], [data-global-nav="compact"]) .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:is([data-global-nav="side"], [data-global-nav="compact"]) #portal .pat-toolbar .toolbar-section.view-options:has(.ueber-back-button),
    body:is([data-global-nav="side"], [data-global-nav="compact"]) .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 — same for the OnlyOffice (EUO) editor shell —
     * 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,
    body[data-global-nav="side"]:has(.onlyoffice-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;
    }
    /* 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;
}

/* Kill the two legacy top-chrome decorations #app-space paints for the classic
 * header: the drop-shadow gradient (::before — forced visible by
 * body:not(:has(#portal-tabs)), and Compact mode has no #portal-tabs) and the
 * separation line above the app area (state-on::after). Compact/Cloud mode's
 * own header supplies its edge, so both read as stray artefacts here — same
 * cleanup Side mode does for the ::before shadow above. */
body[data-global-nav="compact"] #app-space:not(.injecting)::before,
body[data-global-nav="compact"] #app-space.state-on::after {
    opacity: 0 !important;
}

/* With the header shadow gone (above), paint a faint 4px shadow just under the
 * cloud header, spanning the FULL viewport width, that reads as content sliding
 * beneath it. It hangs off #main::after rather than the content panes: a
 * pane-level ::after is trapped in that pane's stacking context —
 * .document-body sits at z-index 0 beside the z-index 1 sidebar, so its shadow
 * was clipped to the content area and never reached over the sidebar. #main is
 * the app shell — a position:fixed box pinned to all four viewport edges — so
 * its ::after spans the whole viewport and, sitting a level above #app-space
 * (z-index 11) with z-index 12, clears the sidebar and every app pane
 * edge-to-edge. position:fixed (offset by the header height) holds it in place
 * as content scrolls beneath.
 *
 * Both the fill and the thickness are read from vars with the default (a faint
 * 4px gradient) supplied as the var() *fallback* rather than declared here — so
 * the only place that ever sets these vars is a theme that wants to override
 * them. The ink theme (assets/brands/ink/style/all.css) sets them at :root to a
 * crisp 1px black line; because this file loads after the brand sheet, an
 * override declared on a selector here would beat ink — the fallback keeps ink
 * (:root) the sole declaration, so it wins. */
body[data-global-nav="compact"] #main::after {
    content: "";
    position: fixed;
    top: var(--global-header-height);
    right: 0;
    left: 0;
    height: var(--kik-cloud-top-shadow-height, 4px);
    background: var(--kik-cloud-top-shadow,
        linear-gradient(
            to bottom,
            color-mix(in srgb, black 8%, transparent) 0%,
            transparent 100%));
    z-index: 12;
    pointer-events: none;
}

/* 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);
}
/* Below the 1280px side-by-side breakpoint (769–1279px) in Compact/Cloud mode:
 * drop the open document body's left offset to 0 so it sits flush-left rather
 * than being pushed aside by the full sidebar width. Only at >=1280px do the
 * sidebar and document coexist side by side; narrower than that the panes flip
 * via focus-* (document fills / sidebar overlays), so the offset must be 0. */
@media only screen and (min-width: calc(768px + 1px)) and (max-width: calc(1280px - 1px)) {
    body[data-global-nav="compact"] .application-body {
        --document-body-left-distance-sidebar-open: 0;
    }
}
/* 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 app labels (multi-tasking): one label per OPEN app, laid out
 * left-to-right in #kik-app-labels. Each label is an app-colour-bordered pill of
 * [icon · name · close]. Show/hide + active highlighting are driven entirely by
 * the per-app :has() rules in the kik_app_label partial's inline <style> (no
 * JS); here we set the row layout, the box and the appearance.
 *
 * #kik-app-labels fills the header between the home brand and the right-floated
 * controls (avatar / candy box / favourites). It's the sole in-flow child of
 * #global-header and establishes a flex formatting context, so it shrinks to
 * clear those right floats automatically — no hard-coded width reserve. Its
 * margin-left clears the absolutely-positioned home tile (15px inset + 36px tile
 * + gap). overflow:hidden clips any labels that still don't fit once shrunk. */
/* #kik-app-labels is a BLOCK box (overflow:hidden establishes its own formatting
 * context) so its auto width FILLS the gap left of the right-floated controls in
 * every engine. The flex row lives on the inner .kik-app-labels-row. Flexing
 * #kik-app-labels directly breaks WebKit: a flex formatting context beside a
 * float is sized shrink-to-fit, collapsing to min-content — and with the names'
 * min-width:0 + ellipsis that is just the icons, so on iPad every name ellipsised
 * to nothing even on an empty header. A block box fills, giving the inner flex a
 * real width so names only ellipsise when genuinely crowded. */
#global-header .kik-app-labels {
    display: block;
    height: var(--global-header-height);
    margin-left: 37px;
    overflow: hidden;
    /* The row fills the full free width, but its empty stretch must not eat
     * taps meant for the content scrolled under the (transparent, pointer-events
     * :none) header on small screens — so only the labels themselves are live. */
    pointer-events: none;
}
#global-header .kik-app-labels .kik-app-labels-row {
    display: flex;
    align-items: center;
    gap: 6px;
    height: 100%;
    overflow: hidden;
}
#global-header .kik-app-labels kik-app-label {
    pointer-events: auto;
}
body[dir="rtl"] #global-header .kik-app-labels {
    margin-left: 0;
    margin-right: 44px;
}
kik-app-label {
    position: relative;
    align-items: center;
    gap: 0;
    /* Idle: icon glyph + name in the muted base header ink (#6B6560, the candy
     * box's colour), and a transparent border. The per-app active rule
     * (kik_app_label.html) overrides --kal-text / --kal-border to the app colour. */
    color: var(--kal-text, #6B6560);
    /* Same overall height as the global-header icons (36px), the 1.5px border
     * included (box-sizing). The inner .kal-icon is sized down to fit inside. */
    box-sizing: border-box;
    height: 36px;
    padding: 0;
    border: 1.5px solid var(--kal-border, transparent);
    border-radius: 8px;
    transition: border-color 0.2s;
    /* Flex item: shrink when the row is crowded (min-width:0 lets the name
     * ellipsis), while the icon + close inside it never shrink. */
    flex: 0 1 auto;
    min-width: 0;
}
/* Hovering an INACTIVE tab darkens its icon + name to black (mirrors the candy
 * box's hover). The active tab is immune: its per-app rule pins --kal-text /
 * --kal-icon-fg at a far higher specificity than this :hover. */
kik-app-label:hover {
    --kal-text: #000;
    --kal-icon-fg: #000;
}
/* The icon tile. Idle: transparent background, glyph in the app colour (the
 * label's border already carries the app identity). Active: the per-app rule
 * fills the tile with the app colour, flips the glyph white and scales it to 0.8
 * so a ring of the label's (white) background shows around it — all via the
 * --kal-icon-* custom properties, with a transition on the transform. */
kik-app-label .kal-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    /* Square tile, DEFINITE 33px (the label's 36px height minus its 1.5px
     * border top+bottom). The app-icon SVGs are viewBox-only (no intrinsic px
     * size). A definite width matters in TWO WebKit places:
     *   1. Visual: width:auto + the flex default min-width:auto lets the
     *      indeterminate SVG width balloon the tile to full width (old iOS bug).
     *   2. Layout: WebKit resolves an auto/aspect-ratio flex item's FLEX BASIS
     *      from the SVG's ~300px fallback intrinsic, NOT the aspect-ratio-derived
     *      width, so each tab is measured ~300px wide → the row over-subscribes →
     *      flex-shrink ellipsises every .kal-name even with a near-empty header
     *      (tiny icons + acres of space + truncated names, Safari/iPad only).
     * A definite width fixes both: real 33px basis, names ellipsise only when the
     * row is genuinely crowded. (Mirrored inline in kik_app_label.html so it ships
     * without a SASS recompile — the assets submodule is volatile.) */
    width: 33px;
    min-width: 33px;
    height: 33px;
    flex: 0 0 33px;
    /* contain:size makes WebKit measure the tile purely from its CSS size and
     * IGNORE the viewBox-only SVG's phantom intrinsic — the definite width above
     * is meant to do this alone, but some WebKit builds still leak a flex item's
     * content intrinsic into the ancestor (tab row) sizing pass, re-inflating the
     * row and re-ellipsising the names. contain:size + overflow:hidden slams that
     * door shut. */
    contain: size;
    overflow: hidden;
    padding: 4px;
    border-radius: 6px;
    background-color: var(--kal-icon-bg, transparent);
    color: var(--kal-icon-fg, #6B6560);
    transform: scale(var(--kal-icon-scale, 1));
    transition: all 0.2s;
    /* The icon is the app-label menu trigger; disable it on inactive tabs so a
     * click there can't open the menu. The active tab re-enables it via
     * --kal-icon-pointer (set in the per-app active rule in kik_app_label.html). */
    pointer-events: var(--kal-icon-pointer, none);
}
/* Roughly half the app icons are authored as solid white silhouettes
 * (fill="white"); tie their fill to currentColor so they follow .kal-icon's
 * colour exactly like the outline (stroke: currentColor) icons do. Outline icons
 * (fill: none) are intentionally NOT matched, so they stay outlines. */
kik-app-label .kal-icon svg[fill="white"] *,
kik-app-label .kal-icon svg [fill="white"] {
    /* Ink flips this to a transparent fill + a NON-scaling stroke so the
     * solid-silhouette icons read as black outlines (like the app grid) at the
     * tiny tab size; without non-scaling-stroke a 1px stroke in the 512 viewBox
     * collapses to sub-pixel and the glyph vanishes. Defaults (fill:currentColor,
     * no stroke) keep every other brand's filled rendering. Target the shapes,
     * not the <svg>, so vector-effect (not inherited) actually applies. */
    fill: var(--kal-icon-fill, currentColor);
    stroke: var(--kal-icon-stroke, none);
    stroke-width: var(--kal-icon-stroke-width, 0);
    vector-effect: non-scaling-stroke;
}
kik-app-label .kal-icon svg {
    width: 100%;
    height: 100%;
    transform: scale(var(--kal-icon-svg-scale, 0.8));
}
kik-app-label .kal-icon svg * {
    transition: all 0.2s;
}
/* The tab body: a pat-switch <button> that activates this app, reset to read as
 * plain text (it inherits the label's app colour). */
kik-app-label .kal-name {
    appearance: none;
    border: 0;
    background: transparent;
    padding: 0;
    margin: 0;
    color: inherit;
    font-family: inherit;
    text-align: left;
    cursor: default;
    font-weight: 400;
    font-size: 22px;
    white-space: nowrap;
    /* Only the text is sacrificed when the row is crowded (req: close + icon
     * stay intact). */
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
    flex: 0 1 auto;
}
/* Stretched-link hit-layer: the name button's switch covers the WHOLE tab, so a
 * click anywhere on it activates the app. It sits BEHIND the icon and × (z-index
 * 1 vs their 2): an inactive icon (pointer-events:none) lets the click fall
 * through to it, while the active icon (pointer-events:auto) and the × stay on
 * top with their own actions. overflow:hidden on the button doesn't clip it —
 * the ::before's containing block is the (position:relative) kik-app-label. */
kik-app-label .kal-name::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
}
kik-app-label .kal-icon,
kik-app-label .kal-close {
    position: relative;
    z-index: 2;
}
/* Close button (an <a> → close_app, so the close is recorded in the session):
 * the × in the muted base header ink for inactive tabs (the active tab's rule
 * flips --kal-close-colour to the app colour), no background at rest; on hover a
 * faint app-tinted wash (20% app colour over 80% white). */
kik-app-label .kal-close {
    border: 0;
    background: transparent;
    text-decoration: none;
    cursor: default;
    font-size: 14px;
    line-height: 1;
    padding: 0;
    border-radius: 50%;
    color: var(--kal-close-colour, #6B6560);
    /* Hidden + collapsed on inactive tabs (no × taking space); the active tab's
     * rule sets --kal-close-w / --kal-close-mx / --kal-close-opacity to reveal it.
     *
     * Collapse by WIDTH, not `zoom`. `zoom` is a WebKit layout property, and zoom
     * on a flex item corrupts the tab row's intrinsic sizing: WebKit reserved each
     * inactive ×'s footprint at its UNZOOMED ~18px+margins (not the zoomed-away
     * size it painted), so every inactive tab carried a phantom close that
     * over-subscribed the row → flex-shrink ellipsised the names even with room to
     * spare (Safari/iPad only — the active tab's honest zoom:1 × never triggered
     * it). Width/margin are sized honestly by every engine: 0 when collapsed, 18px
     * on the active tab, eased. Mirrored inline in kik_app_label.html (which also
     * neutralises the stale built-CSS zoom until the submodule recompiles). */
    opacity: var(--kal-close-opacity, 0);
    transition: opacity 0.2s ease, width 0.2s ease, margin 0.2s ease;
    display: flex;
    flex: 0 0 auto;
    box-sizing: border-box;
    width: var(--kal-close-w, 0);
    min-width: var(--kal-close-w, 0);
    height: 18px;
    overflow: hidden;
    align-items: center;
    justify-content: center;
    margin: 0 var(--kal-close-mx, 0);
}
kik-app-label .kal-close:hover {
    background-color: color-mix(in srgb, var(--app-colour) 20%, #fff);
}
/* Thumb-sized hit area without a bigger ×: an invisible pseudo extends the
 * clickable surface well past the 18px visual button — generous above/below
 * and to the right, a little less to the left where the tab's own switch
 * surface lives. Hit-testing only, nothing painted; the <a> stays the event
 * target so the pat-switch/pat-inject close wiring is untouched. */
kik-app-label .kal-close::before {
    content: '';
    position: absolute;
    inset: -10px -8px -10px -6px;
}

/* 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 {
    /* Up to four icons across on roomy viewports. Overrides the base auto-fill
     * so the grid never widens past four. */
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 5px;
    margin-top: 28px;
    padding: 0 15px 10px;
}
/* On small screens four tiles squeeze the (often long) app labels into
 * ellipses; drop to three across and tighten the gap + surrounding padding a
 * touch so the wider tiles have room to breathe. Reuses the app-wide
 * ``--screen-small`` flag (set on the ``screen`` container ≤769px). */
@container screen style(--screen-small: true) {
    .candy-box .quaive-apps-grid.candy-box-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 3px;
        padding: 0 10px 8px;
    }
}
.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 svg {
    /* transform: scale(0.6); */
}
.candy-box .quaive-apps-grid .tile .icon {
    /* Same contrast hook as the apps grid: the Ink theme's :root
     * --app-tile-icon-colour: black paints these black; every other brand
     * falls through to the per-app colour. */
    color: var(--app-tile-icon-colour, 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-tile-icon-colour, var(--app-colour));
    stroke-width: 1.5px;
}
.candy-box .quaive-apps-grid .tile .icon,
.candy-box .quaive-apps-grid .tile svg,
.candy-box .quaive-apps-grid .tile:after {
    border-radius: var(--app-icon-border-radius);
}

/* Cloud/compact mode: the global search FIELD (base.html) is swapped for a
 * launcher button for the Search app, sitting in the field's old header slot
 * (floated right, next to the candy-box). The Search rail icon is a stroked
 * (outline) magnifier painted with currentColor, so the launcher only sets
 * `color`: the app colour by default (visible on the light header). It's pure
 * pat-switch — clicking just brings the always-present #application-body-search
 * pane forward (its pat-lazy-load anchor autoloads the search home the first
 * time it's revealed). */
#global-header .kik-search-launcher {
    float: right;
    /* Match the kik-app-label height (36px, border included), so size the
     * launcher the same and centre it vertically in the header. */
    margin-top: calc((var(--global-header-height) - 36px) / 2);
    margin-left: var(--application-label-white-space);
    box-sizing: border-box;
    width: 36px;
    height: 36px;
    padding: 5px;
    border: 0;
    border-radius: 6px;
    background-color: transparent;
    color: var(--app-colour);
    cursor: pointer;
    transition: background-color var(--base-animation-duration),
                color var(--base-animation-duration);
}
body[dir="rtl"] #global-header .kik-search-launcher {
    float: left;
    margin-left: 0;
    margin-right: var(--application-label-white-space);
}
#global-header .kik-search-launcher svg {
    display: block;
    width: 100%;
    height: 100%;
    stroke: currentColor;
}
#global-header .kik-search-launcher:hover {
    background-color: rgba(0, 0, 0, 0.06);
}

/* "current": Search is the active, on-screen app — fill the tile with the app
 * colour and flip the outline glyph to white so it reads against it, mirroring
 * the side-rail .gns-item.current treatment. The :has() reuses kik-app-label's
 * active-app detection (#app-space.state-on.active-app-search). This file is
 * cache-busted (asset_version), so the :has() rule can live here rather than
 * inline in the template. */
body[data-global-nav="compact"]:has(#app-space.state-on.active-app-search) #global-header .kik-search-launcher {
    background-color: var(--app-colour);
    color: #fff;
}
body[data-global-nav="compact"]:has(#app-space.state-on.active-app-search) #global-header .kik-search-launcher:hover {
    background-color: var(--app-colour);
}
