/* vacuum online: a full-width app below the site header. Controls are Cowboy
   Components and Shoelace, styled by Cowboy's shared CSS (vacuum-online.bundle.js
   adopts button, forms and scrollbar styles into the page). This
   file holds the app's frame and the few layout rules those components need,
   following the doctor's own patterns. */
.vacuum-online-page {
  overflow: hidden;
}

.vo-app {
  --vo-bg: var(--background-color, #0d1117);
  --vo-fg: var(--font-color, #e8e9ed);
  --vo-sub: var(--font-color-sub1, #a7a7a7);
  --vo-primary: var(--primary-color, #62c4ff);
  --vo-secondary: var(--secondary-color, #f83aff);
  --vo-terminal: #05070a;
  --vo-gutter: 24px;
  /* section headings and the terminal bar share one height, so their dashed
     lines run as a single line across the app */
  --vo-bar: 48px;
  /* pb33f's Shoelace theme (cowboy-components/css/pb33f-theme.css), which this
     site does not load globally */
  --sl-color-primary-50: rgba(234, 246, 255, 0.13);
  --sl-color-primary-100: rgba(211, 240, 251, 0.22);
  --sl-color-primary-200: rgba(189, 224, 250, 0.4);
  --sl-color-primary-300: rgba(156, 210, 250, 0.65);
  --sl-color-primary-400: rgba(131, 203, 250, 0.68);
  --sl-color-primary-500: rgba(123, 209, 253, 0.86);
  --sl-color-primary-600: var(--vo-primary);
  --sl-color-primary-700: rgb(81, 189, 251);
  --sl-color-primary-800: rgb(53, 184, 253);
  --sl-color-primary-900: rgb(19, 164, 251);
  --sl-input-font-family: var(--font-stack), monospace;
  --sl-font-sans: var(--font-stack), monospace;
  /* the site pads and sizes every <main>; the app owns its whole box */
  position: fixed;
  inset: var(--vo-top, 57px) 0 0 0;
  box-sizing: border-box;
  height: auto;
  min-height: 0;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  background: var(--vo-bg);
  color: var(--vo-fg);
  font-family: var(--font-stack, BerkeleyMono-Regular, "Roboto Mono", Menlo, monospace);
  font-size: 15px;
  line-height: 1.5;
}

/* the site styles prose lists; these lists are layout */
.vo-app ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.vo-app li::before {
  content: none !important;
}
.vo-app a {
  color: var(--vo-primary);
}
/* help text as the doctor's settings dialog writes it */
.vo-app small {
  display: block;
  color: var(--vo-sub);
  font-size: 0.7rem;
}

.vo-controls,
.vo-scroll,
.vo-dock,
.vo-terminal-pane {
  box-sizing: border-box;
}
/* xterm's fit addon measures the terminal box's computed size: content-box keeps
   that to the visible area, so the padding never adds rows or columns that run
   off the bottom or the right */
.vo-terminal {
  box-sizing: content-box;
}

/* ---- controls ------------------------------------------------------------- */

/* the doctor's split panel: Cowboy's panels.css draws its pink divider and grip */
.vo-split {
  min-height: 0;
  --min: 260px;
  --max: calc(100% - 320px);
}
.vo-split .divider-vert {
  left: -4px;
}

.vo-controls {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
}
.vo-section[hidden] {
  display: none;
}
.vo-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding-bottom: 28px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Section content keeps the gutter; each heading's divider runs the full width
   of the column, edge to edge, like the dock's. */
.vo-section {
  padding: 0 var(--vo-gutter);
}
.vo-section h2 {
  display: flex;
  align-items: center;
  box-sizing: border-box;
  height: var(--vo-bar);
  margin: 0 calc(-1 * var(--vo-gutter)) 16px;
  padding: 0 var(--vo-gutter);
  border-bottom: 1px dashed var(--vo-secondary);
  font-family: var(--font-stack-bold, inherit), monospace;
  font-size: 0.9rem;
  font-weight: normal;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--vo-secondary);
}
/* after the first, each heading also has a dashed rule above it, edge to edge */
.vo-section:not(:first-child) h2 {
  border-top: 1px dashed var(--vo-secondary);
}
/* dropdown labels (Sample, Category) stand a little off their boxes */
.vo-app sl-select::part(form-control-label) {
  margin-bottom: 8px;
}

/* specs: the upload dialog's drop box and entry-document list */
.vo-drop {
  padding: 20px;
  border: 1px dotted var(--tertiary-color, #a3abba);
  color: var(--vo-sub);
  text-align: center;
}
.vo-drop.vo-over {
  border-color: var(--vo-primary);
  color: var(--vo-primary);
}
/* Open files and View spec are secondary: one size, a third
   of the column, on the right, so RUN stays the one big action */
.vo-side {
  display: block;
  width: 33.333%;
  min-width: max-content;
  margin-top: 8px;
  margin-left: auto;
}
.vo-side::part(base) {
  width: 100%;
  margin-top: 0;
}
.vo-drop small {
  padding-top: 4px;
}
.vo-file-list {
  margin-top: 14px !important;
}
.vo-file-list[hidden],
.vo-file-tree[hidden],
.vo-samples[hidden] {
  display: none;
}
/* the sample picker and the rolodex tree sit where the file list would */
.vo-samples,
.vo-file-tree {
  display: block;
  margin-top: 14px;
}
.vo-samples small {
  color: var(--vo-sub);
  font-size: 0.7rem;
}
/* sl-select gives the highlighted option tabindex 0 and paints it light blue;
   its icon and type turn solid black to stay readable on it */
.vo-samples sl-option[tabindex="0"] small {
  color: #000;
}
.vo-samples sl-option[tabindex="0"] pb33f-model-icon {
  filter: brightness(0);
}
.vo-side[hidden],
.vo-own[hidden] {
  display: none;
}
/* each input's first choice, a sample or your own, lined up like the command list */
.vo-source {
  margin: 0 -10px 14px;
}
/* your own files: the drop box, Open, and what you've opened */
.vo-own {
  display: block;
}

/* the spec drawer, as the doctor's details drawer draws it */
.vo-spec-drawer::part(panel) {
  border-left: 1px dashed var(--vo-secondary);
  background: var(--vo-bg);
}
.vo-spec-drawer::part(header) {
  border-bottom: 1px dashed var(--vo-secondary);
}
.vo-spec-drawer::part(title) {
  padding: 10px;
  font-family: var(--font-stack-bold, inherit), monospace;
  font-size: 0.9rem;
  letter-spacing: 0.05em;
  color: var(--vo-primary);
}
.vo-spec-drawer::part(body) {
  padding: 0;
  overflow: hidden;
}
.vo-spec-drawer::part(footer) {
  border-top: 1px dashed var(--vo-secondary);
  padding: 8px var(--vo-gutter);
}
.vo-spec-viewer {
  height: 100%;
  font-size: 0.85rem;
}
.vo-spec-viewer[hidden] {
  display: none;
}
/* the editor fills the drawer (pb33f-editor renders into the page, not a shadow root) */
.vo-spec-editor {
  display: block;
  height: 100%;
}
.vo-file-list li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 8px;
  padding: 4px 8px;
}
.vo-file-list li.active {
  background: var(--primary-color-verylowalpha, rgba(98, 196, 255, 0.08));
}
.vo-file-list li.vo-empty {
  display: block;
  color: var(--vo-sub);
}
.vo-file-list .name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--vo-primary);
  font-family: var(--font-stack-bold, inherit), monospace;
  text-decoration: none;
}
.vo-file-list .not-spec {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--vo-secondary);
}
.vo-file-list .size {
  color: var(--vo-sub);
  font-size: 0.8rem;
}

/* command: Cowboy's choice list, its names lined up with the column's gutter */
.vo-commands {
  margin: 0 -10px;
}

/* options: dropdowns, then small switches with help text, then hard mode */
.vo-options {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.vo-switch sl-switch::part(label) {
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.vo-switch small {
  padding-top: 4px;
}

/* ---- dock: the way to the CLI ---------------------------------------------------- */

.vo-dock {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 18px var(--vo-gutter) 20px;
  border-top: 1px dashed var(--vo-secondary);
}
/* RUN, between the command and the specs; the gutter leaves room for its sparks */
.vo-run-row {
  padding: 6px var(--vo-gutter) 10px;
}
.vo-run {
  display: block;
}

/* ---- terminal ------------------------------------------------------------- */

.vo-terminal-pane {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}
/* below the idle screen (z-index 2), so it only shows over output */
.vo-clear[hidden] {
  display: none;
}
.vo-clear {
  position: absolute;
  top: 12px;
  right: var(--vo-gutter);
  z-index: 1;
}
/* where vacuum renders: on the page background, not a black cutout */
.vo-terminal {
  flex: 1;
  min-height: 0;
  /* the top padding clears the floating CLEAR button */
  padding: 40px 8px 8px var(--vo-gutter);
  background: var(--vo-bg);
}

/* output files: icon, full name and the doctor's slim download buttons */
.vo-tray {
  flex: none;
  max-height: 42%;
  overflow-x: hidden;
  overflow-y: auto;
  border-bottom: 1px dashed var(--vo-secondary);
}
.vo-tray[hidden] {
  display: none;
}
.vo-tray-files li {
  overflow: hidden;
}
.vo-output-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 18px;
  padding: 18px var(--vo-gutter);
  animation: vo-arrive 0.5s cubic-bezier(0.2, 1.4, 0.4, 1) var(--arrive, 0ms) both;
}
/* each output powers on as it arrives, like the screens do */
@keyframes vo-arrive {
  0% { opacity: 0; transform: translateX(-14px) scaleY(0.15); }
  55% { opacity: 1; transform: translateX(3px) scaleY(1.06); }
  100% { opacity: 1; transform: none; }
}
/* the icon is big and pops in; pb33f-model-icon sizes itself inside its shadow
   root, so this box holds the space and the icon is scaled up within it */
.vo-output-icon {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  animation: vo-pop 0.6s cubic-bezier(0.2, 1.8, 0.4, 1) calc(var(--arrive, 0ms) + 120ms) both;
}
.vo-output-icon pb33f-model-icon {
  transform: scale(1.7);
  filter: drop-shadow(0 0 5px color-mix(in srgb, var(--vo-secondary) 70%, transparent));
}
@keyframes vo-pop {
  0% { transform: scale(0.3) rotate(-12deg); opacity: 0; }
  60% { transform: scale(1.25) rotate(3deg); opacity: 1; }
  100% { transform: none; opacity: 1; }
}
.vo-tray-files li + li {
  border-top: 1px dotted var(--vo-secondary);
}
.vo-output-name .name {
  position: relative;
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  word-break: break-all;
  font-size: 1.1rem;
  color: var(--vo-primary);
  font-family: var(--font-stack-bold, inherit), monospace;
  text-shadow: 0 0 10px color-mix(in srgb, var(--vo-primary) 65%, transparent),
    0 0 22px color-mix(in srgb, var(--vo-secondary) 35%, transparent);
}
/* a sheen keeps passing over the name; it moves by transform alone, so it costs
   the compositor and not a repaint */
.vo-output-name .name::after {
  content: '';
  position: absolute;
  inset: 0;
  width: 300%;
  background: linear-gradient(100deg, transparent 44%, rgba(255, 255, 255, 0.55) 50%, transparent 56%);
  transform: translateX(-66.7%);
  animation: vo-name-sheen 4s ease-in-out 1s infinite;
  pointer-events: none;
}
@keyframes vo-name-sheen {
  0%, 60% { transform: translateX(-66.7%); }
  90%, 100% { transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce) {
  .vo-output-row,
  .vo-output-icon,
  .vo-output-name .name::after {
    animation: none;
  }
}
.vo-output-actions {
  display: flex;
  gap: 0.5rem;
}
/* Open and Save match Open files and View spec: Shoelace's small size, with the
   icon and label as one centred group, evenly padded */
.vo-output-actions sl-button::part(base) {
  align-items: center;
  margin-top: 0;
  padding: 0 14px;
}
.vo-output-actions sl-button::part(prefix) {
  font-size: 0.8rem;
}
.vo-output-actions sl-button::part(label) {
  padding: 0 0 0 6px;
}

/* ---- idle screen: the scene behind the command RUN will start --------------- */

.vo-terminal-stage {
  position: relative;
  flex: 1;
  min-width: 0;
  min-height: 0;
  display: flex;
}
/* The terminal's box sizes the terminal, never the reverse. Without min-width: 0
   a fit that overshoots by a column widens the box, which refits wider, forever. */
.vo-terminal-stage > .vo-terminal {
  flex: 1;
  min-width: 0;
  overflow: hidden;
}

.vo-idle {
  position: absolute;
  inset: 0;
  z-index: 2;
  overflow: hidden;
  background: var(--vo-terminal);
}
.vo-idle[hidden] {
  display: none;
}
.vo-idle-canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}
.vo-idle-static {
  background: radial-gradient(ellipse at 50% 55%, rgba(248, 58, 255, 0.16), transparent 55%),
    radial-gradient(ellipse at 50% 100%, rgba(98, 196, 255, 0.18), transparent 60%), var(--vo-terminal);
}
.vo-idle-static .vo-idle-canvas {
  display: none;
}
.vo-idle-over::after {
  content: "drop to load";
  position: absolute;
  inset: 16px;
  z-index: 1;
  display: grid;
  place-items: center;
  border: 1px dashed var(--vo-primary);
  color: var(--vo-primary);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.vo-idle-hud {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
/* The scene's horizon sits 4% above the middle (HORIZON in scene.js), so the
   command is centred on 46%. */
.vo-idle-center {
  position: absolute;
  top: 46%;
  left: 50%;
  width: min(820px, calc(100% - 2 * var(--vo-gutter)));
  transform: translate(-50%, -50%);
  pointer-events: auto;
}
/* RUN spans the command line's full width, large and tucked right under it. */
.vo-idle-actions {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  right: 0;
}
.vo-idle-command {
  margin-bottom: 0;
  /* keeps the wrapped command clear of the COPY button */
  padding-right: 84px;
}
.vo-idle-command-text {
  white-space: normal;
}
.vo-idle-arg {
  white-space: nowrap;
}
.vo-idle-run {
  display: block;
}

/* the same CRT power-on and power-off the loader uses */
.vo-idle-on {
  animation: vo-crt-on 0.38s ease-out both;
}
/* RUN waits for this while the grid warps behind it (idle.js OFF_MS, 200ms) */
.vo-idle-off {
  animation: vo-crt-off 0.2s ease-in both;
}
@keyframes vo-crt-off {
  0% { transform: scale(1, 1); filter: brightness(1); }
  60% { transform: scale(1, 0.004); filter: brightness(3); }
  100% { transform: scale(0, 0.004); filter: brightness(4); opacity: 0; }
}
@keyframes vo-crt-on {
  0% { transform: scale(0, 0.004); filter: brightness(4); opacity: 0.6; }
  45% { transform: scale(1, 0.004); filter: brightness(3); }
  100% { transform: scale(1, 1); filter: brightness(1); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .vo-idle-on,
  .vo-idle-off {
    animation: none;
  }
}

@media (max-width: 860px) {
  .vo-app {
    --vo-gutter: 16px;
    overflow-y: auto;
  }
  /* stacked on narrow screens: no divider to drag, the page scrolls instead */
  .vo-split {
    display: flex !important;
    flex-direction: column;
    height: auto;
  }
  .vo-split::part(divider) {
    display: none;
  }
  .vo-terminal-pane {
    min-height: 70vh;
  }
  .vo-controls {
    min-height: auto;
    border-bottom: 1px dashed var(--vo-secondary);
  }
  /* stacked, the controls take their natural height and the page scrolls */
  .vo-scroll {
    flex: none;
    overflow: visible;
  }
  .vo-output-row {
    grid-template-columns: auto minmax(0, 1fr);
  }
  .vo-output-actions {
    grid-column: 1 / -1;
  }
}

/* ---- fault: vacuum couldn't read its input (fault.js) ------------------------- */

/* the structural lines, the divider and Cowboy's pink accents all go red */
.vo-app.vo-fault {
  --vo-secondary: #ff3c74;
  --secondary-color: #ff3c74;
}
.vo-fault-canvas {
  position: absolute;
  inset: 0;
  z-index: 60;
  pointer-events: none;
}
.vo-fault-canvas[hidden] {
  display: none;
}
/* the hit: a hard shake that dies away */
.vo-app.vo-shake .vo-split {
  animation: vo-shake 0.55s cubic-bezier(0.36, 0.07, 0.19, 0.97);
}
@keyframes vo-shake {
  0%, 100% { transform: none; }
  10% { transform: translate(-9px, 3px) rotate(-0.3deg); }
  20% { transform: translate(8px, -4px) rotate(0.3deg); }
  30% { transform: translate(-7px, 2px); }
  40% { transform: translate(6px, -2px); }
  55% { transform: translate(-4px, 1px); }
  70% { transform: translate(3px, -1px); }
  85% { transform: translate(-1px, 0); }
}
