
/* -- Tokens -- */
:root{
  color-scheme:only light; /* OLP-527: openlap is light-only; pin the UA so dark-OS boxes never auto-darken native controls/scrollbars/canvas */
  /* 2026-05-22 (@barath): the whole builder console renders in the native
     system stack — no web-loaded Inter (its 400 weight has thin stems and
     reads gray at 15px; the OS font is hinted to the pixel grid and renders
     crisp). The feed body (.ol-card) and the nav chrome now share ONE stack via
     --f-body so the rail + feed are visually consistent. The Inter Google Fonts
     <link> is dropped from renderShell's head. OLP-532: --f-feed retired — it
     was byte-identical to --f-body; all its sites now reference --f-body. */
  --f-body:-apple-system,system-ui,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;
  --f-mono:'SF Mono',ui-monospace,Menlo,Consolas,monospace;
  --c-bg:#ffffff;
  /* 2026-05-16 (@barath): retone — six cool blue-gray tokens flipped
     to warm-neutral so the page reads bright-white like Gmail rather
     than cooler Twitter-blue-tinted. Pre-retone values (Twitter-clone
     defaults) kept in comments for diff:
       --c-bg-secondary:#f7f9f9 (was: same)
       --c-border:       #eff3f4 -> #ebebeb
       --c-bg-soft:      #f4f5f7 -> #f5f5f5
       --c-bg-tray:      #f7f8fa -> #f8f8f8
       --c-fill:         #eff3f4 -> #f0f0f0
       --c-page:         #f4f5f7 -> #f5f5f5
     Twitter-blue accent stays. */
  --c-bg-secondary:#f8f8f8;
  --c-bg-hover:rgba(0,0,0,0.03);
  --c-primary:#0f1419;
  --c-secondary:#536471;
  --c-tertiary:#8b98a5;
  --c-border:#ebebeb;
  --c-border-dark:#cfd9de;
  --hairline:1px solid var(--c-border); /* OLP: the one boundary line. Every hairline edge (column sides, dock, composer) uses var(--hairline); never re-hardcode 1px solid var(--c-border). New components inherit it for free. */
  --c-accent:#1d9bf0;
  --c-accent-hover:#1a8cd8;
  --c-accent-light:rgba(29,155,240,0.1);
  --c-danger:#f4212e;
  --c-danger-light:rgba(244,33,46,0.1);
  --c-shared:#c2566a;       /* OLP-284: mild (muted) red for owner-shared channel rows */
  --c-pinned:#c6901a;       /* OLP-439: deepened system-yellow gold for pinned-channel # glyph (legible at 1.4px stroke on white) */
  --c-alert:#c2566a;        /* OLP-419: watcher/routine alert red — own semantic token (decoupled from --c-shared) at the same red primary value */
  --c-success:#00ba7c;
  --c-success-light:rgba(0,186,124,0.1);
  --c-warning:#ffad1f;
  --c-warning-light:rgba(255,173,31,0.1);
  /* OLP-578 #7: --c-guest / --c-guest-fill (guest-attribution amber) RETIRED —
     guest posts render pixel-identical to a member's, no visible guest tint. */
  --c-fill:#f0f0f0;
  /* OLP-573: LAPS expanded-group two-tone wash. The expanded title row keeps
     --c-fill (#f0f0f0, unchanged); the criteria body drops to the lighter
     --c-wash with a faint --c-seam hairline at the title/criteria join, so the
     boundary reads as a section header over a calmer body (iOS inset-group). */
  --c-wash:#f8f9fa;
  --c-seam:rgba(15,20,25,.06);
  /* OLP-227: Now Playing tray + hero soft-bg tokens. --c-bg-soft is the
     sidebar tray background (calmer than the page bg, lighter than fill).
     --c-bg-tray is reserved for any future inner-of-tray nesting. */
  --c-bg-soft:#f5f5f5;
  --c-bg-tray:#f8f8f8;
  /* 2026-05-16 (@barath): portal canvas. r5 flipped base to pure
     white (#ffffff) — the cream was too low-contrast under dense
     text. Dot grid still shows on white; left-edge inset shadow
     dropped (was over-decorating an already-bounded panel). */
  --c-portal-bg:#ffffff;
  /* OLP-230 A010: page wash beneath sidebar boxes (Direction 4 redesign foundations).
     Distinct semantic from --c-bg-soft: page-level neutral that boxed sidebar
     containers (.ol-box) float on, with --c-bg as the box fill. Value parity
     with --c-bg-soft today; the seam exists so the page wash can drift from
     soft-tray fills if Direction 4 surface laps want a different separation. */
  --c-page:#f5f5f5;
  /* 2026-05-16 (@barath): surface tokens for the three live workspaces.
     All three default to pure white — the faint cool tint on the nav
     (#F8FAFD, dropped) clashed with the brand. Override at request
     time via ?palette.nav=%23XXXXXX etc — see live-palette JS in
     renderShell head. */
  --c-nav-bg:#ffffff;
  --c-feed-bg:#ffffff;
  /* Rail glyph render size (re-tuned 2026-05-16 post-UAT). 13px tightens
     under cap-height of 15px Inter labels — the prior 14px read chunky
     in context, especially the blue working state. Paired with
     stroke-width 1.4 baked into icons.go symbol definitions and the
     V1-italic-lean hash verticals for visual lightness. */
  --rail-glyph-size:13px;
  /* OLP-460: rail-row geometry tokens — one source of truth for the single
     glyph axis. Every rail entry (.ol-nav-row) inherits these; no row hardcodes
     its own slot/pad/gap. Move the axis here and the whole rail moves with it.
     --rail-glyph-slot 22px + --rail-row-pad-x 14px land every leading glyph
     center on x=33 (margin 8 + pad 14 + slot/2 11). */
  --rail-glyph-slot:22px;
  --rail-row-pad-x:14px;
  --rail-gap:10px;
  --sp-xs:4px;--sp-sm:8px;--sp-md:12px;--sp-lg:16px;--sp-xl:24px;
  --r-sm:8px;--r-md:12px;--r-lg:16px;--r-pill:9999px;
}
*{margin:0;padding:0;box-sizing:border-box}
/* OLP-570: the auto-height .ol-aside rides the DOCUMENT scroll (nav + center
   self-scroll at height:100vh), so expanding the TEAM roster grows the page
   past the viewport and pops a document scrollbar — which shifts the whole
   window one position left (barath). Give the document scroll the feed's
   (.ol-center / OLP-465) scrollbar-less treatment: scroll still works, but no
   visible track/thumb and ZERO reserved gutter, so the layout never shifts. */
html{overflow-y:auto;scrollbar-width:none;-ms-overflow-style:none}
html::-webkit-scrollbar{display:none;width:0;height:0}
/* 2026-05-26 (@barath): font-smoothing antialiased->subpixel-antialiased.
   The forced grayscale 'antialiased' renders strokes thin/light, which on
   white reads as low-contrast "dull/washed". subpixel-antialiased restores
   macOS native stem-darkening (heavier, higher-contrast) -- the crisp look
   verified live in Dia/Chromium (the property still toggles stroke weight
   even though LCD subpixel itself was retired on macOS Mojave+). */
body{font-family:var(--f-body);font-size:15px;line-height:1.5;color:var(--c-primary);background:var(--c-bg);min-height:100vh;-webkit-font-smoothing:subpixel-antialiased}
.ol-scroll,.ol-center,.ol-aside{scrollbar-width:thin;scrollbar-color:transparent transparent}
.ol-scroll.scrolling,.ol-center.scrolling,.ol-aside.scrolling{scrollbar-color:rgba(0,0,0,0.12) transparent}
.ol-scroll::-webkit-scrollbar,.ol-center::-webkit-scrollbar,.ol-aside::-webkit-scrollbar{width:4px;height:4px;background:transparent}
.ol-scroll::-webkit-scrollbar-track,.ol-center::-webkit-scrollbar-track,.ol-aside::-webkit-scrollbar-track{background:transparent}
.ol-scroll::-webkit-scrollbar-thumb,.ol-center::-webkit-scrollbar-thumb,.ol-aside::-webkit-scrollbar-thumb{background:transparent;border-radius:2px}
.ol-scroll.scrolling::-webkit-scrollbar-thumb,.ol-center.scrolling::-webkit-scrollbar-thumb,.ol-aside.scrolling::-webkit-scrollbar-thumb{background:rgba(0,0,0,0.12)}
.ol-shell{display:flex;justify-content:center;min-height:100vh;width:100%;position:relative}
.ol-shell.has-artifact{justify-content:flex-start}
.ol-nav{width:272px;flex-shrink:0;position:sticky;top:0;height:100vh;display:flex;flex-direction:column;padding:12px 0;border-right:1px solid var(--c-border);overflow:hidden;background:var(--c-nav-bg)}
/* 2026-04-22: center pins with sticky so that the aside (now auto-height)
   can push body-scroll without taking the feed column with it. */
.ol-center{width:600px;flex-shrink:0;border-right:var(--hairline);position:sticky;top:0;align-self:flex-start;height:100vh;overflow-y:auto;background:var(--c-feed-bg)}
/* OLP-465 (@barath): the FEED scroll is scrollbar-less, Twitter-style — scroll
   works, but no visible track/thumb, even mid-scroll. Overrides the scroll-reveal
   default (above) for the center column ONLY; .ol-aside + .ol-scroll keep reveal.
   Source-order wins over the grouped rule at equal specificity. The deliberate
   in-post .ol-table-wrap scrollbar (feed.css) is a child element with its own
   ::-webkit-scrollbar and is unaffected. */
.ol-center,.ol-center.scrolling{scrollbar-width:none;-ms-overflow-style:none}
.ol-center::-webkit-scrollbar,.ol-center.scrolling::-webkit-scrollbar{display:none;width:0;height:0}
/* 2026-04-22 @barath fast-fix: drop height:100vh + overflow:auto so the
   aside scrolls with the page instead of clipping at viewport height.
   Scroll container is .ol-center (the center column); the aside now rides
   with the page, and short asides still get the sticky-top pin. */
.ol-aside{flex:1;min-width:280px;max-width:400px;flex-shrink:0;padding:12px 16px;display:flex;flex-direction:column}
/* OLP-221 #1: widen the aside on large viewports so the MEMORY wall rows
   (verb chip + body excerpt + author + relative time) stop wrapping. The
   1280px breakpoint matches the natural width where 600px center column
   + 640px aside fits comfortably alongside the 68px nav rail. Mobile
   <1024px hide rule below still wins (the @media further down). */
@media(min-width:1280px){
  .ol-aside{max-width:640px}
}
/* OLP-222: artifact-open layout flipped. Sidebar is HIDDEN by default
   when an artifact is open -- it does NOT live alongside the viewer.
   The header drawer-pull button summons the sidebar as a slide-in drawer
   over the artifact viewer. Click outside, Esc, or the drawer-pull
   again dismisses.
   Replaces the OLP-192 translucent-overlay pattern (visible-by-default
   alongside viewer) which conflicted with the artifact reading flow.

   Drawer chrome (post-tweak): the wrapper is TRANSPARENT (no bg, no
   border, no shadow) so the cards read as floating chips against the
   artifact's warm canvas -- notification-tray UX, not a slab panel.
   Each card keeps its own border + radius and gains a soft drop shadow
   in drawer mode so they lift off the canvas without a wrapper veil. */
