.v1-main {
  display: grid;
  grid-template-columns: 48px 260px 6px minmax(0, 1fr) 6px 300px;
  min-height: 0;
  overflow: hidden;
  height: 100%;
  /* Base (LTR) order: activity bar / LS docked left, RS docked right - grid
     column order follows `direction` the same way flex row order does. Full
     RTL mirror below flips this (LS -> right, RS -> left, plus CS/DS content
     direction, matching a real RTL language) - tied to `dir="rtl"` only.
     layout-runtime.js's resize-drag delta math has a matching sign flip,
     also dir="rtl"-only. The activity bar's own side is handled separately
     below (General -> "Swap panel sides"), independent of dir="rtl": since
     `order` assigns track NUMBERS regardless of `direction` (direction only
     flips which physical side track 1 vs track 6 render on), the same
     `order: 6` on the activity bar lands it on the opposite physical side
     from wherever dir="rtl" already put it, in both directions - "swap
     panel sides" always means "flip the activity bar to the other end",
     not "force it right". See .v1-activity's rules further down for the
     matching button-alignment/indicator overrides this needs per direction. */
  direction: ltr;
}

html[dir="rtl"] .v1-main {
  direction: rtl;
}

/* General -> "Swap panel sides": relocates the activity bar via `order`
   (not `direction`), so LS/RS's relative position to CS and CS/DS content
   direction are untouched - independent of dir="rtl" (see comment above).
   layout-runtime.js's applySizes() toggles this class and regenerates
   gridTemplateColumns to match the reordered track sizes, since grid
   auto-placement fills tracks in `order` sequence regardless of direction. */
body.v1-activity-last .v1-sidebar {
  order: 1;
}

body.v1-activity-last [data-resize="left"] {
  order: 2;
}

body.v1-activity-last .v1-editor {
  order: 3;
}

body.v1-activity-last [data-resize="right"] {
  order: 4;
}

body.v1-activity-last .v1-rightbar {
  order: 5;
}

body.v1-activity-last .v1-activity {
  order: 6;
}

/* .v1-activity's own flex-column cross-axis alignment (buttons flush-left
   vs flush-right) needs to match whichever physical side it actually ends
   up on - which depends on BOTH dir="rtl" and v1-activity-last together
   (order: 6 lands on the opposite side depending on direction, see above),
   not either one alone. Icon-only buttons, no text to mirror, so flipping
   direction here is always safe. */
html:not([dir="rtl"]) body.v1-activity-last .v1-activity {
  /* LTR + toggle: order: 6 -> physical right (inherits nothing, needs rtl). */
  direction: rtl;
}

html[dir="rtl"] body.v1-activity-last .v1-activity {
  /* RTL + toggle: order: 6 -> physical LEFT (cancel the rtl it would
     otherwise inherit from .v1-main, since it's no longer on the right). */
  direction: ltr;
}

.v1-resizer-v {
  background: var(--bg-elev);
  border-left: 0;
  border-right: 0;
  cursor: col-resize;
}

.v1-resizer-v:hover,
.v1-resizer-v.dragging {
  background: var(--activity);
}

.v1-activity {
  border-right: 0;
  background: #191919;
  padding: 8px 6px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.v1-activity button {
  width: 34px;
  height: 34px;
  border-radius: 8px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--txt-dim);
  font-size: 16px;
  cursor: pointer;
  position: relative;
}

.v1-activity button.active {
  background: transparent;
  border-color: transparent;
  color: #fff;
}

.v1-activity button.active::before {
  content: "";
  position: absolute;
  left: -6px;
  top: 0;
  bottom: 0;
  width: 2px;
  border-radius: 0;
  background: #3794ff;
}

/* Physically-right cases only: (dir="rtl" AND NOT toggle) OR (NOT dir="rtl"
   AND toggle) - the same "opposite side" logic as .v1-activity's direction
   rules above. The remaining two combinations (both true or both false)
   land the activity bar on the LEFT, where the base left: -6px above is
   already correct without any override. */
html[dir="rtl"] body:not(.v1-activity-last) .v1-activity button.active::before,
html:not([dir="rtl"]) body.v1-activity-last .v1-activity button.active::before {
  /* Not a mirrored -6px (poking 6px past the button's own box, into
     .v1-activity's padding gutter) - when the activity bar is the
     rightmost grid column, its right edge sits flush against .v1-main's
     right edge (0 buffer), so pushing content past the button's box out
     that way gets clipped by .v1-main's overflow: hidden. Sitting flush
     with the button's own edge (right: 0) stays inside the button's box,
     so it can never be clipped, regardless of exactly how much padding
     buffer the activity bar has on that side. */
  left: auto;
  right: 0;
}

