/*
 * shell-v3.css — Feature 253: the "shell v3" chrome skin, ported from the
 * operator's template `shell-header-template/css/shell.css` (source of visual
 * truth). Feature 254 added the SIDE layout + the layout toggle (section 11),
 * closing deviation D2 of the 253 spec. Feature 256 added the collapsible
 * rail groups + mini flyout (closing D-254-3) and the content-width rules
 * (section 12) that align `.app-content` with the chrome in both layouts.
 *
 * Scoping contract (the #1 risk of this feature is CSS collision — lessons
 * .crumbs/046, .sop/218, .kwgd/225):
 *   - Every DESIGN TOKEN lives on `html[data-shell-v3]` (never :root), so a
 *     non-pilot page is byte-identical. On the pilot page the tokens
 *     DELIBERATELY override style.css's :root palette (--bg/--surface/--ink/
 *     --line…): that page-wide repaint IS the feature ("default colours").
 *   - `--accent` is the ONE exception: style.css uses the same name for its
 *     ORANGE ui accent while the template uses it as a SUCCESS green. It is
 *     therefore defined only under `.shv3` (chrome containers) — page content
 *     keeps the platform meaning.
 *   - Every COMPONENT selector is prefixed with `.shv3` (the class carried by
 *     each v3 chrome root: header/nav/crumb strips). Generic template class
 *     names (.menu/.nav/.card/.tag…) can never leak into page content.
 *   - The template's DEMO-content classes (.empty/.grid3/.card/.page/.ghost)
 *     are NOT ported — they are not chrome. (The side-layout block, initially
 *     deferred, IS ported — 254. The .credits wallet pill, ported in 253 rev,
 *     was later REMOVED from the chrome on operator request — 2026-08-25.)
 *
 * Loaded lazily by app-shell.js only when <html data-shell-v3> is present.
 */

