@scope (.native-shell) {
/*
 * sidebar-base.css — Sidebar module-owned base styles.
 *
 * Owned module: public/assets/css/core/sidebar/
 *
 * Contains the foundation that the other sidebar-*.css
 * files build on:
 *
 *   - First-paint hydration contract (data-app-no-anim + collapsed
 *     pre-paint override)
 *   - .sb root variable scopes (dark + light theme tokens)
 *   - .sb dimensions, transitions, expanded/collapsed widths
 *   - .sb floating chrome (border-radius / border / shadow)
 *   - Non-calendar sticky-card positioning
 *   - Mobile @media @ 900px collapse lock
 *   - .li shared SVG stroke style (used by every sidebar SVG icon)
 *
 *
 * Loaded by base.php AFTER core/shell.css so the cascade order keeps
 * shell-level tokens upstream of the sidebar variables defined here.
 */

/* ════════════════════════════════════════════════════════════════════
 * FIRST-PAINT HYDRATION (no-anim window + collapsed override).
 *
 *   These two rule groups eliminate the "open then close" flicker on
 *   collapsed-state hard reloads. Two factors compound the flash and
 *   each group addresses one of them:
 *
 *   A. The server always renders `<div class="sb expanded">` because
 *      the partial has no access to localStorage. The defer-loaded
 *      sidebar JS swaps to `.sb.collapsed` after the first paint,
 *      which triggers the 0.28s width transition declared on `.sb`.
 *      The user sees a brief 240px sidebar that animates down to 64px.
 *
 *   B. Even after disabling the transition, the first paint at 240px
 *      still reads as a flash before the class swap snaps the width
 *      to 64px.
 *
 *   Group 1 (no-anim) handles A: while <:scope data-app-no-anim> is
 *   set (synchronously in base.php boot script, before any CSS
 *   parses), every transition and animation in the document is
 *   forced to none. The defer JS removes the attribute via two
 *   nested rAFs after the class hydration runs.
 *
 *   Group 2 (collapsed override) handles B: when the boot script
 *   has set data-app-sidebar-collapsed="true" on <:scope>, every
 *   visual rule that the eventual `.sb.collapsed` class would apply
 *   is replicated here against the still-`.sb.expanded` element.
 * ════════════════════════════════════════════════════════════════════ */

/* Group 1 — no-anim window. */
:scope[data-app-no-anim],
:scope[data-app-no-anim] *,
:scope[data-app-no-anim] *::before,
:scope[data-app-no-anim] *::after {
  transition: none !important;
  animation-duration: 0.001s !important;
  animation-delay: 0s !important;
}

/* Group 2 — collapsed-state hydration. */
:scope[data-app-sidebar-collapsed="true"] .sb.expanded { width: 64px; flex-basis: 64px; }
:scope[data-app-sidebar-collapsed="true"] .sb.expanded .logo-word { opacity: 0; pointer-events: none; }
:scope[data-app-sidebar-collapsed="true"] .sb.expanded .logo-mark { opacity: 1; pointer-events: auto; }
:scope[data-app-sidebar-collapsed="true"] .sb.expanded .item-text,
:scope[data-app-sidebar-collapsed="true"] .sb.expanded .item .ch,
:scope[data-app-sidebar-collapsed="true"] .sb.expanded .label,
:scope[data-app-sidebar-collapsed="true"] .sb.expanded .submenu,
:scope[data-app-sidebar-collapsed="true"] .sb.expanded .uinfo,
:scope[data-app-sidebar-collapsed="true"] .sb.expanded .ucaret { display: none !important; }
/* Mirror of `.sb.collapsed .collapse-btn` (sidebar-nav.css) — hide the toggle
 * under the centered "O" mark so first paint shows no button flash. */
:scope[data-app-sidebar-collapsed="true"] .sb.expanded .collapse-btn {
  position: absolute;
  left: 50%; top: 50%;
  margin: 0;
  transform: translate(-50%, -50%);
  opacity: 0;
  pointer-events: none;
}
:scope[data-app-sidebar-collapsed="true"] .sb.expanded .collapse-tip__txt--close { display: none; }

.sb {
  /* Sidebar surface hierarchy — bg sourced from --ds-color-bg-chrome
   * (dark = #1d1d1f, design-tokens.css:702 — the chrome bg was lightened
   * from #0a0a0c). The grey text ramp below is re-tuned to clear WCAG AA
   * (≥4.5:1) against that #1d1d1f surface; the prior #6e6e72/#555558/#4a4a4e
   * ramp (tuned for the old near-black bg) measured ~3.0 / ~2.0 / ~1.5:1. */
  --c-bg: var(--ds-color-bg-chrome, #1d1d1f);
  --c-hover: #14161a;
  --c-active: #1f2228;
  --c-text: #ffffff;
  --c-text-medium: #9ba0ac;
  --c-text-muted: #8a8a8e;   /* ~4.9:1 on #1d1d1f (idle nav / sub-items / flyout items) */
  --c-text-soft: #86868a;    /* ~4.6:1 (account email + caret) */
  --c-text-dim: #88888c;     /* ~4.7:1 (section labels / footer) */
  --c-divider: #1f2228;
  --c-border-soft: #1f2228;
  --c-rail: #2a2e36;
  --c-rail-active: #ffffff;
  --c-rail-glow: rgba(255, 255, 255, 0.4);
  --c-shadow: 0 20px 50px rgba(0,0,0,0.25);
  --c-popup-bg: #17191f;
  --c-popup-border: #2a2e36;
  --c-popup-hover: #232327;
  --c-popup-button: #232327;
  --c-popup-button-border: #2e2e32;
  --c-popup-shadow: 0 16px 40px rgba(0,0,0,0.6), 0 0 0 0.5px rgba(255,255,255,0.04);
  --c-up-popup-shadow: 0 20px 50px rgba(0,0,0,0.7), 0 0 0 0.5px rgba(255,255,255,0.04);
  --c-up-divider: #2a2e36;
  --c-collapse-btn-bg: #1f2228;
  --c-collapse-btn-border: #2a2e36;
  --c-collapse-btn-text: #888;
  --c-collapse-btn-hover-bg: #2a2e36;
  --c-foot-hover: #15151a;
  --c-tooltip-bg-from: #2a2e36;
  --c-tooltip-bg-to: #1f1f23;
  --c-tooltip-text: #ffffff;
  --c-tooltip-border: rgba(255, 255, 255, 0.06);
  --c-tooltip-shadow: 0 12px 28px -8px rgba(0,0,0,0.6), 0 4px 10px -2px rgba(0,0,0,0.4), inset 0 1px 0 rgba(255,255,255,0.05);
  --logo-filter: invert(1) brightness(1.1);
}

.sb {
  background: var(--c-bg);
  /* border-radius is owned by the floating-chrome rule below
   * (var(--ollira-frame-radius, 18px)); no value here to avoid a dead override. */
  display: flex;
  flex-direction: column;
  position: relative;
  box-shadow: var(--c-shadow);
  height: 700px;
  overflow: visible;
  flex-shrink: 0;
  /* 2026-05-18 — Calendar Day V4 Desktop Shell Geometry Fix.
   *
   *   The sidebar is a flex child of .ollira-stage. Browsers compute
   *   inline-axis size from flex-basis FIRST, then `width` only acts as
   *   a tie-breaker; on a flex item where flex-basis is `auto` (the
   *   default), the resolved basis tracks the `width` declaration —
   *   so animating `width` alone DOES animate the flex slot.
   *
   *   That works on its own, but when V4's WorkspaceFrameController
   *   predicts the calendar's available width during the same 280ms
   *   sidebar motion, it samples the sidebar's currentWidth via
   *   getBoundingClientRect() and interpolates with the SAME cubic-
   *   bezier(0.4, 0, 0.2, 1) declared here. For the prediction to
   *   match the painted geometry sub-pixel, the flex-basis must
   *   advance on the SAME curve as `width`. Making flex-basis an
   *   explicit transition target (instead of relying on the
   *   `width`-tracks-`basis` behaviour, which differs slightly
   *   between engines under high CPU load) pins that contract.
   *
   *   The transition runs on width AND flex-basis with identical
   *   timing so the visual width and the layout basis are always in
   *   the same animation frame. */
  transition:
    width      0.28s cubic-bezier(0.4, 0, 0.2, 1),
    flex-basis 0.28s cubic-bezier(0.4, 0, 0.2, 1),
    background 0.35s ease,
    box-shadow 0.35s ease;
}
/* Explicit flex-basis values match the width literals so the two
 * properties animate from the same pair of endpoints. */
.sb.expanded  { width: 240px; flex-basis: 240px; }
.sb.collapsed { width:  64px; flex-basis:  64px; }

/* Shared SVG stroke style — used by every sidebar inline SVG icon
 * (.collapse-btn svg, .item .ic, .item .ch, .sub-item, .ucaret,
 *  .up-branch svg, .up-item svg, .br-caret, .up-toggle, etc.). */
.li { fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }


/* === Sidebar floating-chrome integration ================================
 * The sidebar is a floating card lifted on the page backdrop. Same chrome
 * applies on every route; calendar-route height overrides live in
 * core/sidebar/sidebar-calendar-context.css. */
.sb {
  /* DO NOT set `overflow: hidden` here. The sidebar contains
   * four absolute-positioned popups that MUST extend past its edges:
   *   .collapse-tip  — `left: calc(100% + 8px)` (toggle-hover pill)
   *   .tooltip       — `left: calc(100% + 14px)` (collapsed-mode tip)
   *   .flyout        — `left: calc(100% + 10px)` (collapsed appts menu)
   *   .user-popup    — `left: calc(100% + 10px)` (account popup)
   * `overflow: hidden` clips ALL of them. The base `.sb { overflow:
   * visible }` rule above is the correct setting; this rule deliberately
   * omits any overflow declaration so the base default keeps applying. */
  border-radius: var(--ollira-frame-radius, 18px);
  /* unified-surface seam: the sidebar's only edge is a single hairline against the
     content (no floating island border/shadow). Mobile (≤900px) clears it below. */
  border: 0;
  border-right: 1px solid var(--ds-color-border-subtle, rgba(0,0,0,.08));
  box-shadow: none;
}

/* Non-calendar — sidebar sticks to viewport top during page scroll.
 *
 * z-index: 200 is LOad-BEARING, not decorative. `position: sticky` ALWAYS
 * forms a stacking context, so the four rail popups (.tooltip / .flyout /
 * .user-popup / branch dropdown) are trapped inside .sb: their authored
 * z-index 200–220 only orders them against each other, never against the
 * page. With .sb left at the default `z-index: auto`, the whole rail paints
 * at the ~0 level of the stage, so any page element with a positive z-index
 * (sticky toolbars / cards at 100–110, date popovers at 70, etc.) renders
 * OVER the open flyout — the collapsed-rail menus bled through behind page
 * content. Lifting .sb itself to 200 puts the entire rail subtree above page
 * content (≤150) while staying below the fixed modal/drawer/toast overlays
 * (600+, all position:fixed in the root context, so they still cover it).
 * Calendar routes keep .sb at position:relative / z-index:auto (no sticky
 * trap there), so this is correctly scoped to :not(.is-calendar-workspace). */
:scope:not(.is-calendar-workspace) .sb {
  position: sticky;
  top: var(--ollira-frame-outer, 22px);
  align-self: flex-start;
  height: calc(100vh - (var(--ollira-frame-outer, 22px) * 2));
  max-height: calc(100vh - (var(--ollira-frame-outer, 22px) * 2));
  z-index: 200;
}

/* Tablet / mobile: under 900px the sidebar locks to its 64px collapsed
 * form. Sidebar drops its rounded corners + shadow on mobile. */
@media (max-width: 900px) {
  .sb {
    height: 100vh;
    max-height: 100vh;
    border-radius: 0;
    border: 0;
    box-shadow: none;
    position: static;
  }
  :scope:not(.is-calendar-workspace) .sb {
    position: static;
    height: 100vh;
    max-height: 100vh;
  }
  .sb.expanded,
  .sb.collapsed { width: 64px !important; flex-basis: 64px !important; }
  .sb .item-text,
  .sb .ch,
  .sb .label,
  .sb .submenu,
  .sb .uinfo,
  .sb .ucaret,
  .sb .logo-word { display: none !important; }
  .sb .logo-mark { opacity: 1 !important; }
  .sb .collapse-btn { display: none; }
}

/* Respect reduced-motion — tame every sidebar transition/animation (submenu
 * unroll, flyout/tooltip fades, chevron rotation, theme cross-fade, width). */
@media (prefers-reduced-motion: reduce) {
  .sb,
  .sb *,
  .tooltip {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}

}