.v1-activity button:hover:not(.active) {
  background: #2a2d31;
  border-color: #3f3f46;
  color: #ffffff;
}

.v1-sidebar {
  border-right: 0;
  background: var(--v1-tabs-strip-bg);
  min-width: 0;
  min-height: 0;
  display: grid;
  grid-template-rows: 34px 1fr;
}

/* LS's content wrapper - structural mirror of RS's .v1-right-content
   (components/assistant.css): same grid shape, same role (single "home
   container" for both the static panels below and reparented/generic-slot
   content in navigation-runtime.js/bootstrap-runtime.js). Deliberately no
   padding/background of its own, unlike .v1-right-content - LS's children
   already provide their own (.v1-tool-list's 8px, #v1SidebarGenericContent's
   own background), so adding any here would double up, not just mirror. */
.v1-left-content {
  display: grid;
  grid-template-rows: minmax(0, 1fr);
  height: 100%;
  min-height: 0;
  box-sizing: border-box;
  overflow: hidden;
}

/* LS "generic content slot" (Network Monitor, ip-library, ShellCraft
   Library, ...) - index.html no longer gives this element class="tool-
   detail" (that's a CS-card style: 10px padding + ".tool-detail ul"'s 6px
   margin-top, meant for prose-style About/License/Versions panels, not a
   compact sidebar list). Supplying only what this slot actually needs -
   background/base typography, nothing card-shaped - avoids that whole
   class of bug leaking in again. [data-sidebar-tool-panel] (scan-runner's
   dedicated, non-generic-slot panel) is the reference: it carries zero
   wrapper padding of its own, .v1-tool-list's own 8px is the only
   spacing. */
#v1SidebarGenericContent {
  min-height: 0;
  overflow: hidden;
  background: var(--bg-elev);
  color: var(--txt-dim);
  font-size: 12px;
  line-height: 1.5;
}

/* Bare attribute selector (not scoped to .v1-sidebar >) - scan-runner's
   panel can be reparented into RS's .v1-right-content (see navigation-
   runtime.js's activateGenericContent() "move" branch), so its layout must
   keep applying regardless of current DOM parent. This attribute is unique
   to scan-runner's panel, so widening the selector is safe. */
[data-sidebar-tool-panel] {
  min-height: 0;
  overflow: hidden;
  height: 100%;
  display: flex;
  flex-direction: column;
}

[data-sidebar-tool-panel][hidden] {
  display: none !important;
}

[data-sidebar-tool-panel] .v1-tool-list {
  flex: 1 1 auto;
  min-height: 0;
  background: var(--bg-elev);
}

.v1-side-head {
  border-bottom: 0;
  background: var(--v1-tabs-strip-bg);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--txt-dim);
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  gap: 8px;
  padding: 0 6px 0 0;
}

html[dir="rtl"] .v1-side-head {
  padding: 0 0 0 6px;
}

.v1-head-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.v1-sidebar-tool-tabs {
  min-width: 0;
  display: inline-flex;
  align-items: stretch;
  gap: 0;
  flex-wrap: nowrap;
  font-size: 12px;
  letter-spacing: normal;
  text-transform: none;
}

.v1-sidebar-tool-tab-wrap {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  max-width: 240px;
  padding: 7px 10px;
  color: var(--txt-dim);
  background: var(--bg-elev);
  font-family: var(--font-ui);
  line-height: 1.2;
  position: relative;
}

.v1-sidebar-tool-tab-wrap.sidebar-tab-closed {
  display: none;
}

/* border-top instead of inset box-shadow - see .v1-tab.active's comment
   for why (subpixel vertical seam bleed at the active tab's edges). */
.v1-sidebar-tool-tab-wrap.is-left-active {
  color: var(--txt);
  background: var(--bg-elev);
  border-top: 2px solid var(--brand);
  padding-top: 5px;
}

.v1-sidebar-tool-tab-wrap:hover:not(.is-left-active) {
  color: #ffffff;
  background: #222326;
}

.v1-sidebar-tool-tab {
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  padding: 0;
  cursor: pointer;
  white-space: nowrap;
}