/* ── 1. Tokens — light base (template :root), gated ─────────────────── */
html[data-shell-v3]{
--bg:#f6f7fb;--surface:#fff;--surface-2:#fbfbfd;--line:#e8e9f0;--line-2:#f0f1f6;
--ink:#14161f;--ink-2:#4c5063;--ink-3:#868ba1;
--brand:#4536e0;--brand-ink:#fff;--brand-soft:#eeecfe;--brand-line:#ded9fb;
--warn:#c98a00;--warn-soft:#fff6e0;--shadow:0 1px 2px rgba(20,22,31,.04),0 8px 24px -12px rgba(20,22,31,.14);
--shadow-pop:0 12px 40px -12px rgba(20,22,31,.28),0 2px 6px rgba(20,22,31,.06);
--r:12px;
}
html[data-shell-v3][data-theme=dark]{
--bg:#0c0d13;--surface:#14161e;--surface-2:#181b25;--line:#242835;--line-2:#1e212c;
--ink:#f2f3f7;--ink-2:#a7adc0;--ink-3:#6f758a;
--brand:#7c6cff;--brand-ink:#0c0d13;--brand-soft:#1e1b3a;--brand-line:#2e2a55;
--warn:#e8b64a;--warn-soft:#2a2213;
--shadow:0 1px 2px rgba(0,0,0,.4),0 8px 24px -12px rgba(0,0,0,.6);
--shadow-pop:0 16px 48px -14px rgba(0,0,0,.7);
}
/* Success-green accent, CHROME-scoped only (see the header comment). */
html[data-shell-v3] .shv3{--accent:#00a37a}
html[data-shell-v3][data-theme=dark] .shv3{--accent:#3ad6ab}

/* ── 2. The 20 accents (light + dark variants + derived tones) ──────── */
html[data-shell-v3][data-accent=violet]{--brand:#4536e0}
html[data-shell-v3][data-accent=indigo]{--brand:#3355d1}
html[data-shell-v3][data-accent=ocean]{--brand:#0a6fd6}
html[data-shell-v3][data-accent=sky]{--brand:#0e8fc4}
html[data-shell-v3][data-accent=cyan]{--brand:#0a8f9e}
html[data-shell-v3][data-accent=teal]{--brand:#0a8f7f}
html[data-shell-v3][data-accent=emerald]{--brand:#0a8f63}
html[data-shell-v3][data-accent=green]{--brand:#3d8b1f}
html[data-shell-v3][data-accent=lime]{--brand:#6b8c05}
html[data-shell-v3][data-accent=amber]{--brand:#c07a05}
html[data-shell-v3][data-accent=orange]{--brand:#d1620a}
html[data-shell-v3][data-accent=coral]{--brand:#d94f3d}
html[data-shell-v3][data-accent=red]{--brand:#cf3535}
html[data-shell-v3][data-accent=rose]{--brand:#d13b6b}
html[data-shell-v3][data-accent=pink]{--brand:#c23a99}
html[data-shell-v3][data-accent=fuchsia]{--brand:#a63ad1}
html[data-shell-v3][data-accent=purple]{--brand:#7d3ad6}
html[data-shell-v3][data-accent=plum]{--brand:#8c3f6b}
html[data-shell-v3][data-accent=brown]{--brand:#8a5a3c}
html[data-shell-v3][data-accent=slate]{--brand:#3f4657}
html[data-shell-v3][data-theme=light][data-accent]{--brand-soft:color-mix(in oklab,var(--brand) 10%,#fff);--brand-line:color-mix(in oklab,var(--brand) 24%,#fff)}
html[data-shell-v3][data-theme=dark][data-accent=violet]{--brand:#7c6cff}
html[data-shell-v3][data-theme=dark][data-accent=indigo]{--brand:#6f8dff}
html[data-shell-v3][data-theme=dark][data-accent=ocean]{--brand:#4ea3ff}
html[data-shell-v3][data-theme=dark][data-accent=sky]{--brand:#48bde8}
html[data-shell-v3][data-theme=dark][data-accent=cyan]{--brand:#35c9d8}
html[data-shell-v3][data-theme=dark][data-accent=teal]{--brand:#33ccb6}
html[data-shell-v3][data-theme=dark][data-accent=emerald]{--brand:#3ad6ab}
html[data-shell-v3][data-theme=dark][data-accent=green]{--brand:#74cc4c}
html[data-shell-v3][data-theme=dark][data-accent=lime]{--brand:#b3cc38}
html[data-shell-v3][data-theme=dark][data-accent=amber]{--brand:#f0b93f}
html[data-shell-v3][data-theme=dark][data-accent=orange]{--brand:#ff9450}
html[data-shell-v3][data-theme=dark][data-accent=coral]{--brand:#ff8570}
html[data-shell-v3][data-theme=dark][data-accent=red]{--brand:#ff6d6d}
html[data-shell-v3][data-theme=dark][data-accent=rose]{--brand:#ff7ba6}
html[data-shell-v3][data-theme=dark][data-accent=pink]{--brand:#f077d0}
html[data-shell-v3][data-theme=dark][data-accent=fuchsia]{--brand:#d183ff}
html[data-shell-v3][data-theme=dark][data-accent=purple]{--brand:#a97dff}
html[data-shell-v3][data-theme=dark][data-accent=plum]{--brand:#d183b0}
html[data-shell-v3][data-theme=dark][data-accent=brown]{--brand:#c2926e}
html[data-shell-v3][data-theme=dark][data-accent=slate]{--brand:#9aa3ba}
html[data-shell-v3][data-theme=dark][data-accent]{--brand-ink:#0c0d13;--brand-soft:color-mix(in oklab,var(--brand) 16%,#14161e);--brand-line:color-mix(in oklab,var(--brand) 30%,#14161e)}

/* ── 3. Page defaults (the deliberate page-wide part of phase 1) ────── */
/* Font stack per spec FR-007/D5: Plus Jakarta Sans has NO Cyrillic subset —
 * Inter (self-hosted, Latin+Cyrillic, visually close) is the mandatory next
 * hop so Russian never falls back to the raw system font. */
html[data-shell-v3] body{background:var(--bg);color:var(--ink);font:15px/1.5 "Plus Jakarta Sans","Inter",system-ui,sans-serif;-webkit-font-smoothing:antialiased}
.shv3,.shv3 *{box-sizing:border-box}
.shv3 a{color:var(--brand);text-decoration:none}.shv3 a:hover{color:var(--ink)}
.shv3 button{font:inherit;color:inherit;background:none;border:0;cursor:pointer;margin:0;padding:0}
.shv3 .wrap{max-width:1520px;margin:0 auto;padding:0 28px}
.shv3 [hidden]{display:none!important}
/* Footer rail = header rail. The platform footer (095) is a body-level element,
 * NOT a `.shv3` strip, so it cannot inherit `.wrap`; it exposes `--pf-rail` and
 * we retarget it to the header's own max-width so the two line up. The side
 * layout is handled further down (§11), where `.wrap` itself goes full-bleed. */
html[data-shell-v3] #app-footer .pf-inner{--pf-rail:1520px;padding-left:28px;padding-right:28px}

/* ── 4. Legacy chrome hide + reflow (mirror of topnav-archetype.css) ── */
html[data-shell-v3] .app-shell .sidebar,
html[data-shell-v3] .app-shell #app-header{display:none!important}
html[data-shell-v3] .sidebar-overlay{display:none!important}
html[data-shell-v3] .app-shell{display:block}
html[data-shell-v3] .app-shell .app-main{margin:0;width:100%;max-width:100%}

/* ── 5. Top bar (template « .top ») ─────────────────────────────────── */
.shv3.top{position:sticky;top:0;z-index:60;background:color-mix(in oklab,var(--surface) 86%,transparent);backdrop-filter:saturate(160%) blur(14px);border-bottom:1px solid var(--line)}
.shv3.top .wrap{display:flex;align-items:center;gap:18px;height:64px}
.shv3 .brand{display:flex;align-items:center;gap:11px;min-width:0}
.shv3 .logo{width:32px;height:32px;border-radius:7px;background:linear-gradient(145deg,var(--brand),color-mix(in oklab,var(--brand) 55%,#00d3a7));display:grid;place-items:center;color:#fff;font-weight:700;font-size:14px;letter-spacing:-.02em}
.shv3 .brand b{font-size:15.5px;font-weight:700;letter-spacing:-.015em}
.shv3 .brand .sep{width:1px;height:18px;background:var(--line);margin:0 2px}
.shv3 .brand span.role{font-size:13px;color:var(--ink-3);font-weight:500}
.shv3 .spacer{flex:1}
.shv3 .ctl{display:flex;align-items:center;gap:8px}
.shv3 .picker{display:flex;align-items:center;gap:9px;height:38px;padding:0 12px;border:1px solid var(--line);border-radius:8px;background:var(--surface);transition:.15s}
.shv3 .picker:hover{border-color:var(--brand-line);background:var(--surface-2)}
.shv3 .picker .av{width:22px;height:22px;border-radius:5px;background:var(--brand-soft);color:var(--brand);display:grid;place-items:center;font-size:10px;font-weight:700}
.shv3 .picker .nm{font-size:13.5px;font-weight:600;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.shv3 .chev{width:14px;height:14px;color:var(--ink-3);flex:none}
.shv3 .cta{display:flex;align-items:center;gap:7px;height:38px;padding:0 15px;border-radius:8px;background:var(--brand);color:var(--brand-ink);font-size:13.5px;font-weight:600;transition:.15s}
.shv3 .cta:hover{filter:brightness(1.08);transform:translateY(-1px)}
.shv3 .icobtn{width:38px;height:38px;border-radius:8px;border:1px solid transparent;display:grid;place-items:center;color:var(--ink-2);position:relative;transition:.15s}
.shv3 .icobtn:hover{background:var(--surface-2);border-color:var(--line);color:var(--ink)}
.shv3 .icobtn .bell-dot{position:absolute;top:8px;right:9px;width:7px;height:7px;border-radius:50%;background:#ef4444;border:2px solid var(--surface)}
.shv3 .flag{width:20px;height:14px;border-radius:3px;overflow:hidden;display:block;flex:none;box-shadow:inset 0 0 0 1px rgba(0,0,0,.08)}
.shv3 .flag svg,.shv3 .flag img{width:100%;height:100%;display:block}
.shv3 .me{display:flex;align-items:center;gap:8px;height:38px;padding:0 8px 0 5px;border-radius:8px;transition:.15s}
.shv3 .me:hover{background:var(--surface-2)}
.shv3 .me .av{width:28px;height:28px;border-radius:6px;background:linear-gradient(145deg,#2a2f45,#4b5270);color:#fff;display:grid;place-items:center;font-size:11px;font-weight:700}

/* ── 6. Nav row (template « .nav ») ─────────────────────────────────── */
.shv3.nav{background:var(--surface);border-bottom:1px solid var(--line);position:sticky;top:64px;z-index:55}
.shv3.nav .wrap{display:flex;align-items:center;gap:2px;min-height:52px;flex-wrap:nowrap;overflow:visible}
.shv3 .nl{display:flex;align-items:center;gap:8px;height:34px;padding:0 12px;border-radius:7px;font-size:13.5px;font-weight:500;color:var(--ink-2);white-space:nowrap;position:relative;transition:.15s}
.shv3 .nl svg{width:16px;height:16px;opacity:.75;flex:none}
.shv3 .nl:hover{background:var(--surface-2);color:var(--ink)}
.shv3 .nl.on{color:var(--brand);background:var(--brand-soft);font-weight:600}
.shv3 .nl.on svg{opacity:1}
.shv3 .nl .cnt{font:500 10.5px/1 "JetBrains Mono",ui-monospace,monospace;padding:3px 5px;border-radius:5px;background:var(--line-2);color:var(--ink-3)}
.shv3 .nl.on .cnt{background:color-mix(in oklab,var(--brand) 16%,transparent);color:var(--brand)}
.shv3 .navsep{width:1px;height:20px;background:var(--line);margin:0 8px;flex:none}
/* The horizontal guard hides the transient spill between a layout change and
 * the next priority+ fit() pass. It must stay on the X axis ONLY: the template
 * ships `overflow:hidden`, which also clips the Y axis — and a group dropdown
 * (256) hangs BELOW this box, so `hidden` made the SEO/SGE menus invisible AND
 * unclickable (measured: menu top 48px vs clip box bottom 42px, hit-test found
 * the page background). `clip` rather than `hidden` because pairing `hidden`
 * with `overflow-y:visible` coerces the Y axis to `auto` (measured) — i.e. a
 * scrollbar in the nav row. Deliberately NO `overflow:hidden` fallback for
 * engines without `clip`: it would trigger exactly that coercion; those engines
 * fall back to both axes visible, where the menus still work. */
.shv3 .navitems{display:flex;align-items:center;gap:2px;min-width:0;overflow-x:clip;overflow-y:visible;flex:1}
.shv3 .navend{display:flex;align-items:center;gap:2px;flex:none;padding-left:6px}
.shv3 .hid{display:none!important}
.shv3 #sv3-moremenu{max-height:min(70vh,460px);overflow:auto;min-width:260px}
.shv3 #sv3-moremenu .mgrp{padding:9px 10px 5px;font-size:10.5px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3);display:flex;align-items:center;gap:8px}
.shv3 #sv3-moremenu .mgrp svg{width:16px;height:16px;flex:none;opacity:.75}
.shv3 #sv3-moremenu .mi svg{width:16px;height:16px;flex:none;opacity:.75}
.shv3 #sv3-moremenu .sub-mi{padding-left:28px}
.shv3 .navmore .nl .cnt{background:var(--brand-soft);color:var(--brand)}

/* ── 7. Dropdowns (template « .dd ») ────────────────────────────────── */
.shv3 .dd{position:relative}
.shv3 .menu{position:absolute;top:calc(100% + 6px);min-width:250px;background:var(--surface);border:1px solid var(--line);border-radius:8px;box-shadow:var(--shadow-pop);padding:5px;opacity:0;transform:translateY(-4px);pointer-events:none;transition:.16s;z-index:80}
.shv3 .menu.r{right:0}.shv3 .menu.l{left:0}
.shv3 .dd.open .menu{opacity:1;transform:none;pointer-events:auto}
.shv3 .mi{display:flex;align-items:center;gap:10px;padding:9px 10px;border-radius:6px;font-size:13.5px;color:var(--ink-2);width:100%;text-align:left;transition:.12s}
.shv3 .mi:hover{background:var(--surface-2);color:var(--ink)}
.shv3 .mi.on{color:var(--brand);font-weight:600;background:var(--brand-soft)}
.shv3 .mi .k{margin-left:auto;font:500 10.5px/1 "JetBrains Mono",ui-monospace,monospace;color:var(--ink-3)}
.shv3 .mlab{padding:9px 10px 5px;font-size:10.5px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3)}
.shv3 .mhr{height:1px;background:var(--line-2);margin:5px 2px}

/* ── 8. Accent swatches (template « .swgrid ») ──────────────────────── */
.shv3 .swgrid{display:grid;grid-template-columns:repeat(5,1fr);gap:5px;padding:4px}
.shv3 .sw{display:grid;place-items:center;padding:6px 0;border-radius:7px;border:1px solid transparent;transition:.14s}
.shv3 .sw:hover{background:var(--surface-2)}
.shv3 .sw.on{border-color:var(--brand-line);background:var(--brand-soft)}
.shv3 .sw i{width:22px;height:22px;border-radius:5px;display:block;box-shadow:inset 0 0 0 1px rgba(0,0,0,.08)}
.shv3 .sw.on i{box-shadow:inset 0 0 0 1px rgba(0,0,0,.08),0 0 0 2px var(--surface),0 0 0 3.5px var(--brand)}

/* ── 9. Breadcrumb strip (template « .crumb ») ──────────────────────── */
/* Hosts the EXISTING feature-046 breadcrumb component (decision D3): the
 * strip provides the template chrome, the component keeps rendering the
 * localized path. Its own bar chrome is neutralised so it sits flat. */
.shv3.crumb{border-bottom:1px solid var(--line-2);position:relative;z-index:1;background:var(--bg)}
/* Pages whose route has no breadcrumb never show an empty grey band. */
.shv3.crumb:not(:has(.page-crumbbar)){display:none}
.shv3.crumb .wrap{display:flex;align-items:center;gap:12px;min-height:54px}
.shv3.crumb .page-crumbbar{border:0;background:transparent;padding:0;margin:0;box-shadow:none;flex:1;min-width:0}
.shv3 .bc{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--ink-3);min-width:0}
.shv3 .bc a{color:var(--ink-3);display:flex;align-items:center;gap:6px}
.shv3 .bc a:hover{color:var(--ink)}
.shv3 .bc b{color:var(--ink);font-weight:600}
.shv3 .bc .s{color:var(--ink-3);opacity:.5}
.shv3 .tag{display:inline-flex;align-items:center;gap:6px;height:24px;padding:0 9px;border-radius:6px;background:var(--surface);border:1px solid var(--line);font-size:11.5px;font-weight:600;color:var(--ink-2)}
.shv3 .tag .d{width:6px;height:6px;border-radius:50%;background:var(--accent)}

/* ── 10. Responsive (template media queries, chrome parts only) ─────── */
@media(max-width:1100px){.shv3 .brand .sep,.shv3 .brand .role{display:none}}
@media(max-width:760px){.shv3 .wrap{padding:0 16px}.shv3 .picker .nm{max-width:90px}
/* Keep the footer rail on the header's narrow gutter too — without this the
 * rule above (higher specificity than footer.css) would pin 28px on mobile. */
html[data-shell-v3] #app-footer .pf-inner{padding-left:16px;padding-right:16px}}

/* ── 11. Side layout (feature 254 — template « sidebar layout ») ────── */
/* Attributes are NAMESPACED (`data-sv3-layout`/`data-sv3-side`) so a third-
 * party module claiming the template's generic `data-layout` can never fight
 * this chrome (spec FR-002). Everything below stays gated. */
.shv3.side{display:none}
html[data-shell-v3][data-sv3-layout=side] .shv3.nav{display:none}
html[data-shell-v3][data-sv3-layout=side] .shv3.side{display:flex;flex-direction:column;position:fixed;top:0;left:0;bottom:0;width:268px;background:var(--surface);border-right:1px solid var(--line);z-index:70;overflow-y:auto;overflow-x:hidden;scrollbar-width:none;transition:width .18s}
.shv3.side::-webkit-scrollbar{display:none}
/* Chrome strips shift by the rail width (template pads .top/.crumb/.page). */
html[data-shell-v3][data-sv3-layout=side] .shv3.top{padding-left:268px;transition:padding-left .18s}
html[data-shell-v3][data-sv3-layout=side] .shv3.crumb{padding-left:268px;transition:padding-left .18s}
/* Content + footer shift via MARGIN, not padding (D-254-1): the platform
 * content column and the body-level footer keep their own inner padding —
 * the template's `.page` padding trick does not map onto them. */
html[data-shell-v3][data-sv3-layout=side] .app-shell .app-main{margin:0 0 0 268px;width:auto;transition:margin-left .18s}
html[data-shell-v3][data-sv3-layout=side] #app-footer{margin-left:268px;transition:margin-left .18s}
/* Mini rail — 66px, icons only. */
html[data-shell-v3][data-sv3-layout=side][data-sv3-side=mini] .shv3.side{width:66px}
html[data-shell-v3][data-sv3-layout=side][data-sv3-side=mini] .shv3.top,
html[data-shell-v3][data-sv3-layout=side][data-sv3-side=mini] .shv3.crumb{padding-left:66px}
html[data-shell-v3][data-sv3-layout=side][data-sv3-side=mini] .app-shell .app-main{margin-left:66px}
html[data-shell-v3][data-sv3-layout=side][data-sv3-side=mini] #app-footer{margin-left:66px}
/* The brand moves into the rail head; the top-bar copy disappears. */
html[data-shell-v3][data-sv3-layout=side] .shv3.top .brand{display:none}
/* Template fix line: strips span the full width next to a fixed rail. */
html[data-shell-v3][data-sv3-layout=side] .shv3 .wrap{margin-left:0;max-width:none}
/* …and the footer rail follows them, so the bottom bar keeps matching the
 * header in BOTH views (top = centred 1520px rail, side = full-bleed). */
html[data-shell-v3][data-sv3-layout=side] #app-footer .pf-inner{margin-left:0;--pf-rail:none}
/* Burger (top bar) only exists while the side layout is on. */
.shv3 .burger{display:none}
html[data-shell-v3][data-sv3-layout=side] .shv3 .burger{display:grid}
/* Rail head + collapse button. */
.shv3.side .shead{display:flex;align-items:center;gap:11px;height:76px;padding:0 18px;flex:none}
.shv3.side .shead .logo{flex:none;width:34px;height:34px;border-radius:50%}
.shv3.side .shead b{font-size:20px;font-weight:700;letter-spacing:-.03em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.shv3.side .scol{margin-left:auto;width:30px;height:30px;border-radius:7px;display:grid;place-items:center;color:var(--ink-3);transition:.14s;flex:none}
.shv3.side .scol:hover{background:var(--surface-2);color:var(--ink)}
/* Rail nav: group labels + links (mirror of the legacy sidebar-nav). */
.shv3.side .sgrp{padding:18px 20px 6px;font-size:12.5px;font-weight:600;color:var(--ink-3);white-space:nowrap}
/* Divider row (a bare `—` section in the legacy nav becomes a hairline). */
.shv3.side .shr{height:1px;background:var(--line-2);margin:10px 14px;flex:none}
.shv3.side nav{padding:4px 12px 24px;display:flex;flex-direction:column;gap:1px}
.shv3 .sl{display:flex;align-items:center;gap:13px;width:100%;height:44px;padding:0 12px;border-radius:9px;font-size:15px;font-weight:500;color:var(--ink);white-space:nowrap;transition:.14s}
/* `.si` = icon slot (the legacy sidebar icons are text/emoji, not template
 * SVGs — a fixed slot keeps mini-mode centering identical either way).
 *
 * Feature 262 — menu icons enlarged by 80 % (operator request). The slot holds
 * TWO kinds of glyph and each needed its own lever, otherwise only half the
 * rows would have grown:
 *   - text/emoji (rows with no ICON_BY_KEY mapping, e.g. 📊) scale with
 *     `font-size`: 16px → 28.8px;
 *   - SVG icons carry their own width/height attributes (15px) which no CSS
 *     rule overrode, so `font-size` alone left them untouched: 15px → 27px.
 * The 15 vs 16 gap is the pre-existing one, preserved by construction (both
 * multiplied by the same 1.8) rather than silently harmonised.
 *
 * Feature 263 — the DEPLOYED rail came back 10 % too big (operator), so these
 * base values are the 262 ones × 0.9. The MINI rail keeps the full 262 size on
 * purpose: with the labels gone, the icon is the only thing left to recognise a
 * row by, so it must not shrink with them. The restore lives in BOTH mini
 * contexts further down (the explicit toggle AND the ≤1100px forced mini) —
 * covering only one would make a narrow window differ from a collapsed one. */
.shv3 .sl .si{width:32.4px;height:32.4px;display:grid;place-items:center;font-size:25.92px;line-height:1;opacity:.85;flex:none}
.shv3 .sl .si svg{width:24.3px;height:24.3px}
.shv3 .sl:hover{background:var(--surface-2)}
.shv3 .sl.on{background:var(--brand-soft);color:var(--brand);font-weight:600}
.shv3 .sl .cnt{margin-left:auto;font:600 11.5px/1 "Plus Jakarta Sans",sans-serif;padding:5px 9px;border-radius:999px;background:color-mix(in oklab,var(--brand) 13%,transparent);color:var(--brand)}
/* Collapsible groups (256 — template `.sgroup`): the <details> groups of the
 * project sidebar mirror into the rail with the template grid-rows reveal. */
.shv3 .sl .ar{margin-left:auto;width:16px;height:16px;color:var(--ink-3);transition:.16s}
.shv3 .sl .cnt+.ar{margin-left:10px}
.shv3 .sgroup.open>.sl .ar{transform:rotate(180deg)}
.shv3 .sgroup .sub{display:grid;grid-template-rows:0fr;transition:grid-template-rows .18s}
.shv3 .sgroup.open .sub{grid-template-rows:1fr}
.shv3 .sgroup .sub>div{overflow:hidden}
/* Sub-links are text-only, so their indent is not decorative: it aligns them
 * under the LABEL of their parent row. The value is derived, not chosen —
 * `padding (12) + icon slot + gap (13)` — so it MUST move with the slot.
 * Feature 262: 12 + 20 + 13 = 45px  →  12 + 36 + 13 = 61px. Leaving 45px would
 * have left every sub-menu visibly hanging left of its own group label.
 * Feature 263 (slot back to 32.4px): 12 + 32.4 + 13 = 57.4px. It tracks the
 * DEPLOYED slot, never the mini one — sub-menus are hidden in mini mode. */
.shv3 .sub .sl{height:38px;font-size:14px;font-weight:400;padding-left:57.4px;color:var(--ink-2)}
.shv3 .sub .sl:hover{color:var(--ink)}
/* Mini: hide labels/groups/badges/collapse, center the icons. */
html[data-shell-v3][data-sv3-layout=side][data-sv3-side=mini] .shv3.side .lbl,
html[data-shell-v3][data-sv3-layout=side][data-sv3-side=mini] .shv3.side .sgrp,
html[data-shell-v3][data-sv3-layout=side][data-sv3-side=mini] .shv3.side .cnt,
html[data-shell-v3][data-sv3-layout=side][data-sv3-side=mini] .shv3.side .ar,
html[data-shell-v3][data-sv3-layout=side][data-sv3-side=mini] .shv3.side .sub,
html[data-shell-v3][data-sv3-layout=side][data-sv3-side=mini] .shv3.side .scol{display:none}
html[data-shell-v3][data-sv3-layout=side][data-sv3-side=mini] .shv3.side .sl{justify-content:center;padding:0}
/* Feature 263 — mini keeps the FULL 262 icon size (the deployed rail shrank by
 * 10 %, this one deliberately did not): once the labels are hidden the icon is
 * the only thing identifying a row, so shrinking it here would cost legibility
 * exactly where there is nothing else to read. */
html[data-shell-v3][data-sv3-layout=side][data-sv3-side=mini] .shv3.side .sl .si{width:36px;height:36px;font-size:28.8px}
html[data-shell-v3][data-sv3-layout=side][data-sv3-side=mini] .shv3.side .sl .si svg{width:27px;height:27px}
html[data-shell-v3][data-sv3-layout=side][data-sv3-side=mini] .shv3.side .shead{justify-content:center;padding:0}
/* ≤1100px the rail is forced mini (template behaviour). */
@media(max-width:1100px){
html[data-shell-v3][data-sv3-layout=side] .shv3.side{width:66px}
html[data-shell-v3][data-sv3-layout=side] .shv3.top,
html[data-shell-v3][data-sv3-layout=side] .shv3.crumb{padding-left:66px}
html[data-shell-v3][data-sv3-layout=side] .app-shell .app-main{margin-left:66px}
html[data-shell-v3][data-sv3-layout=side] #app-footer{margin-left:66px}
html[data-shell-v3][data-sv3-layout=side] .shv3.side .lbl,
html[data-shell-v3][data-sv3-layout=side] .shv3.side .sgrp,
html[data-shell-v3][data-sv3-layout=side] .shv3.side .cnt,
html[data-shell-v3][data-sv3-layout=side] .shv3.side .ar,
html[data-shell-v3][data-sv3-layout=side] .shv3.side .sub,
html[data-shell-v3][data-sv3-layout=side] .shv3.side .scol{display:none}
html[data-shell-v3][data-sv3-layout=side] .shv3.side .sl{justify-content:center;padding:0}
/* Feature 263 — same full-size icons in the FORCED mini (≤1100px). Restoring
 * only the toggled mini above would make a narrow window show smaller icons
 * than a collapsed rail, for no reason a user could explain. */
html[data-shell-v3][data-sv3-layout=side] .shv3.side .sl .si{width:36px;height:36px;font-size:28.8px}
html[data-shell-v3][data-sv3-layout=side] .shv3.side .sl .si svg{width:27px;height:27px}
html[data-shell-v3][data-sv3-layout=side] .shv3.side .shead{justify-content:center;padding:0}
}
/* Mini-rail hover tooltip (template #sidetip). */
.shv3.sidetip{position:fixed;z-index:300;background:var(--surface);color:var(--ink);border:1px solid var(--line);box-shadow:var(--shadow-pop);border-radius:7px;padding:7px 11px;font-size:12.5px;font-weight:600;white-space:nowrap;pointer-events:none;opacity:0;transform:translateY(-50%) translateX(-4px);transition:opacity .12s,transform .12s}
.shv3.sidetip.on{opacity:1;transform:translateY(-50%)}
/* Grouped flyout (256 — template #sidetip.flyout): an INTERACTIVE panel
 * listing a group's entries while the rail is icons-only; pointer-events
 * re-enabled so the entries stay clickable (the label tip stays inert).
 *
 * ⚠ Interactivity is gated on `.on` ON PURPOSE (ghost-click incident,
 * 2026-08-23): hiding only removes `.on` (opacity 0) and the panel keeps its
 * fixed position next to the rail with the group's links still in it. With
 * `pointer-events:auto` on the bare `.flyout`, that invisible panel kept
 * capturing hovers and clicks over the content beside the rail — on the
 * project page, clicking the Article Writer stepper randomly opened
 * rank-tracking or another SEO entry, whichever ghost row sat under the
 * cursor. Hidden ⇒ inert, always. */
.shv3.sidetip.flyout{padding:5px;min-width:196px;transform:translateY(0) translateX(-4px)}
.shv3.sidetip.flyout.on{transform:translateY(0);pointer-events:auto}
.shv3.sidetip .ftitle{padding:8px 10px 6px;font-size:10.5px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3)}
.shv3.sidetip .fi{display:block;width:100%;text-align:left;padding:8px 10px;border-radius:6px;font-size:13px;font-weight:500;color:var(--ink-2);transition:.12s;cursor:pointer}
.shv3.sidetip .fi:hover{background:var(--surface-2);color:var(--ink)}

/* ── 12. Content width (feature 256 — template `.page > .wrap`) ─────── */
/* The template centres page content inside a 1520px wrap ALIGNED with the
 * chrome strips in the top layout, and lets it fill the remaining width next
 * to the fixed rail in the side layout (`.wrap{margin-left:0;max-width:none}`).
 * `.app-content` is the shared content container of every gated page — the
 * rules below give it exactly that behaviour, in BOTH layouts. */
html[data-shell-v3] .app-shell .app-content{width:100%;max-width:1520px;margin:0 auto;padding:32px 28px 64px}
html[data-shell-v3][data-sv3-layout=side] .app-shell .app-content{max-width:none;margin:0}
@media(max-width:760px){html[data-shell-v3] .app-shell .app-content{padding:16px 16px 48px}}