.ol-shell.has-artifact .ol-aside{display:none}
.ol-shell.has-artifact.drawer-open .ol-aside{
  display:flex;
  position:fixed;top:48px;right:0;bottom:0;
  width:380px;max-width:calc(100vw - 32px);
  z-index:38;
  padding:12px 16px;
  background:transparent;
  border-left:0;
  box-shadow:none;
  animation:olDrawerSlideIn .22s cubic-bezier(.2,.8,.2,1);
  pointer-events:none;
}
/* Re-enable pointer events on the cards themselves (the wrapper passes
   through clicks so the user can still interact with the artifact
   underneath in the gaps between cards). */
.ol-shell.has-artifact.drawer-open .ol-aside .ol-sidebar-card,
.ol-shell.has-artifact.drawer-open .ol-aside .ol-sidebar-footer{
  pointer-events:auto;
}
.ol-shell.has-artifact.drawer-open .ol-aside .ol-sidebar-card{
  box-shadow:0 4px 20px rgba(15,20,25,0.10),0 1px 3px rgba(15,20,25,0.06);
}
/* OLP-222 tweak: dim the artifact viewer behind the drawer so the
   floating cards have visual lift. The dimmer is a pseudo-element on
   the shell -- inset top:48px keeps the header bright + clickable, the
   z-index sits between the viewer (default 0) and the drawer (38).
   Click on the dimmer falls through to .ol-shell, which the existing
   outside-click handler treats as "outside .ol-aside" -> close drawer. */
.ol-shell.has-artifact.drawer-open::before{
  content:"";
  position:fixed;
  top:48px;left:0;right:0;bottom:0;
  background:rgba(15,20,25,0.28);
  z-index:36;
  cursor:pointer;
  animation:olDrawerDimIn .22s ease-out;
}
@keyframes olDrawerSlideIn{from{transform:translateX(20px);opacity:0}to{transform:translateX(0);opacity:1}}
@keyframes olDrawerDimIn{from{opacity:0}to{opacity:1}}
@media(max-width:768px){.ol-shell.has-artifact.drawer-open .ol-aside{width:100vw;max-width:100vw}}
/* 2026-05-16 (@barath): center column keeps its right border when the
   portal is open. Was previously stripped by OLP-222 to make the
   artifact feel edge-to-edge against the feed, but with the new
   dot-grid portal canvas (r2 above) the feed column needs its own
   visible boundary so the two surfaces read as distinct workspaces.
   The default .ol-center border-right rule on line 1581 stays applied
   in the has-artifact state. */
/* Viewer is edge-to-edge in its column.
   OLP-portal-material: portal canvas with a 24px dot grid. Dot grid
   classifies the right panel as a distinct portal workspace, echoing
   the macOS sheet/HUD material vocabulary. Tuned over four passes:
     r1 (2.14.5): base #f3f2ef, dots rgba(15,17,21,0.085), 22px spacing,
       top-wash gradient. Read as too dark next to the white shell.
     r2 (2.14.6): base #fafaf7 (near-white), 24px spacing, top-wash
       removed, dot opacity bumped to 0.16 so the grid survives the
       lighter base.
     r3 (2.14.7): dot grid lifted off the .ol-artifact box and onto a
       .ol-artifact::before pseudo with inset:20px, so the pattern is
       padded in from all four sides instead of running flush against
       the title bar, right border, and scrollbar. Adds inset shadow
       on the LEFT edge only -- the portal reads as recessed beneath
       the feed, matching the depth metaphor in apps like Zoho Mail
       (which paints the shadow on the list side); we mirror it onto
       the portal side so the feed stays perfectly flat. Negative
       spread (-12px) confines the shadow to a thin band on the left
       and prevents it from leaking around the top/bottom corners.
     r4 (2.15.1, @barath 2026-05-16): base flipped from #fafaf7 to
       #f7f5ef (--c-portal-bg, parity with customize --c-paper) so
       the portal reads as the same design language as /customize.
       Dot grid + inset shadow unchanged.
     r5 (this, @barath 2026-05-16 post-UAT): cream was too
       low-contrast for dense text — flip to pure #ffffff. Drop the
       inset left-edge shadow (decoration the panel doesn't need).
       Dots stay (24px spacing, 0.16 opacity) — they survive on
       white at the same opacity. Net portal = pure white + dots,
       no chrome. */
.ol-artifact{display:none;flex:1;min-width:0;height:100vh;position:sticky;top:0;overflow:hidden;background-color:var(--c-portal-bg)}
.ol-artifact::before{content:"";position:absolute;inset:20px;background-image:radial-gradient(circle at 1px 1px,rgba(15,17,21,0.01) 1px,transparent 0);background-size:24px 24px;background-position:0 0;pointer-events:none;z-index:0}
.ol-shell.has-artifact .ol-artifact{display:flex;flex-direction:column}
/* Retired: .ol-artifact-tab + .ol-aside-dismiss + .aside-dismissed.
   The header's drawer-pull button is the single ingress now; the
   right-edge tab + corner X used to be necessary because the sidebar
   was always visible in has-artifact. With drawer-as-summon there's
   nothing to dismiss inline -- header button toggles. */
.ol-artifact-tab,.ol-aside-dismiss{display:none !important}
.ol-shell--centered .ol-nav{display:none}
.ol-shell--centered .ol-aside{display:none}
.ol-shell--centered .ol-center{width:100%;max-width:480px;border:none;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;padding:40px 24px;text-align:center}
@media(max-width:520px){.ol-shell--centered .ol-center{padding:32px 16px}}
/* OLP-118 follow-up (v3, per barath):
   Channel page = nav (fixed) + 600px center + right aside.
   Dashboard page = nav (fixed) + ONE page area matching "center + aside"
   combined (~1000px), centered in viewport like the channel layout. Nav
   stays the same fixed column across both. The center + aside columns fuse
   into a single flex child so tables get ~1000px instead of 600px, without
   edge-to-edge stretch. */
.ol-shell[data-layout="wide"] .ol-aside{display:none}
.ol-shell[data-layout="wide"] .ol-center{width:auto;max-width:1000px;flex:1;min-width:0;border-right:none}
@media(max-width:1024px){.ol-aside{display:none}}
@media(max-width:768px){.ol-nav{width:68px;align-items:center}.ol-nav-label,.ol-nav-ch-label,.ol-nav-channels,.ol-nav-wordmark{display:none !important}.ol-nav-item{width:42px;height:42px;padding:0;margin:6px 0;justify-content:center;gap:0}.ol-nav-logo{margin:0 0 12px;padding:0;gap:0;justify-content:center}.ol-nav-footer{padding:8px}.ol-nav-badge{position:absolute;top:2px;right:2px}/* OLP-440: collapsed 68px rail — the channel list is hidden, so its search is too; the settings row (now .ol-nav-channel-row, not .ol-nav-item) needs the same 42x42 centered icon treatment the old gear had. */.ol-nav-search{display:none}.ol-nav-ch-settings{width:42px;height:42px;padding:0;margin:6px 0;justify-content:center;gap:0}}
@media(max-width:500px){.ol-nav{display:none}.ol-center{width:100%;border:none}}
/* OLP-122: logo is a clickable link back to /office (return path). */
/* Flat single-column rail (2026-05-14). Every row aligns its icon to a
   fixed 22px slot and its label to a single vertical line. Padding +
   margin are shared across .ol-nav-logo, .ol-nav-item, .ol-nav-newch,
   .ol-nav-channel-row so the macOS Notes/Mail look holds. */
.ol-nav-logo{display:flex;align-items:center;gap:10px;padding:8px 14px;margin:1px 8px 8px;text-decoration:none;color:inherit}
.ol-nav-logo:hover{opacity:0.85}
/* R3 (2026-05-17): brand mark renders as inline SVG, edge-to-edge in
   the 22px slot, painted in currentColor (--c-primary). No plate, no
   border-radius, no padding chrome — the mark sits naked on the
   surface, peer to the rail glyphs while still being the heaviest
   element on the row. spike: figma/logo-spike-r3.html. */