.v1-sidebar-tool-tab-close {
  display: none;
  width: 16px;
  height: 16px;
  align-items: center;
  justify-content: center;
  border: 1px solid transparent;
  color: #c6c9d0;
  cursor: pointer;
  line-height: 1;
  font-size: 12px;
  flex: 0 0 auto;
}

.v1-sidebar-tool-tab-wrap.is-left-active .v1-sidebar-tool-tab-close {
  display: inline-flex;
}

.v1-sidebar-tool-tab-close:hover {
  border-color: #4a4f58;
  background: #2c3139;
  color: #ffffff;
}

.v1-panel-toggle {
  align-self: center;
  border: 0;
  background: transparent;
  color: #aeb2b9;
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  font-size: 11px;
  font-family: var(--font-ui);
  line-height: 1;
  padding: 0;
  text-transform: none;
  letter-spacing: normal;
}

.v1-panel-toggle:hover {
  background: #2f3338;
  color: #ffffff;
}

.v1-tool-list {
  list-style: none;
  margin: 0;
  padding: 8px;
  overflow: auto;
  overflow-x: hidden;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-content: start;
  gap: 5px;
  height: 100%;
}

.v1-tool-list li {
  border: 1px solid transparent;
  border-radius: 8px;
  padding: 8px 10px;
  min-width: 0;
  overflow: visible;
  font-size: 13px;
  color: var(--txt-dim);
  cursor: pointer;
  background: transparent;
}

.v1-tool-list li.active {
  color: var(--txt);
  border-color: var(--line);
  background: color-mix(in oklab, var(--bg-soft) 82%, var(--brand) 18%);
}

.v1-tool-list li:hover:not(.active) {
  color: #ffffff;
  border-color: #3f3f46;
  background: #2a2d31;
}

/* ── Collapsible sections ── */
.v1-section-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  cursor: pointer;
  user-select: none;
}
.v1-collapse-arrow {
  font-size: 9px;
  color: var(--txt-dim);
  transition: transform 0.15s;
  flex-shrink: 0;
  padding-left: 4px;
  transform: rotate(-180deg);
}

html[dir="rtl"] .v1-collapse-arrow {
  padding-left: 0;
  padding-right: 4px;
}
.v1-tool-list li.v1-collapsed .v1-collapse-arrow {
  transform: rotate(0deg);
}
.v1-section-body {
  overflow: hidden;
}
.v1-tool-list li.v1-collapsed .v1-section-body {
  display: none;
}

/* ip-scanner tool: scanner actions/detect UI/port presets/IP library/
   IP extractor/range history/octet inputs moved to
   css/new-ui/tools/ip-scanner/{scanner-actions,scanner-sidebar-controls}.css */

.v1-tool-list .v1-section-header + .v1-section-body {
  margin-top: 6px;
}

.v1-sidebar.collapsed,
.v1-rightbar.collapsed {
  grid-template-rows: 34px 0;
  overflow: hidden;
}

.v1-rightbar.collapsed {
  background: var(--v1-tabs-strip-bg);
}

.v1-sidebar.collapsed .v1-left-content,
.v1-rightbar.collapsed .v1-right-content {
  display: none;
}

.v1-sidebar.collapsed .v1-sidebar-tool-tabs,
.v1-rightbar.collapsed .v1-right-tabs {
  display: none;
}

.v1-sidebar.collapsed .v1-side-head {
  justify-content: flex-end;
  padding: 0 4px 0 0;
}

html[dir="rtl"] .v1-sidebar.collapsed .v1-side-head {
  padding: 0 0 0 4px;
}

.v1-rightbar.collapsed .v1-side-head {
  justify-content: flex-start;
  padding: 0 0 0 6px;
}

html[dir="rtl"] .v1-rightbar.collapsed .v1-side-head {
  padding: 0 6px 0 0;
}

.v1-rightbar.collapsed .v1-panel-toggle {
  margin-left: 0;
}

.v1-sidebar.collapsed .v1-head-title,
.v1-rightbar.collapsed .v1-head-title {
  display: none;
}

.v1-editor {
  min-width: 0;
  display: grid;
  grid-template-rows: 34px minmax(120px, 1fr) 6px 200px;
  background: var(--bg-elev);
  min-height: 0;
  overflow: hidden;
  position: relative;
}

.v1-editor.no-center-tabs {
  grid-template-rows: 0 minmax(120px, 1fr) 6px 200px;
}

.v1-editor.no-center-tabs .v1-content {
  background: var(--v1-empty-state-bg);
}

/* .v1-editor.console-collapsed itself (grid-template-rows, .v1-resizer-h,
   .v1-console-body display:none) is defined in components/console.css,
   which loads after this file and fully supersedes an identical trio of
   rules that used to live here too - two copies with different
   grid-template-rows values is exactly how the tab row silently reverted
   to the old stale height (38px) whenever the console was collapsed,
   even after this file's copy was "fixed". Removed rather than kept in
   sync, to not re-create that trap. */

.v1-resizer-h {
  background: var(--bg-elev);
  border-top: 0;
  border-bottom: 0;
  cursor: row-resize;
}

.v1-resizer-h:hover,
.v1-resizer-h.dragging {
  background: var(--activity);
}

.v1-tabs-shell {
  display: flex;
  align-items: stretch;
  min-width: 0;
  background: var(--v1-tabs-strip-bg);
  position: relative;
}

.v1-tabs-nav {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 22px;
  z-index: 2;
  border: 0;
  background: var(--v1-tabs-scroll-overlay-bg);
  color: #aeb2b9;
  font-family: var(--font-ui);
  font-size: 13px;
  cursor: pointer;
  padding: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms ease, background-color 120ms ease, color 120ms ease;
}

#v1TabsScrollLeft {
  left: 0;
}

#v1TabsScrollRight {
  right: 0;
}

.v1-tabs-shell.has-overflow .v1-tabs-nav:not(:disabled) {
  opacity: 0.92;
  pointer-events: auto;
}

.v1-tabs-nav:hover:not(:disabled) {
  background: #2f3338;
  color: #ffffff;
}

.v1-tabs-nav:disabled {
  opacity: 0;
  pointer-events: none;
  cursor: default;
}

.v1-tabs-shell.has-overflow .v1-tabs-nav:disabled {
  opacity: 0.35;
}

.v1-tabs {
  display: flex;
  flex: 1 1 auto;
  min-width: 0;
  overflow-x: auto;
  overflow-y: hidden;
  border-bottom: 0;
  background: var(--v1-tabs-strip-bg);
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.v1-tabs::-webkit-scrollbar {
  width: 0;
  height: 0;
  display: none;
}

.v1-tab {
  border: 0;
  border-right: 0;
  background: var(--bg-elev);
  color: var(--txt-dim);
  padding: 7px 10px;
  font-size: 12px;
  cursor: pointer;
  font-family: var(--font-ui);
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: 240px;
  flex: 0 0 auto;
}

.v1-tab-icon {
  width: 14px;
  text-align: center;
  opacity: 0.95;
  flex: 0 0 auto;
}

.v1-tab-icon .v1-ext-icon-img {
  width: 14px;
  height: 14px;
  vertical-align: middle;
}

.v1-tab-title {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.v1-tab-close {
  display: none;
  margin-left: 2px;
  width: 16px;
  height: 16px;
  align-items: center;
  justify-content: center;
  border: 1px solid transparent;
  color: #c6c9d0;
  font-size: 12px;
  line-height: 1;
  flex: 0 0 auto;
}

.v1-tab-popout {
  display: none;
  margin-left: 2px;
  width: 16px;
  height: 16px;
  align-items: center;
  justify-content: center;
  border: 1px solid transparent;
  color: #c6c9d0;
  font-size: 11px;
  line-height: 1;
  flex: 0 0 auto;
}

.v1-tab-popout:hover {
  border-color: #4a4f58;
  background: #2c3139;
  color: #ffffff;
}

.v1-tab-close:hover {
  border-color: #4a4f58;
  background: #2c3139;
  color: #ffffff;
}

.v1-tab.active .v1-tab-popout,
.v1-tab .v1-tab-popout.is-detached {
  display: inline-flex;
}

.v1-tab.active .v1-tab-close {
  display: inline-flex;
}

.v1-tab.tab-closed {
  display: none;
}

.v1-tab.tab-detached-hidden {
  display: none !important;
}

/* A real border-top instead of an inset box-shadow: on a flex-sized tab
   whose right edge lands on a fractional pixel, Chromium's box-shadow
   rasterizer left a faint 1px vertical seam bleeding down both the left
   and right edges of the active tab (worse/more visible on one side than
   the other) instead of staying a clean horizontal strip. border-top
   paints only the top edge, no side bleed. padding-top drops 7px -> 5px
   to keep content vertically aligned with inactive tabs (border is inside
   the border-box, same as box-shadow was). */
.v1-tab.active {
  color: var(--txt);
  background: var(--bg-elev);
  border-top: 2px solid var(--brand);
  padding-top: 5px;
}

.v1-tab:hover:not(.active) {
  color: #ffffff;
  background: #222326;
}

.v1-content {
  overflow: hidden;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
  min-width: 0;
  min-height: 0;
  height: 100%;
  position: relative;
  background: var(--bg-elev);
}

.v1-empty-state {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  background: var(--v1-empty-state-bg);
  display: flex;
  justify-content: center;
  align-items: center;
  pointer-events: none;
  z-index: 0;
}

.v1-empty-logo {
  position: absolute;
  inset: 0;
  margin: auto;
  width: min(320px, 62vw);
  max-width: 100%;
  height: auto;
  opacity: var(--v1-empty-logo-opacity);
  filter: var(--v1-empty-logo-filter);
}

.v1-session-welcome {
  position: relative;
  z-index: 1;
  pointer-events: auto;
  width: min(480px, 90vw);
  max-height: 80%;
  display: grid;
  gap: 10px;
  padding: 16px;
  border: none;
  background: none;
}

.v1-session-welcome h3 {
  margin: 0;
  color: var(--txt);
  font: 600 13px/1.2 var(--font-ui);
}

.v1-session-welcome-list {
  display: grid;
  gap: 6px;
  max-height: 240px;
  overflow-y: auto;
  min-width: 0;
}

.v1-session-welcome-empty {
  margin: 0;
  color: #9aa3b2;
  font: 400 12px/1.4 var(--font-ui);
}

#v1MainCard {
  position: relative;
  z-index: 1;
}

.v1-detached-tool-card {
  position: fixed;
  top: 64px;
  left: 96px;
  width: min(980px, calc(100vw - 160px));
  height: min(72vh, calc(100vh - 120px));
  z-index: 70;
  border: 1px solid #3c414a;
  box-shadow: 0 20px 44px rgba(0, 0, 0, 0.55);
  resize: both;
  overflow: hidden;
  display: grid;
  grid-template-rows: 34px minmax(0, 1fr);
  padding: 0;
}

.v1-detached-tool-card.is-dragging {
  box-shadow: 0 24px 54px rgba(0, 0, 0, 0.62);
}

.v1-detached-tool-card.is-dragging .v1-detached-tool-body {
  overflow: hidden;
}

.v1-detached-tool-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  border-bottom: 1px solid #30343b;
  background: #252a31;
  padding: 6px 8px;
  cursor: move;
  user-select: none;
}