.ol-nav-logo .ol-logo-slot{width:22px;height:22px;flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;color:var(--c-primary)}
.ol-nav-logo .ol-logo-slot svg{width:22px;height:22px;display:block}
.ol-nav-wordmark{font-size:16px;font-weight:700;color:var(--c-primary);letter-spacing:-0.3px;line-height:1}
.ol-nav-item{display:flex;align-items:center;gap:10px;padding:8px 14px;margin:1px 8px;border-radius:var(--r-sm);color:var(--c-secondary);text-decoration:none;transition:background .15s,color .15s;font-size:15px;font-weight:500;position:relative}
.ol-nav-item:hover{background:var(--c-bg-hover);color:var(--c-primary)}
.ol-nav-item.active{color:var(--c-primary);background:rgba(0,0,0,0.05);font-weight:600;border-radius:10px}
.ol-nav-item svg{width:22px;height:22px;flex-shrink:0}
.ol-nav-badge{margin-left:auto;min-width:20px;height:20px;border-radius:10px;background:var(--c-danger,#c0392b);color:#fff;font-size:11px;font-weight:700;display:flex;align-items:center;justify-content:center;padding:0 6px;line-height:1}
/* OLP-302: global header strip simplified to a single menu button.
   Previously housed refresh/close/drawer-pull (now in .ol-artifact-bar)
   + a notif badge (no notifications source today, removed). The lone
   menu button hides when the portal opens — Reorder is unavailable in
   portal mode and nothing competes with the artifact-bar chrome.
   Legacy .ol-header-btn--artifact rules retired; if someone re-adds
   that selector by mistake the elements simply won't render (no class
   on any button anymore). */
.ol-header{position:fixed;top:0;right:0;height:48px;display:flex;align-items:center;justify-content:flex-end;padding:0 16px;z-index:30;pointer-events:none;gap:4px}
.ol-header-btn{
  position:relative;display:none;align-items:center;justify-content:center;
  width:32px;height:32px;border-radius:10px;
  background:var(--c-bg,#fff);
  color:var(--c-secondary);
  border:1px solid var(--c-border);
  cursor:pointer;pointer-events:auto;
  text-decoration:none;
  transition:background .12s ease,color .12s ease,border-color .12s ease,transform .08s ease;
  padding:0;
}
.ol-header-btn:hover{background:var(--c-fill);color:var(--c-primary);border-color:var(--c-border-dark)}
.ol-header-btn:active{transform:scale(0.96)}
.ol-header-btn svg{width:16px;height:16px;stroke-width:1.75}
.ol-header-menu-btn{display:flex}
/* OLP-302: menu hides when artifact is open — no Reorder mid-portal,
   no chrome competing with the artifact-bar. Single rule, single
   source of truth via body.has-artifact (set/cleared by Artifact.open /
   Artifact.close). */
body.has-artifact .ol-header-menu-btn{display:none}
@media(max-width:768px){.ol-header{height:44px;padding:0 12px}.ol-header-btn{width:36px;height:36px}.ol-header-btn svg{width:18px;height:18px}}

/* Header menu popover (HeaderMenu IIFE). Floats below the menu button,
   right-aligned. Card-style chrome to match the new sidebar cards. */
.ol-header-menu-pop{
  position:fixed;display:none;
  width:280px;max-width:calc(100vw - 32px);
  background:var(--c-bg);
  border:1px solid var(--c-border);
  border-radius:var(--r-md);
  box-shadow:0 8px 28px rgba(0,0,0,0.10);
  padding:8px;z-index:40;
  font-family:var(--f-body);font-size:13px;
}
.ol-menu-section-hdr{display:flex;align-items:center;justify-content:space-between;padding:6px 8px;font-size:10.5px;font-weight:700;color:var(--c-tertiary);text-transform:uppercase;letter-spacing:0.14em}
.ol-menu-reset{background:transparent;border:0;cursor:pointer;font-size:11px;color:var(--c-tertiary);text-transform:none;letter-spacing:0;padding:2px 6px;border-radius:6px}
.ol-menu-reset:hover{background:var(--c-fill);color:var(--c-primary)}
.ol-menu-reorder-list{display:flex;flex-direction:column;gap:2px;padding:2px 4px}
.ol-menu-reorder-row{display:flex;align-items:center;gap:4px;padding:4px 6px;border-radius:var(--r-sm)}
.ol-menu-reorder-row:hover{background:var(--c-fill)}
.ol-menu-reorder-label{flex:1;color:var(--c-primary);font-size:13px}
.ol-menu-reorder-btn{background:transparent;border:0;cursor:pointer;width:24px;height:24px;border-radius:6px;display:inline-flex;align-items:center;justify-content:center;color:var(--c-tertiary);transition:background .12s,color .12s;padding:0}
.ol-menu-reorder-btn:hover:not([disabled]){background:var(--c-bg);color:var(--c-primary)}
.ol-menu-reorder-btn[disabled]{opacity:0.3;cursor:not-allowed}
.ol-menu-divider{height:1px;background:var(--c-border);margin:8px 4px}
.ol-menu-link{display:block;padding:8px 10px;color:var(--c-primary);text-decoration:none;font-size:13px;border-radius:var(--r-sm)}
.ol-menu-link:hover{background:var(--c-fill)}
.ol-menu-empty{padding:8px 12px;color:var(--c-tertiary);font-size:12px;font-style:italic}
/* OLP-222 tweak: artifact-actions section in the HeaderMenu popover.
   Shows only when an artifact is open (body.has-artifact). The action
   buttons share visual grammar with .ol-menu-link so the popover reads
   as one consistent surface, but get the icon + label two-row layout. */
.ol-menu-section--artifact,.ol-menu-divider--artifact{display:none}
body.has-artifact .ol-menu-section--artifact,body.has-artifact .ol-menu-divider--artifact{display:block}
.ol-menu-action{
  display:flex;align-items:center;gap:10px;
  width:100%;padding:8px 10px;
  background:transparent;border:0;cursor:pointer;
  font-family:inherit;font-size:13px;color:var(--c-primary);
  border-radius:var(--r-sm);text-align:left;
  transition:background .12s ease;
}
.ol-menu-action:hover{background:var(--c-fill)}
.ol-menu-action svg{width:14px;height:14px;color:var(--c-tertiary);flex-shrink:0}
.ol-menu-action:hover svg{color:var(--c-primary)}
.ol-nav-label{font-size:15px;white-space:nowrap;overflow:hidden}
.ol-nav-channels{flex:1;overflow:hidden;display:flex;flex-direction:column;margin-top:0;min-height:0}
/* OLP-282 hotfix r2: legacy .ol-nav-section-label rule retired -- it was
   leftover from a pre-OLP-276 design and over-styled the DM label span so
   "DM" rendered at 12px/0.5em-letter-spacing while CHANNELS rendered at
   11px/0.06em. The label span now inherits .ol-nav-section-header styling
   via the parent div, so DM and CHANNELS look identical. */
/* OLP-122: Channels header is a peer nav-item, not a tiny gray label.
   Includes the inline + button. */
.ol-nav-channels-header{justify-content:flex-start;gap:10px}
.ol-nav-channels-header .ol-nav-btn-new{margin-left:auto}
.ol-nav-btn-new{width:22px;height:22px;border-radius:4px;border:none;background:transparent;color:var(--c-tertiary);font-size:18px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s}
.ol-nav-btn-new:hover{background:var(--c-bg-hover);color:var(--c-primary)}
/* Channel list flush-left -- no nested indent. Share the same horizontal
   padding/margin as .ol-nav-item so channel labels land directly under
   Skills + Settings labels. */
/* OLP-440: the list fills the flex column between logo and footer. NO
   max-height magic number (the old calc(100vh - 360px) capped it ~300px
   short, hiding channels under a premature scroll while a void sat below).
   min-height:0 lets this flex child shrink below content size so overflow-y
   engages ONLY on true overflow. The .ol-nav-footer sibling (margin-top:auto)
   already reserves its own height via flex. */
/* OLP-460: scrollbar-gutter:stable reserves the scrollbar's track ALWAYS, so the
   channel rows keep one width whether or not the list is overflowing — they no
   longer shrink (and shift their right edge in) when the bar appears. Paired with
   the same reservation on .ol-nav-footer below, the list rows and the
   search/settings rows share an identical right edge (kills the "search/settings
   lean right" gap). On overlay-scrollbar platforms stable reserves 0 on both, so
   they stay aligned there too. */
.ol-nav-channel-list{flex:1;overflow-y:auto;scrollbar-gutter:stable;padding:0;min-height:0}
/* OLP-460: the rail-row primitive. ONE shell every rail entry composes —
   ingress (# New / + Add), channel rows, Workspaces, Search, Settings. Geometry
   is 100% token-driven (--rail-glyph-slot / --rail-row-pad-x / --rail-gap) so the
   leading glyph of every row lands on a single vertical axis (x=33: margin 8 +
   pad 14 + slot/2 11). Adding a new rail entry = give it .ol-nav-row + a
   .ol-nav-ch-kind sprite glyph and it cannot fall off the line. The four legacy
   row geometries (.ol-nav-newch, .ol-nav-channel-row, .ol-nav-search,
   .ol-nav-ch-settings) collapse into this — they keep only state/behaviour.
   Locked by TestRailSingleGlyphAxis. */
.ol-nav-row{display:flex;align-items:center;gap:var(--rail-gap);padding:8px 6px 8px var(--rail-row-pad-x);margin:1px 8px;width:calc(100% - 16px);box-sizing:border-box;border-radius:var(--r-sm);text-decoration:none;color:var(--c-secondary);font-size:15px;font-family:inherit;text-align:left;background:transparent;border:none;white-space:nowrap;overflow:hidden;cursor:pointer;transition:background .15s,color .15s}
.ol-nav-row:hover{background:var(--c-bg-hover);color:var(--c-primary)}
/* .ol-nav-channel-row keeps ONLY state + the kind-glyph color cascade below;
   all geometry now flows from .ol-nav-row (markup carries both classes). */
.ol-nav-channel-row.active{background:rgba(0,0,0,0.05);color:var(--c-primary);font-weight:600;border-radius:10px}
.ol-nav-channel-row.unread{color:var(--c-primary);font-weight:600}
.ol-nav-channel-row .ol-nav-ch-label{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}
/* Reserved 22px leading slot. Hosts a 6px presence dot when the channel
   has live presence (present_count > 0); empty otherwise so the label
   still aligns with icon-bearing rows. */
.ol-nav-ch-pulse{width:22px;height:22px;flex-shrink:0;display:inline-flex;align-items:center;justify-content:center}
.ol-nav-ch-pulse::before{content:"";display:block;width:0;height:0;border-radius:50%;background:transparent;transition:width .15s,height .15s}
.ol-nav-channel-row.working .ol-nav-ch-pulse::before{width:6px;height:6px;background:var(--c-accent,#1d9bf0)}
/* V3 rail kind glyph — # for team channels, @ for agent DMs,
   office briefcase for the home row. 22px slot matches the legacy
   .ol-nav-ch-pulse alignment so labels stay on the same vertical
   line. The inner SVG sizes via --rail-glyph-size (root token) so
   one knob re-flows every kind glyph across the rail. Color inherits
   via currentColor — secondary by default, primary on the active
   row, accent when row.working. */
.ol-nav-ch-kind{width:var(--rail-glyph-slot,22px);height:var(--rail-glyph-slot,22px);flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;color:var(--c-tertiary)}
.ol-nav-ch-kind svg{width:var(--rail-glyph-size,14px);height:var(--rail-glyph-size,14px);display:block}
.ol-nav-ch-kind use{display:block}
.ol-nav-channel-row .ol-nav-ch-kind{color:var(--c-tertiary)}
.ol-nav-channel-row:hover .ol-nav-ch-kind,.ol-nav-channel-row.active .ol-nav-ch-kind{color:var(--c-primary)}
.ol-nav-channel-row.unread .ol-nav-ch-kind{color:var(--c-primary)}
.ol-nav-channel-row.working .ol-nav-ch-kind{color:var(--c-accent,#1d9bf0)}
.ol-nav-channel-empty{padding:8px 16px;font-size:13px;color:var(--c-tertiary);font-style:italic}
/* OLP-283 (2026-05-15): the OLP-276/OLP-282 section-header + collapse
   CSS rules (.ol-nav-section-header, .ol-nav-section-dm-hdr, chevron,
   count, pill, label, collapsed-section selectors) retired entirely.
   Flat rail in OLP-283 means no section headers and no collapse
   mechanic, so the CSS for them is dead. setActive + active-class
   styling already live on .ol-nav-channel-row below. */
/* DM rows visually align with team rows -- no extra chrome today. The
   .ol-nav-ch-dm class is a hook for future per-section tuning (avatar
   chips, read-receipt markers) without re-targeting .ol-nav-channel-row. */
.ol-nav-ch-dm{}
/* OLP-439: pinned rows recolor the # KIND GLYPH gold (--c-pinned). Source
   order is LOAD-BEARING: this rule sits AFTER the hover/active/unread/working
   glyph rules (gold beats ink/blue/gray) but BEFORE the shared rule below
   (red wins over gold). All are equal (0,3,0) specificity, so source order
   resolves the decided cascade: shared > pinned > working > unread > gray.
   Label text stays untinted — glyph only (mirrors the shared rule). The
   OLP-284 trailing push-pin glyph + .ol-nav-ch-pinmark CSS are retired; the
   .ol-nav-ch-pinned class is now purely this color hook. */
.ol-nav-channel-row.ol-nav-ch-pinned .ol-nav-ch-kind{color:var(--c-pinned)}
/* OLP-284: owner-shared rows recolor the # KIND GLYPH mild-red — the HIGHEST
   priority glyph state. This rule sits LAST among the equal (0,3,0) glyph
   color rules so shared red wins by source order across pinned/working/unread.
   The label text keeps its normal color. Unshare flips .ol-nav-ch-shared off
   and the glyph reverts to its default color. */
.ol-nav-channel-row.ol-nav-ch-shared .ol-nav-ch-kind{color:var(--c-shared)}
/* +New ingress: lives at the top of the channel list, same row grammar as
   the channel rows + icon-bearing rows. "+" sits in the shared 22px icon
   slot so its label aligns with the channel labels and Skills/Settings. */
/* OLP-460: # New / + Add ingress are now plain .ol-nav-row rows — geometry from
   the primitive. The old 15px typed-hash slot + plus slot are DELETED; both rows
   carry a .ol-nav-ch-kind sprite glyph on the rail axis. The .ol-nav-newch class
   and its -btn id survive only as JS hooks (start/submit/closest). */
.ol-nav-newch:hover .ol-nav-ch-kind{color:var(--c-primary)}
/* + Add (DM-subscribe) keeps its plus glyph per design — it opens a modal, a
   different interaction than "# New" in-place — but now in the shared 22px
   .ol-nav-ch-kind slot, so it sits on the rail axis like every other glyph. */
/* OLP-460: shared body cell for the ingress label + search input. flex:1 after
   the glyph slot, so labels start on one x just like .ol-nav-ch-label. */
.ol-nav-row-body{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}
/* OLP-447 + OLP-460 (spikes 8FbEvVYCCaYDhJXb / 9VCxODk5WBJdA3W_): New-channel
   inline field. The # is the .ol-nav-ch-kind sprite (i-hash) in the 22px slot at
   pad-left 14 (var --rail-row-pad-x) — the same axis as the "# New" row and every
   channel, so it never relocates on activate. NO border/ring: a CONTAINED lift
   shadow is the only focus cue (OLP-460 recut from the 22px-blur cloud that bled
   past both rail seams onto the rows below). No divider, counter, or helper line.
   Invalid = a quiet faint-red shadow + unlit tick, no rule text. */
.ol-nav-newch-field{display:flex;align-items:center;gap:var(--rail-gap);margin:1px 8px;padding:6px 6px 6px var(--rail-row-pad-x);border-radius:10px;background:var(--c-bg);box-sizing:border-box;box-shadow:0 2px 8px rgba(15,20,25,0.10),0 1px 2px rgba(15,20,25,0.06)}
.ol-nav-newch-field.bad{box-shadow:0 2px 8px rgba(244,33,46,0.16),0 0 0 1px rgba(244,33,46,0.30)}
/* OLP-466: rail new-channel input matches the search input (14px) and sits
   one size below the channel-name rows (.ol-nav-row, 15px) — the rail's
   one-size-up hierarchy. Was 13.5px, which read optically smaller than both. */
.ol-nav-newch-input{flex:1;min-width:0;border:0;outline:none;background:transparent;font:14px var(--f-body);color:var(--c-primary);padding:0}
.ol-nav-newch-input::placeholder{color:var(--c-tertiary)}
.ol-nav-newch-tick{width:28px;height:28px;border-radius:8px;flex-shrink:0;border:0;cursor:pointer;display:flex;align-items:center;justify-content:center;background:var(--c-fill);color:var(--c-tertiary);transition:background .12s,color .12s,transform .1s}
.ol-nav-newch-tick svg{width:14px;height:14px}
.ol-nav-newch-tick.ready{background:var(--c-primary);color:var(--c-bg)}
.ol-nav-newch-tick.ready:hover{opacity:.9}
.ol-nav-newch-tick:active{transform:scale(0.9)}
.ol-nav-newch-tick:disabled{cursor:default}
.ol-nav-newch-tick.load{background:transparent;border:2px solid var(--c-border);border-top-color:var(--c-accent);border-radius:50%;color:transparent;animation:ol-modal-resolve-spin .6s linear infinite}
@media(prefers-reduced-motion:reduce){.ol-nav-newch-tick.load{animation:none;border-top-color:var(--c-border)}}
.ol-nav-ch{display:flex;align-items:center;gap:6px;padding:6px 16px;margin:1px 8px;border-radius:var(--r-sm);text-decoration:none;color:var(--c-secondary);font-size:14px;transition:background .15s,color .15s;cursor:pointer;white-space:nowrap;overflow:hidden}
.ol-nav-ch:hover{background:var(--c-bg-hover);color:var(--c-primary)}
.ol-nav-ch.active{background:rgba(0,0,0,0.05);color:var(--c-primary);font-weight:600;border-radius:10px}
.ol-nav-ch.unread{color:var(--c-primary);font-weight:600}
.ol-nav-ch-name{flex:1;overflow:hidden;text-overflow:ellipsis}
.ol-nav-ch-pill{margin-left:auto;min-width:18px;height:18px;border-radius:9px;background:var(--c-accent);color:#fff;font-size:10px;font-weight:700;display:flex;align-items:center;justify-content:center;padding:0 5px;flex-shrink:0}
.ol-nav-ch-pill.mention{background:var(--c-danger)}
/* OLP-440: horizontal padding 0 (was 8px) so the footer rows' own
   margin:1px 8px alone sets their left edge — matching the channel list
   (padding:0). Without this the settings/search glyphs landed 8px right of
   the # column (left:70 vs 62). Vertical 8px kept; mobile rule unaffected. */
/* OLP-460: the footer is a scroll container (overflow-y:auto) with the SAME thin
   scrollbar styling as the list (.ol-scroll class in markup) + scrollbar-gutter:
   stable. With no overflow content no bar ever shows, but the reserved gutter
   matches the list's exactly — so the footer rows' right edge lands on the list
   rows' right edge (verified 252==252). Without scrollbar-width:thin the footer
   reserved the classic ~15px gutter and sat 4px inside the list rows. */
.ol-nav-footer{flex-shrink:0;padding:8px 0;margin-top:auto;overflow-y:auto;scrollbar-gutter:stable}
/* OLP-440: the settings row now uses .ol-nav-channel-row grammar (22px
   .ol-nav-ch-kind slot + 14px glyph + 14px left pad + 10px gap) so the gear
   lines up under the # column at rail weight. Resting color (secondary) +
   hover/active states are inherited from .ol-nav-channel-row — this hook
   exists for any future footer-row-only tweak. */
.ol-nav-ch-settings{}
/* OLP-571 ③e: calm guest rail bottom cluster. Both rows ride the shared
   .ol-nav-row grammar (22px glyph slot + 14px glyph + 14px pad + 10px gap) so
   they align under the single channel row above. rail-register is the quiet
   upgrade ingress (accent, weight 600); rail-identity is the read-only guest
   name slot (cursor:default, no hover-fill) with an amber guest badge. */
.rail-register{color:var(--c-accent);font-weight:600}
.rail-register .ol-nav-ch-kind{color:var(--c-accent)}
.rail-register:hover{background:var(--c-accent-light);color:var(--c-accent)}
.rail-identity{cursor:default}
.rail-identity:hover{background:transparent;color:var(--c-secondary)}
.rail-identity .ol-nav-ch-kind{color:var(--c-secondary)}
.rail-identity .ol-nav-ch-label{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;color:var(--c-primary);font-weight:600;margin-right:7px}
/* OLP-440: channel-list search row. Rail grammar — leading .ol-nav-ch-kind
   slot (i-search) + flush input. Lives in the footer above the settings row,
   fixed (does not scroll with the list). */
/* OLP-460: search row is a .ol-nav-row now (geometry from the primitive). It is
   an input row, not a clickable nav row, so suppress the row hover-fill and use a
   text cursor; the leading i-search glyph rides the shared .ol-nav-ch-kind slot. */
.ol-nav-search{cursor:text}
.ol-nav-search:hover{background:transparent}
.ol-nav-search .ol-nav-ch-kind{color:var(--c-tertiary)}
/* font-size matches .ol-nav-row (15px) so the input + its placeholder sit on the
   same type scale as every channel label — the 14px here read as a different font. */
.ol-nav-search-input{flex:1;min-width:0;border:none;background:transparent;color:var(--c-primary);font-family:inherit;font-size:15px;padding:2px 0;outline:none}
.ol-nav-search-input::placeholder{color:var(--c-tertiary)}
/* Clear (×) button — appears only when the search has text; restores the full
   list in one click instead of backspacing every character. Tertiary at rest,
   inks on hover; rides the row's flex end. */
.ol-nav-search-clear{flex:0 0 auto;display:flex;align-items:center;justify-content:center;width:18px;height:18px;padding:0;margin:0;border:none;background:transparent;color:var(--c-tertiary);cursor:pointer;border-radius:var(--r-sm);transition:color .15s,background .15s}
.ol-nav-search-clear:hover{color:var(--c-primary);background:var(--c-bg)}
.ol-nav-search-clear svg{width:14px;height:14px;display:block}
/* OLP-440: non-destructive filter overlay. ChannelSearch toggles this class on
   .ol-nav-channel-row to hide non-matches — it NEVER rewrites
   #nav-channel-list.innerHTML (single-writer contract, TestOLP282). */
.ol-nav-channel-row.ol-nav-hidden{display:none}
/* OLP-440: no-match empty-state line (sibling of the list, owned by
   ChannelSearch — not inside #nav-channel-list). */
.ol-nav-search-empty{padding:8px 16px;font-size:13px;color:var(--c-tertiary);font-style:italic}
.ol-section{padding:var(--sp-lg);border-bottom:1px solid var(--c-border)}
.ol-section h2{font-size:16px;font-weight:700;margin-bottom:var(--sp-md)}
.ol-section-header{padding:var(--sp-lg);border-bottom:1px solid var(--c-border)}
.ol-section-header h1{font-size:20px;font-weight:700}
.ol-section-header p{font-size:14px;color:var(--c-secondary);margin-top:2px}
.ol-stat-row{display:flex;gap:var(--sp-lg)}
.ol-stat-card{flex:1;background:var(--c-bg-secondary);border-radius:var(--r-md);padding:var(--sp-lg)}
.ol-stat-card .ol-stat-value{font-size:28px;font-weight:700}
.ol-stat-card .ol-stat-label{font-size:13px;color:var(--c-secondary);margin-top:2px}
.ol-info-grid{display:grid;grid-template-columns:100px 1fr;gap:var(--sp-sm) var(--sp-lg);font-size:14px}
.ol-info-label{color:var(--c-secondary);font-weight:500}
.ol-info-value code{font-family:var(--f-mono);font-size:13px;background:var(--c-fill);padding:1px 5px;border-radius:var(--r-sm)}
.ol-agent-row{display:flex;align-items:center;gap:var(--sp-md);padding:10px 0;border-bottom:1px solid var(--c-border)}
.ol-agent-row .ol-agent-info{flex:1;min-width:0}
.ol-agent-row .ol-agent-name{font-size:14px;font-weight:600}
.ol-agent-row .ol-agent-meta{font-size:12px;color:var(--c-tertiary)}
.ol-channel-row{display:flex;align-items:center;gap:var(--sp-md);padding:14px var(--sp-lg);border-bottom:1px solid var(--c-border);cursor:pointer;text-decoration:none;color:inherit;transition:background .15s}
.ol-channel-row:hover{background:var(--c-bg-hover)}
.ol-channel-row .ol-channel-info{flex:1;min-width:0}
.ol-channel-row .ol-channel-name{font-size:15px;font-weight:700}
.ol-channel-row .ol-channel-topic{font-size:13px;color:var(--c-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ol-channel-row .ol-channel-stats{text-align:right;flex-shrink:0}
.ol-channel-row .ol-channel-count{font-size:13px;color:var(--c-secondary)}
.ol-channel-row .ol-channel-time{font-size:12px;color:var(--c-tertiary)}
.ol-form-row{display:flex;gap:var(--sp-sm);margin-top:var(--sp-md)}
.ol-input{height:36px;padding:0 var(--sp-md);border:1px solid var(--c-border-dark);border-radius:var(--r-sm);font-size:14px;font-family:var(--f-body);outline:none}
.ol-input:focus{border-color:var(--c-accent)}
.ol-btn{height:36px;padding:0 var(--sp-lg);border-radius:var(--r-pill);border:none;font-size:14px;font-weight:600;cursor:pointer;font-family:var(--f-body)}
.ol-btn--primary{background:var(--c-accent);color:#fff}
.ol-btn--primary:hover{background:var(--c-accent-hover)}
.ol-btn--danger{background:transparent;border:1px solid var(--c-danger);color:var(--c-danger)}
.ol-btn--danger:hover{background:var(--c-danger-light)}
.ol-code-block{background:var(--c-bg-secondary,#f5f5f5);border:1px solid var(--c-border);border-radius:var(--r-sm);padding:10px 14px;font-family:var(--f-mono);font-size:13px;overflow-x:auto;white-space:pre;margin:0;line-height:1.5}
.ol-empty-state{text-align:center;color:var(--c-tertiary);padding:40px var(--sp-lg);font-size:15px}
.ol-settings-tabs{display:flex;gap:0;border-bottom:1px solid var(--c-border);margin:0 0 var(--sp-lg) 0}
.ol-settings-tab{background:none;border:none;padding:var(--sp-sm) var(--sp-lg);font-size:14px;font-weight:600;color:var(--c-tertiary);cursor:pointer;border-bottom:2px solid transparent;font-family:var(--f-body);transition:color .15s}
.ol-settings-tab:hover{color:var(--c-primary)}
.ol-settings-tab.active{color:var(--c-primary);border-bottom-color:var(--c-accent)}
.ol-settings-content{display:none}
.ol-settings-content.active{display:block}
.ol-agent-name-status{font-size:12px;margin-top:4px;min-height:18px}
.ol-agent-name-status.available{color:#16a34a}
.ol-agent-name-status.taken{color:var(--c-danger)}
.ol-agent-name-status.checking{color:var(--c-tertiary)}
.ol-agent-name-status.invalid{color:var(--c-danger)}
/* OLP-275: inline status pill rendered next to the Save button on the
   Profile avatar form (/settings + /agents/{handle}). The same class is
   reused on both surfaces so the visual treatment stays consistent. */
.ol-profile-status{font-size:13px;font-weight:500;color:var(--c-secondary);min-height:18px;transition:color .15s}
.ol-profile-status.pending{color:var(--c-tertiary)}
.ol-profile-status.success{color:var(--c-success)}
.ol-profile-status.error{color:var(--c-danger)}
/* Color well rules live in customizeAppCSS (customize_shell.go) — every
   route that renders the swatch is under /customize/* which uses that
   self-contained stylesheet, not shellCSS. */
.ol-agent-name-hint{margin-top:2px}
.ol-agent-role-pill{display:inline-block;padding:2px 10px;border-radius:var(--r-pill);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.5px}
.ol-create-agent-form .ol-form-row{gap:var(--sp-sm);align-items:flex-start}
.ol-avatar{display:flex;align-items:center;justify-content:center;font-weight:700;color:#fff;flex-shrink:0;border-radius:var(--r-pill)}
.ol-avatar--xs{width:22px;height:16px;font-size:8px}
.ol-avatar--sm{width:28px;height:18px;font-size:9px}
.ol-avatar--md{width:38px;height:24px;font-size:11px}
.ol-avatar--lg{width:48px;height:28px;font-size:13px}
.ol-avatar--pill{width:48px;height:28px;font-size:13px}
/* OLP-222: sidebar cards as discrete bordered containers. Replaces the
   OLP-176 hairline-stack with self-contained cards (1px border, rounded
   corners, internal padding). Each card carries a title row + body; the
   title row hosts the count chip, collapse toggle, and a stack-up/down
   handle (managed by window.SidebarStack).
   The cards live inside .ol-aside which arranges them with row-gap rather
   than border-top separators, so reorder via flex order keeps the
   spacing consistent regardless of position. */
.ol-aside{gap:10px}
.ol-sidebar-card{
  margin-bottom:0;
  padding:6px 0 8px;
  background:var(--c-bg);
  border:1px solid var(--c-border);
  border-radius:var(--r-md);
  overflow:hidden;
  display:flex;
  flex-direction:column;
}
.ol-sidebar-card[data-collapsed="1"] > *:not(.ol-sidebar-title){display:none !important}
.ol-sidebar-card[data-collapsed="1"]{padding-bottom:6px}
.ol-sidebar-card-inner{}
.ol-sidebar-title{
  display:flex;
  align-items:center;
  font-size:10.5px;
  font-weight:700;
  color:var(--c-tertiary);
  text-transform:uppercase;
  letter-spacing:0.14em;
  padding:8px 14px 6px;
  flex-shrink:0;
  font-feature-settings:'ss01';
  gap:6px;
  cursor:pointer;
  user-select:none;
}
/* OLP-445: the whole header is the disclosure (accordion) hit target via the
   delegated collapse handler — signal it so state + affordance align on first
   render. Hover brightens the label; the chevron has its own hover (below). */
.ol-sidebar-title:hover{color:var(--c-secondary)}
.ol-sidebar-count{font-family:var(--f-mono);font-size:10px;color:var(--c-tertiary);font-variant-numeric:tabular-nums;font-weight:400}
.ol-sidebar-count::before{content:"—";margin-right:6px;color:var(--c-border-dark);letter-spacing:0;font-family:var(--f-body)}
/* OLP-268-130 phase A · meta chip sits to the right of .ol-sidebar-count
   in the title flex row. Composed client-side by Sidebar.loadLaps as
   " · M in flight · K landed" so LAPS header surfaces cross-bucket
   counts inline. Mono + tertiary, mild letter-spacing reset so the
   composed text reads as prose, not chip-style. Empty string when
   wipLaps + doneLaps are both zero (CSS hides via :empty rule below). */
.ol-sidebar-meta{font-family:var(--f-mono);font-size:10px;color:var(--c-tertiary);font-variant-numeric:tabular-nums;font-weight:400;letter-spacing:0;text-transform:none}
.ol-sidebar-meta:empty{display:none}
/* Subhead — small italic line below the title. OLP-268-130 phase A uses
   it on LAPS to disclose the deployer-scope filter ("Scoped to your
   fleet") that the legacy 3-tray layout filtered silently. OLP-268-200
   adds a dotted underline + help cursor when a title= attribute is
   present, so the disclosure-on-hover affordance is discoverable without
   needing a second visual atom. The [title] attribute selector keeps the
   styling opt-in — subheads without a tooltip render flat. */
.ol-sidebar-subhead{font-size:11px;color:var(--c-tertiary);font-style:italic;font-weight:400;padding:0 14px 6px;letter-spacing:0;text-transform:none}
.ol-sidebar-subhead:empty{display:none}
.ol-sidebar-subhead[title]{cursor:help;text-decoration:underline dotted var(--c-stroke,rgba(15,20,25,0.2));text-decoration-thickness:1px;text-underline-offset:3px}
.ol-sidebar-subhead[title]:hover{color:var(--c-secondary);text-decoration-color:var(--c-secondary)}
/* OLP-268-140 · inline expansion DOM for wip-active laps inside the
   unified #laps-panel. Renders directly beneath the lap row in
   #laps-list (same anchor, no separate tray). Markup contract:
     .ol-lap-expansion > ul.ol-lap-crits > li.ol-lap-crit.--<status>
   Visual atoms intentionally minimal — render shape lands in a
   follow-up lap. This layer establishes the structural anchor only. */
.ol-lap-expansion{margin:2px 0 6px 28px;padding:4px 0 2px}
.ol-lap-crits{list-style:none;margin:0;padding:0}
.ol-lap-crit{display:flex;align-items:flex-start;gap:8px;padding:3px 4px;font-size:12.5px;line-height:1.4;color:var(--c-primary)}
.ol-lap-crit-state{width:12px;height:12px;flex-shrink:0;margin-top:3px;border-radius:50%;border:1.2px solid var(--c-border-dark);box-sizing:border-box}
.ol-lap-crit-text{flex:1;overflow:hidden;text-overflow:ellipsis}
.ol-lap-crit--done .ol-lap-crit-state{background:var(--c-tertiary);border-color:var(--c-tertiary)}
.ol-lap-crit--done .ol-lap-crit-text{color:var(--c-tertiary)}
.ol-lap-crit--wip .ol-lap-crit-state{background:var(--c-accent);border-color:var(--c-accent)}
.ol-lap-crit--wip .ol-lap-crit-text{color:var(--c-primary);font-weight:500}
.ol-lap-crit--blocked .ol-lap-crit-state{border-color:var(--c-warning,#f59e0b)}
.ol-lap-crit--dropped .ol-lap-crit-text{color:var(--c-quaternary,var(--c-tertiary));text-decoration:line-through}
.ol-lap-crit--next .ol-lap-crit-text{color:var(--c-secondary,var(--c-primary))}
/* OLP-268-170 · unified empty-state copy. Single message replaces the
   prior multi-tray split (no separate "All clear" interstitial vs
   "Nobody's pushing right now"). Quiet, small, no chrome. */
.ol-laps-empty{padding:12px 14px;font-size:12.5px;color:var(--c-tertiary);font-style:italic;text-align:center}
/* Title-row controls: collapse chevron + stack-reorder handle.
   Controls slide into the title row's flex container; .ol-card-toggle is
   rightmost. The drag-handle (#stack handle) sits next to it for
   keyboard-only fallback when the menu's reorder UI isn't in use. */
.ol-card-controls{margin-left:auto;display:flex;align-items:center;gap:2px}
.ol-card-toggle,.ol-card-handle{
  background:transparent;border:0;cursor:pointer;
  width:22px;height:22px;border-radius:6px;
  display:inline-flex;align-items:center;justify-content:center;
  color:var(--c-tertiary);transition:background .12s,color .12s,transform .15s;
  padding:0;
}
.ol-card-toggle:hover,.ol-card-handle:hover{background:var(--c-fill);color:var(--c-primary)}
.ol-card-toggle svg,.ol-card-handle svg{width:12px;height:12px}
.ol-sidebar-card[data-collapsed="1"] .ol-card-toggle{transform:rotate(-90deg)}
/* OLP-249: user-led pin add — + button on the LAPS + ARTIFACTS card
   headers. Lives inline in the .ol-sidebar-title flex row, pushed
   right via margin-left:auto. 20×20 to match the rest of the title
   chrome (toggle + handle are 22×22 from JS-injected controls).
   parchment fill, blue on hover (visible-but-quiet — channel-public
   action). Hidden via JS for non-write-eligible viewers (presence
   gate happens server-side at click time; this hides the affordance
   when the user clearly can't act on it). */
.ol-pin-add{
  background:transparent;border:0;
  width:22px;height:22px;border-radius:6px;
  display:inline-flex;align-items:center;justify-content:center;
  color:var(--c-tertiary);cursor:pointer;
  font-size:16px;line-height:1;font-weight:500;
  padding:0;
  transition:background .12s,color .12s,transform .15s;
}
.ol-pin-add:hover{background:var(--c-fill);color:var(--c-primary)}
.ol-pin-add:active{transform:scale(0.94)}
.ol-pin-add[hidden]{display:none !important}
.ol-sidebar-card[data-collapsed="1"] .ol-pin-add{display:none}
.ol-sidebar-item{padding:10px 16px;transition:background .15s;display:block;text-decoration:none;color:var(--c-primary)}
.ol-sidebar-item:hover{background:var(--c-bg-hover)}
/* OLP-222 hotfix: explicit order:9999 keeps the footer pinned to the
   bottom of the flex column even when SidebarStack assigns explicit
   order values to the cards above. Without this, the footer's default
   order:0 tied with the topmost card and the footer rendered mid-stack. */
.ol-sidebar-footer{padding:16px;font-size:12px;color:var(--c-tertiary);line-height:1.6;margin-top:auto;order:9999}
.ol-sidebar-footer a{color:var(--c-tertiary);text-decoration:none;margin-right:12px}
.ol-sidebar-footer a:hover{text-decoration:underline}
@keyframes shimmer{0%{background-position:-200px 0}100%{background-position:200px 0}}
.ol-skeleton{background:linear-gradient(90deg,var(--c-fill) 25%,var(--c-border) 50%,var(--c-fill) 75%);background-size:400px 100%;animation:shimmer 1.2s ease-in-out infinite;border-radius:var(--r-sm)}
.ol-skeleton-card{height:72px;margin:var(--sp-lg);border-radius:var(--r-md)}
.ol-skeleton-line{height:14px;margin:var(--sp-sm) var(--sp-lg);border-radius:var(--r-sm)}
.ol-skeleton-line:nth-child(2){width:60%}
.ol-skeleton-line:nth-child(3){width:80%}
@keyframes wr-pulse{0%,100%{opacity:0.12;transform:scale(0.97)}35%,50%{opacity:1;transform:scale(1)}}
.wr-spinner{display:inline-block;line-height:0;color:var(--c-primary)}
.wr-spinner svg{width:100%;height:100%}
.wr-ring{animation:wr-pulse 1.4s ease-in-out infinite;transform-origin:center}
.wr-r4{animation-delay:0s}
.wr-r3{animation-delay:.15s}
.wr-r2{animation-delay:.3s}
.wr-r1{animation-delay:.45s}
.wr-spinner-loading{display:flex;align-items:center;justify-content:center;padding:var(--sp-xl) 0;min-height:120px}
/* Inline neutral ring spinner (2026-05-17). Use for transient,
   frequent waits where the brand spinner would be too loud — feed
   pagination, artifact-portal open, channel switch. Inherits color
   via currentColor so it tints with surrounding text; default surface
   sets --c-tertiary. Spec: 24px viewBox, 1.8px stroke, 270deg arc
   rotating at 0.7s linear infinite. */
.ol-inline-spinner{display:inline-block;line-height:0;color:var(--c-tertiary);vertical-align:middle}
.ol-inline-spinner svg{display:block;width:100%;height:100%;animation:ol-inline-spinner-rot .7s linear infinite;transform-origin:center}
.ol-inline-spinner svg circle{fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-dasharray:42 18}
@keyframes ol-inline-spinner-rot{to{transform:rotate(360deg)}}
.ol-inline-spinner-loading{display:flex;align-items:center;justify-content:center;padding:var(--sp-lg) 0;min-height:64px}
.ol-error-banner{padding:var(--sp-sm) var(--sp-lg);font-size:14px;color:var(--c-danger);background:var(--c-danger-light);border-bottom:1px solid var(--c-border);display:none}
.ol-error-banner.visible{display:block}
.ol-sse-dot{width:6px;height:6px;border-radius:50%;display:none}
.ol-center{transition:opacity 80ms ease,border-color .2s}
.ol-center.ol-fade-out{opacity:0}
.ol-mobile-header{display:none;align-items:center;gap:var(--sp-sm);padding:var(--sp-md) var(--sp-lg);border-bottom:1px solid var(--c-border)}
.ol-mobile-header a{color:var(--c-primary);text-decoration:none;display:flex;align-items:center}
.ol-mobile-header .ol-mobile-title{font-size:16px;font-weight:700;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* OLP-191 follow-up (channel-header r3 V3): channel name is now 38px sans
 * and lives inside a clickable button. Status is HIDDEN by default; the
 * button toggles a status-panel below (aria-expanded + .ol-channel-chev
 * rotates 180deg). Empty status -> chevron suppressed via
 * .ol-channel-header[data-has-status="false"] .ol-channel-chev{opacity:0}. */
/* No own border-bottom: the header sits inside .ol-feed-dock, whose border-bottom
   is the single hairline separator. After OLP-607 moved the composer OUT of the
   dock, the header's bottom edge coincides with the dock's, so a border here
   stacked on the dock's into a 2px-looking line — dropped, dock owns the edge. */
.ol-channel-header{padding:var(--sp-md) 20px;flex-shrink:0}
/* OLP-611: flex row holding the title button (left); the right slot is left
   empty by the pager retirement (OLP-612 mounts the members cluster there).
   NB class is NOT ol-channel-header-row (negative test lock). */
.ol-channel-header-bar{display:flex;align-items:center;gap:10px}
.ol-channel-header-btn{display:inline-flex;align-items:center;gap:10px;background:transparent;border:0;padding:4px 8px;margin:-4px -8px;border-radius:6px;font:inherit;color:inherit;cursor:pointer;text-align:left;transition:background .12s ease;min-width:0}
.ol-channel-header-btn:hover{background:var(--c-bg-hover)}
.ol-channel-header-btn:focus-visible{outline:2px solid var(--c-accent);outline-offset:2px}
.ol-channel-header-name{font-size:32px;font-weight:700;letter-spacing:-.02em;margin:0;line-height:1.15;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
/* OLP-662 (jyo): read-only stage chip beside the 32px channel name. SOLID blue
   (var(--c-accent) fill + var(--c-card) text) — the masthead twin of the PLAN
   panel's translucent .ol-plan-stage-val, distinguished by being filled. Sized
   ~12px so it reads in proportion to the name, not as large. Display-only:
   no caret, no pointer affordance (the PLAN picker stays the writable surface). */
.ol-channel-stage-chip{display:inline-flex;align-items:center;align-self:center;flex:0 0 auto;font-size:12px;font-weight:600;line-height:1;letter-spacing:.01em;padding:4px 10px;border-radius:6px;background:var(--c-accent);color:var(--c-card,#fff);white-space:nowrap}
/* chevron sized + colored to match the pager arrows (16px glyph in a 20px box,
   secondary -> accent on hover) so the title row reads as one control line. */
.ol-channel-chev{display:inline-flex;align-items:center;justify-content:center;color:var(--c-secondary);transition:transform .18s ease,color .12s ease;flex-shrink:0;width:20px;height:20px}
.ol-channel-header-btn:hover .ol-channel-chev{color:var(--c-accent)}
.ol-channel-header-btn[aria-expanded="true"] .ol-channel-chev{transform:rotate(180deg);color:var(--c-accent)}
.ol-channel-header[data-has-status="false"] .ol-channel-chev{opacity:.35}
/* OLP-612: header member COUNT CHIP (variant A, barath 2026-06-13), mounted in
   the right slot the OLP-611 pager vacated. A single compact pill — "users"
   glyph + member count + chevron — REPLACES the v1 stacked overlapping pills
   (read "too big"). Quiet at rest (--c-bg-soft), accent border + rotated chevron
   when open. Tokens only, no hex. The popover below is unchanged. */
.ol-members{margin-left:auto;position:relative;flex-shrink:0}
.ol-members-cluster{display:inline-flex;align-items:center;gap:6px;height:28px;padding:0 10px;border-radius:9999px;background:var(--c-bg-soft);border:1px solid var(--c-border);cursor:pointer;font:inherit;color:var(--c-secondary);transition:background .12s ease,border-color .12s ease,color .12s ease}
.ol-members-cluster:hover{background:var(--c-bg-hover);border-color:var(--c-tertiary);color:var(--c-primary)}
.ol-members-cluster:focus-visible{outline:2px solid var(--c-accent);outline-offset:2px}
.ol-members-cluster[aria-expanded="true"]{border-color:var(--c-accent);color:var(--c-primary)}
/* "users" glyph: dims at rest on --c-secondary, accents with the chip on hover. */
.ol-members-icon{display:inline-flex;align-items:center;color:var(--c-secondary)}
.ol-members-cluster:hover .ol-members-icon{color:var(--c-primary)}
/* tabular count holds a steady width as the roster changes. */
.ol-members-count{font-size:13px;font-weight:600;font-variant-numeric:tabular-nums;letter-spacing:.01em}
.ol-members-chev{display:inline-flex;align-items:center;justify-content:center;color:var(--c-tertiary);transition:transform .18s ease,color .12s ease;width:13px;height:13px;flex-shrink:0}
.ol-members-cluster:hover .ol-members-chev{color:var(--c-accent)}
.ol-members-cluster[aria-expanded="true"] .ol-members-chev{transform:rotate(180deg);color:var(--c-accent)}
/* read-more popover: extends LEFTWARD (right edge tracks the trigger) so a long
   @handle never clips off the right. Left-aligned single-line @handle rows. */
.ol-members-panel{position:absolute;top:calc(100% + 8px);right:0;min-width:200px;max-width:280px;background:var(--c-bg);border:1px solid var(--c-border);border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,.14);padding:8px;z-index:1200}
.ol-members-panel[hidden]{display:none}
.ol-members-panel-head{font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--c-tertiary);padding:2px 8px 8px}
.ol-members-row{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:7px;text-align:left}
.ol-members-row:hover{background:var(--c-bg-hover)}
.ol-members-row-avatar{flex-shrink:0}
.ol-members-row-name{font-size:13px;color:var(--c-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
/* OLP-636: ephemeral share-link guests read distinct from durable members —
   muted pill + a small "guest" tag. Design-system tokens only. */
.ol-members-row--guest .ol-members-row-avatar{opacity:.6}
.ol-members-row--guest .ol-members-row-name{color:var(--c-secondary)}
.ol-members-row-tag{margin-left:auto;flex-shrink:0;font-size:10px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--c-tertiary);background:var(--c-bg-hover);border:1px solid var(--c-border);border-radius:5px;padding:1px 5px}
@media (prefers-reduced-motion:reduce){.ol-members-cluster,.ol-members-chev{transition:none}}
@media (max-width:700px){.ol-members-panel{max-width:240px}}
.ol-channel-header-topic{font-size:13px;color:var(--c-secondary);margin-top:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:none}
.ol-channel-header.expanded .ol-channel-header-topic{display:block;white-space:normal;text-overflow:unset}
/* OLP-456: Office coordinator presence banner (line only). The offline launch
   card under #office-coord-action is owned by OLP-457; this styles the banner
   line + the present/absent dot (::before keyed on data-state). */
.ol-office-coord{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-top:6px;font-size:13px;line-height:1.4;max-width:100%;min-width:0}
.ol-office-coord[hidden]{display:none}
.ol-office-coord-line{display:inline-flex;align-items:center;color:var(--c-secondary)}
.ol-office-coord-line::before{content:"";display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:7px;border:1.5px solid var(--c-tertiary);background:transparent;flex:0 0 auto}
.ol-office-coord[data-state="live"] .ol-office-coord-line{color:var(--c-primary)}
.ol-office-coord[data-state="live"] .ol-office-coord-line::before{background:var(--c-accent);border-color:var(--c-accent)}
.ol-office-coord[data-state="absent"] .ol-office-coord-line{color:var(--c-tertiary)}
/* OLP-191 status panel: hidden by default, revealed when header button is
 * clicked. Slides down via max-height transition. */
.ol-channel-status-panel{margin-top:10px;overflow:hidden;max-height:160px;opacity:1;transition:max-height .22s ease,opacity .18s ease,margin-top .18s ease}
.ol-channel-status-panel[hidden]{display:block;max-height:0;opacity:0;margin-top:0;visibility:hidden}
.ol-channel-status-row{position:relative}
.ol-channel-status-btn{background:transparent;border:none;padding:6px 10px;margin:0 -10px;border-radius:6px;font-size:14px;font-family:inherit;color:var(--c-secondary);cursor:pointer;display:inline-flex;align-items:baseline;gap:8px;max-width:100%;text-align:left;line-height:1.45}
.ol-channel-status-btn:hover{background:var(--c-bg-hover);color:var(--c-primary)}
.ol-channel-status-btn .ol-channel-status-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;color:var(--c-primary)}
.ol-channel-status-btn .ol-channel-status-text--empty{font-style:italic;color:var(--c-tertiary)}
.ol-channel-status-btn .ol-channel-status-when{font-size:12px;color:var(--c-tertiary);font-family:var(--f-mono);font-variant-numeric:tabular-nums;flex-shrink:0;white-space:nowrap}
.ol-channel-status-btn .ol-channel-status-when:not(:empty)::before{content:" · ";color:var(--c-tertiary);margin-right:2px}
.ol-channel-status-btn .ol-channel-status-edit-hint{font-size:11px;color:var(--c-tertiary);text-transform:uppercase;letter-spacing:0.5px;opacity:0;transition:opacity .15s;flex-shrink:0}
.ol-channel-status-btn:hover .ol-channel-status-edit-hint{opacity:1}
.ol-channel-status-input{display:block;width:100%;box-sizing:border-box;padding:4px 8px;font-size:14px;font-family:inherit;color:var(--c-primary);background:var(--c-bg);border:1px solid var(--c-accent);border-radius:4px;outline:none}
.ol-channel-status-counter{display:inline-block;margin-top:2px;font-size:11px;color:var(--c-tertiary);font-family:var(--f-mono);font-variant-numeric:tabular-nums}
.ol-channel-status-error{margin-top:2px;font-size:11px;color:var(--c-danger,#c0392b)}
/* OLP-308 — channel-header turn-pill caption.
   Inline indicator next to the # channel-name showing "@handle · in turn"
   (or "+N more" when multiple agents active). window.TurnPill in the
   per-page IIFE toggles the hidden attribute + sets .ol-channel-turn-text
   from the presence_list SSE event. Three-dot organic loader pulses
   beside the text. */
.ol-channel-turn{display:none;align-items:center;gap:6px;margin-left:10px;font-size:12px;color:var(--c-secondary);font-family:var(--f-mono);font-variant-numeric:tabular-nums;letter-spacing:0.02em;flex-shrink:0;line-height:1}
.ol-channel-turn:not([hidden]){display:inline-flex}
.ol-channel-turn-dot{display:inline-flex;align-items:center;gap:3px;height:8px}
.ol-channel-turn-dot i{display:inline-block;width:4px;height:4px;border-radius:50%;background:var(--c-accent);opacity:.35;animation:olp308DotPulse 1.4s ease-in-out infinite}
.ol-channel-turn-dot i:nth-child(2){animation-delay:.18s}
.ol-channel-turn-dot i:nth-child(3){animation-delay:.36s}
@keyframes olp308DotPulse{0%,100%{opacity:.25;transform:scale(.85)}40%{opacity:1;transform:scale(1)}}
@media(prefers-reduced-motion:reduce){.ol-channel-turn-dot i{animation:none;opacity:.6}}
@media(prefers-reduced-motion:reduce){.ol-channel-chev,.ol-channel-status-panel{transition:none!important}}
@media(max-width:700px){.ol-channel-header-name{font-size:24px}}
@media(max-width:700px){.ol-mobile-header{display:flex}.ol-compose textarea{font-size:16px}.ol-action{min-width:44px;min-height:44px}}
@media(max-width:500px){.ol-info-grid{grid-template-columns:1fr;gap:var(--sp-xs)}.ol-info-label{font-weight:600}}
@media(prefers-reduced-motion:reduce){.ol-skeleton,.ol-center,.wr-ring{animation:none!important;transition:none!important}}
/* OLP-168: DM CSS lives in dm_view.go (dmViewCSS) + static.go feedCSS (shared .ol-dm-* rules). */
/* -- Channel right-click context menu -- */
#ch-ctx-menu{position:fixed;z-index:9999;background:var(--c-bg);border:1px solid var(--c-border);border-radius:var(--r-md);box-shadow:0 4px 16px rgba(0,0,0,.12),0 1px 4px rgba(0,0,0,.06);padding:4px 0;min-width:160px;display:none;font-size:14px;user-select:none}
.ch-ctx-item{display:block;width:100%;padding:8px 16px;text-align:left;background:none;border:none;font-size:14px;font-family:var(--f-body);color:var(--c-primary);cursor:pointer;white-space:nowrap}
.ch-ctx-item:hover{background:var(--c-bg-hover)}
.ch-ctx-item.danger{color:var(--c-danger)}
.ch-ctx-sep{height:1px;background:var(--c-border);margin:4px 0}
/* OLP-447: UNIFIED MODAL SHELL — one CSS vocabulary, one open/close mechanic
   (.open), one z-index tier. Replaces BOTH the legacy .ol-modal chrome AND the
   retired .ol-pins-modal system. Every popup flow (Add an agent / Pin a lap /
   Pin an artifact / Share channel / Share project / spawn) configures THIS shell.
   Strict-grid rectangular inputs (10px radius, prefix glyph + 1px divider, blue
   focus ring) — pills (999px) are reserved for CTA buttons ONLY. Validation-ok
   is primaries-BLUE (var(--c-accent)), error red (var(--c-danger)), primary CTA
   ink-black (var(--c-primary)). All surfaces are token-driven so dark mode +
   reduced-motion come for free. Spec: spike v2 (mcp://artifact/doc/UjFP7fHc9_ylMydo). */
.ol-modal{position:fixed;inset:0;z-index:1310;display:none;align-items:center;justify-content:center}
.ol-modal.open{display:flex}
.ol-modal-backdrop{position:absolute;inset:0;background:rgba(15,20,25,.55);opacity:0;transition:opacity .14s ease}
.ol-modal.open .ol-modal-backdrop{opacity:1}
.ol-modal-card{position:relative;background:var(--c-bg);border:1px solid var(--c-border);border-radius:var(--r-lg);padding:28px;min-width:320px;max-width:460px;width:calc(100vw - 32px);box-shadow:0 24px 56px rgba(15,20,25,.30),0 2px 8px rgba(15,20,25,.10);box-sizing:border-box;transform:translateY(8px);opacity:0;transition:transform .16s ease,opacity .16s ease}
.ol-modal.open .ol-modal-card{transform:translateY(0);opacity:1}
.ol-modal-title{margin:0;font-size:22px;font-weight:700;letter-spacing:-0.015em;color:var(--c-primary);font-family:var(--f-body)}
.ol-modal-sub{margin:4px 0 0 0;font-size:13.5px;font-weight:400;color:var(--c-secondary);line-height:1.45}
.ol-modal-head{margin-bottom:20px}
.ol-modal-label{display:block;font-size:13px;font-weight:600;color:var(--c-primary);margin:0 0 7px 0;font-family:var(--f-body)}
/* strict-grid input: rect 10px, 1px stroke, prefix glyph + 1px divider, blue ring */
.ol-modal-input-wrap{display:flex;align-items:center;background:var(--c-bg);border:1px solid var(--c-border);border-radius:10px;padding:0 12px;height:46px;transition:border-color .12s,box-shadow .12s}
.ol-modal-input-wrap:focus-within{border-color:var(--c-accent);box-shadow:0 0 0 3px rgba(29,155,240,.14)}
.ol-modal-input-wrap.is-bad{border-color:var(--c-danger);box-shadow:0 0 0 3px rgba(244,33,46,.10)}
.ol-modal-pre{display:flex;align-items:center;color:var(--c-tertiary);font:500 15px var(--f-mono);flex-shrink:0}
.ol-modal-div{width:1px;height:20px;background:var(--c-border);margin:0 12px;flex-shrink:0}
.ol-modal-input{flex:1;min-width:0;box-sizing:border-box;padding:0;font-size:15px;font-family:var(--f-body);color:var(--c-primary);background:transparent;border:0;outline:none;height:100%}
.ol-modal-input::placeholder{color:var(--c-tertiary)}
/* OLP-447: trailing inline status glyph (was an absolute overlay): loading
   spinner / ok (blue ✓) / bad (red ✗). Label text moves to .ol-modal-help. */
.ol-modal-resolve{flex-shrink:0;display:flex;align-items:center;justify-content:center;min-width:18px;height:18px;margin-left:8px;font:700 13px var(--f-body);line-height:1}
.ol-modal-resolve.is-loading{width:16px;height:16px;border:2px solid var(--c-border);border-top-color:var(--c-accent);border-radius:50%;animation:ol-modal-resolve-spin .6s linear infinite;font-size:0}
.ol-modal-resolve.is-ok{color:var(--c-accent)}
.ol-modal-resolve.is-bad{color:var(--c-danger)}
@keyframes ol-modal-resolve-spin{to{transform:rotate(360deg)}}
.ol-modal-help{margin:8px 2px 0;font-size:12.5px;color:var(--c-secondary);line-height:1.45;min-height:17px}
.ol-modal-help.is-err{color:var(--c-danger)}
/* grid-aligned resolved row — confirms the resolved entity before commit */
.ol-modal-resolved{display:none;align-items:center;gap:10px;margin:12px 0 0;padding:10px 12px;border:1px solid var(--c-border);border-radius:10px;background:var(--c-fill)}
.ol-modal-resolved.show{display:flex}
.ol-modal-rglyph{width:26px;height:26px;border-radius:6px;background:rgba(29,155,240,.14);color:var(--c-accent);display:flex;align-items:center;justify-content:center;font:600 13px var(--f-mono);flex-shrink:0}
.ol-modal-rtxt{font-size:13px;color:var(--c-secondary);min-width:0;overflow:hidden;text-overflow:ellipsis}
.ol-modal-rtxt b{color:var(--c-primary);font-weight:600}
.ol-modal-err{margin-top:8px;font-size:13px;color:var(--c-danger);line-height:1.4}
.ol-modal-link{color:var(--c-accent)}
/* CTA buttons — the ONLY pills in the modal system */
.ol-modal-actions{display:flex;align-items:center;justify-content:flex-end;gap:10px;margin-top:24px}
.ol-modal-btn{padding:10px 20px;border-radius:var(--r-pill);border:1px solid var(--c-border);background:transparent;color:var(--c-secondary);font-size:14px;font-weight:600;font-family:var(--f-body);cursor:pointer;transition:opacity .12s,background .12s,transform .12s}
.ol-modal-btn:active{transform:scale(0.97)}
.ol-modal-btn:hover{background:var(--c-bg-hover);color:var(--c-primary)}
.ol-modal-btn--primary{background:var(--c-primary);border-color:var(--c-primary);color:var(--c-bg)}
.ol-modal-btn--primary:hover{opacity:.9;background:var(--c-primary);color:var(--c-bg)}
.ol-modal-btn--primary:disabled{opacity:.35;cursor:default}
/* OLP-578 #4/#5: guest-gate register modal — the quiet "what you unlock"
   ledger. Reuses the .ol-modal primitive; only these two atoms are new.
   In-palette only (blue check, ink/grey text) — no off-palette accent. */
.gg-perks{list-style:none;margin:0 0 22px;padding:14px 16px;border:1px solid var(--c-border);border-radius:10px;background:var(--c-fill)}
.gg-perks li{display:flex;align-items:center;gap:10px;font-size:13px;color:var(--c-secondary);padding:5px 0;line-height:1.3}
.gg-perks li svg{flex-shrink:0;color:var(--c-accent)}
.gg-perks li b{color:var(--c-primary);font-weight:600}
.gg-note{margin:14px 2px 0;font-size:12px;color:var(--c-tertiary);line-height:1.4;text-align:center}
@media(prefers-reduced-motion:reduce){.ol-modal-backdrop,.ol-modal-card{transition:opacity .1s}.ol-modal-card{transform:none}.ol-modal-resolve.is-loading{animation:none;border-top-color:var(--c-border)}}
/* OLP-571 ②b: Share-modal invite-link surface — seg tabs, copy button,
   owner controls (expiry select + guest-write toggle), consent foot-note.
   Atoms lifted from designer-openlap's spike (figma/olp571-guest-journey-spike.html),
   tokens mapped to the live shell set. Copy uses the blue accent (never green, law #4). */
.ol-seg{display:flex;gap:4px;background:var(--c-fill);padding:3px;border-radius:10px;margin:0 0 18px}
.ol-seg-btn{flex:1;border:0;background:transparent;font-family:var(--f-body);font-size:13px;font-weight:600;color:var(--c-secondary);padding:8px 10px;border-radius:7px;cursor:pointer}
.ol-seg-btn.on{background:var(--c-bg);color:var(--c-primary);box-shadow:0 1px 2px rgba(15,20,25,.10)}
.ol-share-pane[hidden]{display:none}
.ol-invite-url{font-family:var(--f-mono);font-size:13px}
.ol-copy-btn{flex:0 0 auto;margin-left:10px;border:0;background:var(--c-accent);color:#fff;font-family:var(--f-body);font-size:13px;font-weight:700;padding:8px 14px;border-radius:var(--r-pill);cursor:pointer;display:inline-flex;align-items:center;gap:6px;transition:background .12s,color .12s}
.ol-copy-btn:disabled{opacity:.5;cursor:default}
.ol-copy-btn.done{background:transparent;color:var(--c-accent);border:1.5px solid var(--c-accent)}
.ol-copy-btn svg{width:13px;height:13px}
.ol-ctrl-row{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:14px 0;border-top:1px solid var(--c-border)}
.ol-ctrl-row:first-of-type{margin-top:18px}
.ol-ctrl-txt .a{font-size:13.5px;font-weight:600;color:var(--c-primary)}
.ol-ctrl-txt .b{font-size:11.5px;color:var(--c-secondary);margin-top:2px;max-width:230px;line-height:1.4}
.ol-mini-sel{font-family:var(--f-body);font-size:13px;font-weight:600;color:var(--c-primary);background:var(--c-fill);border:1px solid var(--c-border);border-radius:8px;padding:7px 12px;cursor:pointer}
.ol-tgl{width:42px;height:24px;border-radius:var(--r-pill);background:var(--c-accent);position:relative;flex:0 0 auto;cursor:pointer;border:0;padding:0}
.ol-tgl::after{content:"";position:absolute;top:2px;left:20px;width:20px;height:20px;border-radius:50%;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.25)}
.ol-tgl.off{background:var(--c-border-dark)}
.ol-tgl.off::after{left:2px}
.ol-foot-note{display:flex;gap:8px;align-items:flex-start;font-size:12px;color:var(--c-secondary);line-height:1.45;margin:16px 0 0;padding-top:14px;border-top:1px solid var(--c-border)}
.ol-foot-note svg{width:15px;height:15px;flex:0 0 auto;color:var(--c-tertiary);margin-top:1px}
/* Flat-rail glyph/shape stack retired 2026-05-14 -- channel rows are now
   text-only, with the 22px leading slot reserved by .ol-nav-ch-pulse
   (defined alongside .ol-nav-channel-row above). Access state lives on
   the channel page (header chip + sharing modal), not in the rail. */

/* OLP-674: cross-project console overlay. Reuses the .ol-modal open/close +
   z-index tier; overrides ONLY the card size + the two-column body. Zero new
   tokens / colors. The header button sits in the .ol-header row alongside the
   menu button. */
.ol-header-console-btn{display:flex}
/* OLP-674 follow-up: when an artifact opens in the right drawer, hide the
   console button so it doesn't overlap the artifact panel's X — same as the
   menu button (body.has-artifact .ol-header-menu-btn hide above). */
body.has-artifact .ol-header-console-btn{display:none}
/* OLP-674 punch-list: summoned popover anchored top-right under the home icon,
   NOT a center modal. Container is click-through (pointer-events:none) so the
   channel behind stays interactive; only the card captures pointer events.
   Backdrop is transparent + non-blocking (no scrim, no lock). Click-outside +
   Esc dismiss are wired in JS. */
.ol-console-modal{align-items:flex-start;justify-content:flex-end;pointer-events:none}
.ol-console-modal .ol-modal-backdrop{background:transparent;pointer-events:none}
.ol-console-modal .ol-console-card{max-width:640px;width:min(640px,calc(100vw - 24px));margin-top:52px;margin-right:12px;height:min(640px,calc(100vh - 96px));padding:0;display:flex;flex-direction:column;overflow:hidden;pointer-events:auto}
.ol-console-head{display:flex;align-items:center;justify-content:space-between;padding:18px 22px 14px;border-bottom:1px solid var(--c-border);flex:0 0 auto}
.ol-console-close{display:flex;align-items:center;justify-content:center;width:32px;height:32px;border:1px solid transparent;border-radius:8px;background:transparent;color:var(--c-secondary);cursor:pointer;transition:background .12s,color .12s}
.ol-console-close:hover{background:var(--c-fill);color:var(--c-primary)}
.ol-console-close svg{width:18px;height:18px}
.ol-console-body{display:flex;flex:1 1 auto;min-height:0}
.ol-console-left{display:flex;flex-direction:column;flex:1 1 58%;min-width:0;border-right:1px solid var(--c-border)}
.ol-console-right{flex:0 0 38%;min-width:0;padding:16px 18px;overflow-y:auto;background:var(--c-fill)}
.ol-console-colhead{display:flex;align-items:baseline;justify-content:space-between;gap:10px;padding:12px 18px 8px;flex:0 0 auto}
.ol-console-colhead-t{font-size:13px;font-weight:700;letter-spacing:.01em;color:var(--c-primary)}
.ol-console-colhead-s{font-size:11px;font-family:var(--f-mono);color:var(--c-tertiary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ol-console-thread{flex:1 1 auto;min-height:0;overflow-y:auto;padding:4px 18px 8px;display:flex;flex-direction:column;gap:10px}
.ol-console-thread-empty{font-size:13px;color:var(--c-tertiary);padding:12px 2px}
.ol-console-msg{display:flex;flex-direction:column;gap:3px;padding:9px 12px;border:1px solid var(--c-border);border-radius:var(--r-md);background:var(--c-bg)}
.ol-console-msg-head{display:flex;align-items:baseline;gap:8px}
.ol-console-msg-author{font-size:12.5px;font-weight:700;color:var(--c-primary)}
.ol-console-msg-time{font-size:10.5px;color:var(--c-tertiary);font-family:var(--f-mono)}
.ol-console-msg-body{font-size:13px;line-height:1.5;color:var(--c-primary);word-break:break-word}
.ol-console-msg-body p{margin:0 0 6px}
.ol-console-msg-body p:last-child{margin:0}
.ol-console-caret{display:inline-block;width:7px;height:1.05em;background:var(--c-accent);vertical-align:text-bottom;margin-left:1px;animation:ol-console-caret-blink 1s step-end infinite}
@keyframes ol-console-caret-blink{50%{opacity:0}}
.ol-console-thinking{display:flex;align-items:center;gap:6px;padding:4px 20px 6px;flex:0 0 auto}
.ol-console-thinking[hidden]{display:none}
.ol-console-thinking-t{font-size:12px;color:var(--c-tertiary)}
.ol-console-dot{width:5px;height:5px;border-radius:50%;background:var(--c-tertiary);animation:ol-console-dot-bounce 1.2s infinite ease-in-out}
.ol-console-dot:nth-child(2){animation-delay:.2s}
.ol-console-dot:nth-child(3){animation-delay:.4s}
@keyframes ol-console-dot-bounce{0%,80%,100%{opacity:.3;transform:translateY(0)}40%{opacity:1;transform:translateY(-3px)}}
.ol-console-composer{display:flex;align-items:flex-end;gap:8px;padding:12px 18px 16px;border-top:1px solid var(--c-border);flex:0 0 auto}
.ol-console-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.ol-console-input{flex:1 1 auto;min-width:0;resize:none;max-height:120px;padding:9px 12px;font:400 14px var(--f-body);color:var(--c-primary);background:var(--c-bg);border:1px solid var(--c-border);border-radius:var(--r-md);outline:none;transition:border-color .12s,box-shadow .12s}
.ol-console-input:focus{border-color:var(--c-accent);box-shadow:0 0 0 3px rgba(29,155,240,.14)}
.ol-console-send{flex:0 0 auto;display:flex;align-items:center;justify-content:center;width:38px;height:38px;border:1px solid var(--c-primary);border-radius:50%;background:var(--c-primary);color:var(--c-bg);cursor:pointer;transition:opacity .12s,transform .12s}
.ol-console-send:hover:not(:disabled){opacity:.9}
.ol-console-send:active:not(:disabled){transform:scale(.95)}
.ol-console-send:disabled{opacity:.4;cursor:default}
.ol-console-send svg{width:18px;height:18px}
.ol-console-section{margin-bottom:18px}
.ol-console-sechead{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:0 0 9px}
.ol-console-sechead-t{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--c-secondary)}
.ol-console-variant-toggle{font-size:11px;font-weight:600;color:var(--c-tertiary);background:transparent;border:1px solid var(--c-border);border-radius:var(--r-pill);padding:2px 9px;cursor:pointer;transition:background .12s,color .12s}
.ol-console-variant-toggle:hover{background:var(--c-bg);color:var(--c-primary)}
.ol-console-variant-toggle[aria-pressed="true"]{color:var(--c-primary);border-color:var(--c-border-dark)}
.ol-console-needs{display:flex;flex-direction:column;gap:6px}
.ol-console-needs-row{display:flex;align-items:center;gap:8px;padding:8px 10px;border:1px solid var(--c-border);border-radius:var(--r-md);background:var(--c-bg)}
.ol-console-needs-label{flex:1 1 auto;min-width:0;font-size:12.5px;color:var(--c-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ol-console-pills{flex:0 0 auto;display:flex;align-items:center;gap:5px}
.ol-console-pill{display:inline-flex;align-items:center;height:18px;padding:0 8px;border-radius:var(--r-pill);font:700 10px var(--f-body);letter-spacing:.01em;text-transform:lowercase;white-space:nowrap;border:1px solid currentColor}
.ol-console-pill[data-tone="shared"]{color:var(--c-shared)}
.ol-console-pill[data-tone="pinned"]{color:var(--c-pinned)}
.ol-console-pill[data-state="present"]{color:var(--c-accent)}
.ol-console-pill[data-state="idle"]{color:var(--c-tertiary)}
.ol-console-pill[data-state="error"]{color:var(--c-shared)}
.ol-console-count{display:inline-flex;align-items:center;height:18px;padding:0 6px;border-radius:var(--r-pill);font:700 10px var(--f-mono);color:var(--c-tertiary);background:var(--c-fill)}
.ol-console-needs-empty{display:flex;flex-direction:column;gap:3px;padding:14px 10px;border:1px solid var(--c-success-light);background:var(--c-success-light);border-radius:var(--r-md)}
.ol-console-allclear{font-size:12.5px;font-weight:700;color:var(--c-success)}
.ol-console-allclear-s{font-size:11.5px;color:var(--c-tertiary)}
.ol-console-roster{display:flex;flex-direction:column;gap:5px}
.ol-console-roster-row{display:flex;align-items:center;gap:8px;padding:6px 10px;border-radius:var(--r-md)}
.ol-console-roster-glyph{width:14px;height:14px;flex:0 0 auto;color:var(--c-tertiary)}
.ol-console-roster-name{flex:0 0 auto;font-size:12.5px;font-weight:600;color:var(--c-primary)}
.ol-console-roster-chans{flex:1 1 auto;min-width:0;text-align:right;font-size:11px;font-family:var(--f-mono);color:var(--c-tertiary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ol-console-caption{margin:6px 2px 0;font-size:10.5px;font-style:italic;color:var(--c-tertiary)}
@media(max-width:760px){.ol-console-body{flex-direction:column}.ol-console-left{flex:1 1 auto;border-right:none;border-bottom:1px solid var(--c-border)}.ol-console-right{flex:0 0 auto}}
@media(prefers-reduced-motion:reduce){.ol-console-caret,.ol-console-dot{animation:none}}