.v1-detached-tool-title {
  flex: 1;
  font-size: 12px;
  color: var(--txt);
  font-family: var(--font-ui);
  font-weight: 600;
}

.v1-detached-tool-dock {
  width: 18px;
  height: 18px;
  border: 1px solid #4a4f58;
  background: #2c3139;
  color: #d6dce8;
  cursor: pointer;
  line-height: 1;
  font-size: 11px;
  padding: 0;
}

.v1-detached-tool-dock:hover {
  background: #39424f;
  color: #ffffff;
}

.v1-detached-tool-close {
  width: 18px;
  height: 18px;
  border: 1px solid #4a4f58;
  background: #2c3139;
  color: #d6dce8;
  cursor: pointer;
  line-height: 1;
  font-size: 13px;
  padding: 0;
}

.v1-detached-tool-close:hover {
  background: #5c2d34;
  border-color: #8a3f49;
  color: #ffffff;
}

.v1-detached-tool-swap {
  width: 18px;
  height: 18px;
  border: 1px solid #4a4f58;
  background: #2c3139;
  color: #d6dce8;
  cursor: pointer;
  line-height: 1;
  font-size: 13px;
  padding: 0;
}

.v1-detached-tool-swap:hover {
  background: #39424f;
  color: #ffffff;
}

.v1-detached-tool-card.is-swap-source {
  outline: 2px solid #4d9fff;
  outline-offset: -2px;
}

.v1-detached-tool-card.is-swap-source .v1-detached-tool-swap {
  background: #1e4a8a;
  color: #7bbfff;
  border-color: #4d9fff;
}

.v1-detached-tool-body {
  min-width: 0;
  min-height: 0;
  overflow: auto;
  padding: 10px;
}

.v1-rightbar {
  border-left: 0;
  background: var(--bg-elev);
  min-height: 0;
  display: grid;
  grid-template-rows: 34px 1fr;
  overflow: hidden;
}

.v1-rightbar .v1-side-head {
  background: var(--v1-tabs-strip-bg);
}

