html,
body {
  height: 100%;
  margin: 0;
  background-color: var(--surface-bg);
  font-family: var(--font-ui);
}

button, input, select, textarea, optgroup {
  font-family: inherit;
}

body {
  display: flex;
  overflow: hidden;
}

/* Design tokens (surfaces, geometry, scene) AND the backend-mode accent classes
   (.theme-standard / .theme-ase / .theme-symmetry / .theme-locked-ase) live in
   themes/default/theme.css, which the loader loads first; a theme override can
   redefine any of them. */

#ui {
  height: 98dvh;
  min-width: var(--ui-width);
  width:var(--ui-width);
  max-width:var(--ui-width);
  overflow-y: auto;
overflow-x: hidden;
  /* max() rather than a flat var(--ui-padding): on a rectangular screen
     env() is 0 so this is exactly --ui-padding, unchanged; on a notched
     phone (mobile rung makes this panel full-bleed — see responsive.css)
     it grows just enough to clear the notch/home-indicator instead of
     rendering content under it. Applied unconditionally since it costs
     nothing where there's no cutout to clear. */
  padding: max(var(--ui-padding), env(safe-area-inset-top))
           max(var(--ui-padding), env(safe-area-inset-right))
           max(var(--ui-padding), env(safe-area-inset-bottom))
           max(var(--ui-padding), env(safe-area-inset-left));
  background: var(--panel-bg);
  color: var(--panel-fg);
  backdrop-filter: blur(var(--panel-blur));
  border-right: var(--ui-border) solid var(--panel-border);
}

/* Desktop panel hide state */
#ui.panel-hidden {
  display: none;
}

/*------*/
.rotation-axis {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3-5);
  align-items: center;
}

.angle-controls {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.angle-buttons {
  display: flex;
  gap: var(--sp-3-5);
}

.angle-btn {
  font-size: var(--fs-xs);
  padding: 5px var(--sp-3-5);
  cursor: pointer;
  background:var(--popup-bg);
  color:var(--muted-fg);
  border-radius: var(--radius-md);
  border: 1px solid var(--line-grey);
}
.angle-btn:hover {
    transform: scale(1.05);
    border: 1px solid var(--fg-1);
}

.angle-input {
  width: 40px;
  text-align: center;
  padding: 2px;
  font-size: var(--fs-base);
  border: 1px solid var(--ink-3);
  border-radius: var(--radius-5);
  background-color: var(--input-bg);
  color:var(--muted-fg);
}
/*------*/


/* When panel hidden: disable flex and make view fullscreen */
body.panel-hidden {
  display: block;
  --gizmo-left: 20px;
}

body.panel-hidden #view {
  position: fixed;
  top: 0;
  left: 0;
  width: 100dvw;
  height: 100dvh;
  border-radius: 0;
}

body.panel-hidden #view canvas {
  width: 100% !important;
  height: 100% !important;
}


/* === Panel Hidden Overrides === */
body.panel-hidden #axesLegend {
  left: calc(var(--gizmo-left) + var(--gizmo-size) + 8px);  /* Stick to gizmo */
}

.background-dot {
  position: fixed;
  /* Same size + right-edge as the compact toolbar icons (compactAnchor
     right:20). --compact-stack-bottom is published by PanelManager's
     applyCompactPositions(): the dot drops below the compact icon stack when it
     grows past the dot's default resting spot. */
  width: 54px;
  height: 54px;
  border-radius: 50%;
  border: 2px solid var(--dot-border);
  cursor: grab;
  touch-action: none;
  top: max(120px, calc(var(--compact-stack-bottom, 0px) + 20px));
  /* --split-reserve is kept in sync with the side dock's pane width (see
     ui/panels/SideDock.js) so the dot stays clear of the pane instead of
     sliding underneath it. 0px when the dock is closed. */
  right: calc(20px + var(--split-reserve, 0px));
  z-index: var(--z-anchor);
}

.background-dot.background-dot-dragging {
  cursor: grabbing;
}

.popup {
  position: absolute;
  top: 120px;
  left: 120px;
  overflow: auto;
  background: var(--popup-bg);
  border: 1px solid var(--chrome-4);
  border-radius: var(--radius-12);
  box-shadow: 0px 0px 4px 2px  var(--overlay);
  z-index: var(--z-anchor);
  display: none;         /* initially hidden */
  user-select: none;     /* prevent text selection while dragging */
}
.popup table * {
  user-select: text !important;
}

.floating-comp-panel {
      position: absolute;
      top: 100px;
      left: 100px;
      background: var(--fg-1);
      border: 1px solid var(--ink-3);
      border-radius: var(--radius-md);
      box-shadow: 0px 0px 4px 2px  rgb(var(--shadow-rgb) / 0.2);
      cursor: move;
      padding: var(--sp-3-5);
      z-index: var(--z-comp-panel);

  #LatticeComparisonPopUp.show {
    display: block;
  }
  /* Chart container */
  #latticeCompPanel {
    width: 100%;
    height: 100%;
  }

}

.logo {
  margin: 0 0 25px 0;
  width: 200px;
  height: auto;
  display: block;
  margin: 1rem auto;
  cursor: pointer;
  transition: transform 0.2s ease;
}
.logo:hover{
  transform: scale(1.05);
  filter: drop-shadow(0 8px 25px var(--highlight-color));
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.control-group {
  margin: 0 0 var(--sp-5) 0;
  padding: 20px;
  background: var(--group-bg);
  border-radius: var(--radius-12);
  border: 1px solid var(--line-1);
  backdrop-filter: blur(10px);
}

.control-group h1 {
  margin: 0 0 var(--sp-5) 0;
  color: 	var(--warn);
  font-size: var(--fs-xl);
  font-weight: var(--fw-medium);
}

.control-group h3 {
  margin: 0 0 var(--sp-5) 0;
  font-size: var(--fs-xl);
  font-weight: var(--fw-medium);
}

/* File actions row (Files window): Upload / Paste Text / Download (+ Share,
   added once a structure loads). A wrapping flex row, not an equal-column
   grid: with a larger font scale or a narrow panel the four nowrap labels
   summed to more than the row, and equal tracks just let the labels run into
   each other (and Share past the edge). Buttons keep their natural width,
   share the leftover space, and drop onto a second line when they must. */
.file-actions {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(4px, 1.1111vw, var(--sp-3));
  margin-bottom: var(--sp-5);
  background: var(--wash-1);
  padding: var(--sp-2);
  border-radius: var(--radius-md);
}

.file-actions > * {
  flex: 1 0 auto;
}

.file-actions > .file-action-btn {
  width: auto; /* the base rule's 100% would be the flex basis — one per line */
}

.file-action-btn {
  width: 100%;
  background: var(--bg-color);
  color: var(--fg-1);
  border: 1px solid var(--line-1);
  padding: var(--sp-3-5) var(--sp-3);
  border-radius: var(--radius);
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.2s ease, box-shadow 0.2s ease;
}

/* Fluid below a 720px pivot: --fs-md and --sp-3 are the upper bounds, so
   nothing moves on a desktop-width sheet. (The mobile rung additionally lets
   the label wrap — see responsive.css.) The vw term carries the font scale
   too — otherwise a 2x user gets a button that stops short of 2x wherever
   the fluid middle wins. */
.file-actions .file-action-btn {
  padding: var(--sp-3-5) clamp(3px, 1.1111vw, var(--sp-3));
  font-size: clamp(calc(10px * var(--cv-font-scale, 1)), calc(1.8056vw * var(--cv-font-scale, 1)), var(--fs-md));
}

.file-action-btn:hover {
  box-shadow: 0px 0px 4px 2px var(--highlight-color);
}

/* Auto Range buttons (Forces/Spins/Atoms/Bonds/Planes panels): same look as
   the file-action-btn (Upload etc.) buttons, but sized to sit inline next to
   a Log Scale checkbox instead of stretching to fill the row. */
.cv-auto-range-btn {
  width: auto;
  padding: var(--sp-2) var(--sp-4);
  font-size: var(--fs-base);
  white-space: nowrap;
}

#fileInput {
  display: none;
}

/* Download format dropdown */
.download-dropdown {
  position: relative;
}

.download-menu {
  /* Default anchoring; StructureInputModule portals the menu to <body> and
     switches to position:fixed on open (see below). */
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  min-width: 160px;
  /* Body-portaled dropdown band. The menu is moved out of the Files dock panel
     on open — inside it, sibling dock panels (also at --z-dock 1200, later in
     the DOM) painted over the lower entries no matter the menu's own z-index.
     Literal fallback because --z-menu is only defined in the default theme. */
  z-index: var(--z-menu, 3000);
  background: var(--menu-bg);
  /* --menu-bg is only ~95% opaque in the default theme; the blur makes it read
     solid over the scene/panels so the entries aren't see-through. */
  backdrop-filter: blur(12px);
  border: 1px solid var(--wash-4);
  border-radius: var(--radius);
  padding: var(--sp-1);
  box-shadow: 0 4px 12px rgb(var(--shadow-rgb) / 0.4);
}

.download-menu button {
  display: block;
  width: 100%;
  background: none;
  border: none;
  color: var(--fg-1);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius-sm);
  font-size: var(--fs-md);
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
}

.download-menu button:hover {
  background: var(--hover-color);
}

/* Paste-text modal dialog */
#pasteTextModal {
  position: fixed;
  inset: 0;
  z-index: var(--z-dialog);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--overlay);
}

#pasteTextModal[hidden] {
  display: none;
}

.paste-modal {
  width: min(640px, calc(100vw - 32px));
  padding: var(--sp-5);
  background: var(--menu-bg);
  color: var(--panel-fg);
  border: 1px solid var(--wash-4);
  border-radius: var(--radius-12);
  box-shadow: 0 8px 24px var(--overlay);
  backdrop-filter: blur(10px);
}

.paste-modal-actions {
  display: flex;
  gap: var(--sp-3-5);
}

.paste-modal-actions #cancelTextButton {
  margin-top: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  background: var(--wash-2);
  color: var(--fg-1);
  border: none;
  border-radius: var(--radius);
  font-size: var(--fs-lg);
  cursor: pointer;
}

/* PNG image export modal dialog */
#pngExportModal {
  position: fixed;
  inset: 0;
  z-index: var(--z-dialog);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--overlay);
}

#pngExportModal[hidden] {
  display: none;
}

.png-export-modal {
  width: min(460px, calc(100vw - 32px));
  padding: var(--sp-5) 18px;
  background: var(--menu-bg);
  color: var(--panel-fg);
  border: 1px solid var(--wash-4);
  border-radius: var(--radius-12);
  box-shadow: 0 8px 24px var(--overlay);
  backdrop-filter: blur(10px);
}

.png-export-modal h3 {
  margin: 0 0 var(--sp-4) 0;
  font-size: var(--fs-xl);
  font-weight: var(--fw-bold);
}

.png-row {
  display: flex;
  gap: var(--sp-4);
  margin-bottom: var(--sp-4);
}

.png-row > label {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  font-size: var(--fs-md);
}

.png-row label.png-check {
  flex-direction: row;
  align-items: center;
  gap: var(--sp-3);
}

/* [hidden] alone loses to .png-row's display:flex — the SVG-only "Structure
   as" row needs this to actually disappear in PNG mode. */
.png-row[hidden] {
  display: none;
}

/* Rows holding one full-width control (Format, Structure as): keep the
   control the same width as one column of the two-up rows above it. */
.png-row-single > label {
  flex: 0 1 100%;
}

.png-row input[type="number"],
.png-row select {
  width: 100%;
  padding: var(--sp-3) var(--sp-3-5);
  border-radius: var(--radius);
  border: 1px solid var(--line-1);
  background: var(--overlay-20);
  color: var(--ink-6);
  font-size: var(--fs-md);
  box-sizing: border-box;
}

.png-row label.png-check input {
  width: auto;
}

.png-note {
  margin: 0 0 var(--sp-4) 0;
  font-size: var(--fs-sm);
  opacity: 0.7;
  line-height: var(--lh-normal);
}

/* The two primary (green) actions: Save (direct programmatic export of the
   full view) and Choose region… (the crop-overlay flow). */
.png-export-modal .paste-modal-actions .png-primary {
  flex: 1;
  margin-top: var(--sp-1);
  padding: var(--sp-4) var(--sp-5);
  background: var(--bg-color); /* same fill as the toolbar buttons; the glow is the hover */
  color: var(--fg-1);
  border: none;
  border-radius: var(--radius);
  font-size: var(--fs-lg);
  font-weight: var(--fw-medium);
  cursor: pointer;
}

.png-export-modal .paste-modal-actions .png-primary:hover {
  box-shadow: 0 0 4px 2px var(--highlight-color);
}

.png-export-modal .paste-modal-actions .png-primary:disabled {
  opacity: 0.6;
  cursor: default;
  box-shadow: none;
}

.png-export-modal .paste-modal-actions #pngCancelBtn {
  margin-top: var(--sp-1);
  padding: var(--sp-4) var(--sp-5);
  background: var(--wash-2);
  color: var(--fg-1);
  border: none;
  border-radius: var(--radius);
  font-size: var(--fs-lg);
  cursor: pointer;
}

/* First-run ray/path-tracing performance warning (reuses .png-export-modal
 * card + .paste-modal-actions button row). */
#raytraceWarningModal {
  position: fixed;
  inset: 0;
  z-index: var(--z-dialog);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--overlay);
}

/* "Quantum ESPRESSO input" copy dialog (reuses the .png-export-modal card).
 * A note plus a read-only monospace textarea of the structural cards. */
#qeInputModal {
  position: fixed;
  inset: 0;
  z-index: var(--z-dialog);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--overlay);
}

#qeInputModal[hidden] {
  display: none;
}

.qe-input-modal {
  width: min(620px, calc(100vw - 32px));
}

.qe-input-modal .qe-input-note {
  margin: 0 0 var(--sp-4) 0;
  font-size: var(--fs-sm);
  opacity: 0.8;
  line-height: var(--lh-normal);
}

.qe-input-modal .qe-input-note code {
  font-size: var(--fs-xs);
  padding: 0 var(--sp-1);
  border-radius: var(--radius-3);
  background: var(--overlay-20);
}

.qe-input-modal .qe-input-text {
  width: 100%;
  box-sizing: border-box;
  margin: 0 0 var(--sp-4) 0;
  padding: var(--sp-3) var(--sp-3-5);
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
  color: var(--ink-6);
  background: var(--overlay-20);
  border: 1px solid var(--line-1);
  border-radius: var(--radius);
  white-space: pre;
  overflow: auto;
  resize: vertical;
}

/* "Export CIF with symmetry" dialog (reuses the .png-export-modal card and the
 * Symmetry panel's .sym-kv / .sym-mono for the preview). Tolerance box, live
 * preview, a ladder of tolerances, then the action row. */
#cifExportModal {
  position: fixed;
  inset: 0;
  z-index: var(--z-dialog);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--overlay);
}

#cifExportModal[hidden] {
  display: none;
}

.cif-export-modal {
  width: min(560px, calc(100vw - 32px));
}

.cif-export-modal .cif-export-note {
  margin: 0 0 var(--sp-4) 0;
  font-size: var(--fs-sm);
  opacity: 0.8;
  line-height: var(--lh-normal);
}

.cif-export-modal .cif-export-row {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  flex-wrap: wrap;
  margin-bottom: var(--sp-4);
}

.cif-export-modal .cif-export-row label {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  font-size: var(--fs-base);
  color: var(--fg-75);
}

.cif-export-modal .cif-export-row input[type="number"] {
  width: 6.5em;
  text-align: center;
  border: 1px solid var(--line-1);
  background: var(--input-bg);
  color: var(--fg-1);
  border-radius: var(--radius);
  padding: 5px var(--sp-3);
  font-size: var(--fs-md);
  box-sizing: border-box;
}

.cif-export-modal .cif-export-lock {
  font-size: var(--fs-sm);
  color: var(--fg-50);
}

.cif-export-modal .cif-export-preview {
  justify-content: start;
  margin: 0 0 var(--sp-3) 0;
  padding: var(--sp-3) var(--sp-3-5);
  background: var(--overlay-20);
  border: 1px solid var(--line-1);
  border-radius: var(--radius);
}

.cif-export-modal .cif-export-status {
  margin: 0 0 var(--sp-3) 0;
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
  color: var(--fg-75);
}

.cif-export-modal .cif-export-status[hidden] {
  display: none;
}

.cif-export-modal .cif-export-status.cif-export-warn {
  color: var(--warn);
}

.cif-export-modal .cif-export-status.cif-export-error {
  color: var(--danger);
}

.cif-export-modal .cif-export-ladder {
  /* One row, four equal rungs; text inside may shrink but never wraps the row. */
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sp-2);
  margin: 0 0 var(--sp-3) 0;
}

.cif-export-modal .cif-export-ladder[hidden] {
  display: none;
}

.cif-export-modal .cif-export-ladder button {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-width: 0;
  padding: var(--sp-2) var(--sp-2);
  background: var(--wash-2);
  color: var(--fg-1);
  border: 1px solid var(--line-1);
  border-radius: var(--radius);
  font-size: var(--fs-xs);
  line-height: 1.25;
  cursor: pointer;
}

.cif-export-modal .cif-export-ladder button:hover {
  background: var(--wash-3);
}

.cif-export-modal .cif-export-ladder button.is-active {
  border-color: var(--accent-fg, var(--info-bright));
  background: var(--wash-3);
}

.cif-export-modal .cif-export-ladder button > span {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cif-export-modal .cif-ladder-tol,
.cif-export-modal .cif-ladder-sites {
  color: var(--fg-50);
}

.cif-export-modal .cif-ladder-res {
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
}

.cif-export-modal {
  /* A phone in landscape is shorter than this dialog: scroll the card itself
     rather than clipping the action row off the bottom. */
  max-height: calc(100vh - 24px);
  overflow-y: auto;
  box-sizing: border-box;
}

.cif-export-modal .paste-modal-actions button:not(.png-primary) {
  margin-top: var(--sp-1);
  padding: var(--sp-4) var(--sp-5);
  background: var(--wash-2);
  color: var(--fg-1);
  border: none;
  border-radius: var(--radius);
  font-size: var(--fs-lg);
  cursor: pointer;
}

.cif-export-modal .paste-modal-actions button:not(.png-primary):hover {
  box-shadow: 0 0 4px 2px var(--highlight-color); /* same glow as .png-primary */
}

/* General "file could not be loaded" warning (reuses the .png-export-modal
 * card). Shown for any failed structure load, whatever the format. */
#loadErrorModal {
  position: fixed;
  inset: 0;
  z-index: var(--z-dialog);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--overlay);
}

#loadErrorModal[hidden] {
  display: none;
}

.load-error-modal .load-error-message {
  margin: 0 0 var(--sp-4) 0;
  font-size: var(--fs-md);
  line-height: var(--lh-normal);
}

/* The technical reason: muted, monospace-ish, and wrapped so a long parser
   message never overflows the card. */
.load-error-modal .load-error-detail {
  margin: 0 0 var(--sp-4) 0;
  padding: var(--sp-3) var(--sp-3-5);
  font-size: var(--fs-sm);
  opacity: 0.75;
  line-height: var(--lh-normal);
  background: var(--overlay-20);
  border-radius: var(--radius);
  overflow-wrap: anywhere;
}

/* Warning tone (load-succeeded-with-a-caveat): a softer accent bar on the card
   so it reads as a notice rather than a hard failure. */
#loadErrorModal.load-modal-warning .load-error-modal {
  border-left: 3px solid var(--warn, var(--highlight-color));
}

#raytraceWarningModal[hidden] {
  display: none;
}

.rt-warning-modal .rt-warning-body {
  max-height: min(48dvh, 420px);
  overflow-y: auto;
  margin-bottom: var(--sp-4);
}

.rt-warning-modal .rt-warning-body p {
  margin: 0 0 var(--sp-3-5) 0;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
}

.rt-warning-modal .rt-warning-body p:last-child {
  margin-bottom: 0;
}

.rt-warning-check {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
  font-size: var(--fs-md);
  cursor: pointer;
}

.rt-warning-check input {
  width: auto;
}

.rt-warning-modal .paste-modal-actions #raytraceWarningOk {
  flex: 1;
  margin-top: var(--sp-1);
  padding: var(--sp-4) var(--sp-5);
  background: var(--bg-color);
  color: var(--fg-1);
  border: none;
  border-radius: var(--radius);
  font-size: var(--fs-lg);
  font-weight: var(--fw-medium);
  cursor: pointer;
}

.rt-warning-modal .paste-modal-actions #raytraceWarningOk:hover {
  box-shadow: 0 0 4px 2px var(--highlight-color);
}

.rt-warning-modal .paste-modal-actions #raytraceWarningCancel {
  margin-top: var(--sp-1);
  padding: var(--sp-4) var(--sp-5);
  background: var(--wash-2);
  color: var(--fg-1);
  border: none;
  border-radius: var(--radius);
  font-size: var(--fs-lg);
  cursor: pointer;
}

.rt-warning-modal .paste-modal-actions #raytraceWarningCancel[hidden] {
  display: none;
}

/* Share-link dialog: the URL plus its QR code (reuses the .png-export-modal
 * card + .paste-modal-actions button row). The QR's own colours live in the
 * generated SVG — it must stay black-on-white in every theme to scan. */
#shareLinkModal {
  position: fixed;
  inset: 0;
  z-index: var(--z-dialog);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--overlay);
}

#shareLinkModal[hidden] {
  display: none;
}

/* Password prompt for opening an encrypted ?e= link — same centered overlay
 * as the share dialog above (without it the card renders in normal flow,
 * pinned beside the side panel instead of over the canvas). */
#sharePasswordModal {
  position: fixed;
  inset: 0;
  z-index: var(--z-dialog);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--overlay);
}

#sharePasswordModal[hidden] {
  display: none;
}

.share-qr {
  display: flex;
  justify-content: center;
  margin-bottom: var(--sp-3-5);
}

.share-qr:empty {
  display: none;
}

/* Wider than the other dialogs on purpose: a ~1.5 KB link is a 140-module
 * symbol, and below roughly 2 screen pixels per module phone cameras stop
 * locking on. */
.share-link-modal {
  width: min(520px, calc(100vw - 32px));
}

.share-qr svg {
  width: min(360px, 70vw);
  height: auto;
  border-radius: var(--radius-sm);
}

.share-qr-actions {
  display: flex;
  justify-content: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-3-5);
}

.share-qr-actions[hidden] {
  display: none;
}

.share-qr-actions button {
  padding: var(--sp-2) var(--sp-4);
  background: var(--wash-2);
  color: var(--fg-1);
  border: none;
  border-radius: var(--radius);
  font-size: var(--fs-md);
  cursor: pointer;
}

.share-qr-actions button:hover {
  background: var(--hover-color);
}

.share-link-url {
  width: 100%;
  margin-bottom: var(--sp-4);
  padding: var(--sp-3) var(--sp-3-5);
  border: 1px solid var(--line-1);
  border-radius: var(--radius);
  background: var(--overlay-20);
  color: var(--ink-6);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
  resize: vertical;
  box-sizing: border-box;
}

.share-link-modal .paste-modal-actions #shareLinkClose {
  margin-top: var(--sp-1);
  padding: var(--sp-4) var(--sp-5);
  background: var(--wash-2);
  color: var(--fg-1);
  border: none;
  border-radius: var(--radius);
  font-size: var(--fs-lg);
  cursor: pointer;
}

/* Optional password field in the Share dialog: empty leaves the link
 * unprotected, a value swaps it for the AES-GCM ?e= form (ShareModule). */
.share-link-lock {
  margin-bottom: var(--sp-4);
}

.share-link-lock[hidden] {
  display: none;
}

.share-link-lock label {
  display: block;
  margin-bottom: var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--fg-1);
}

.share-link-lock input,
#sharePwInput {
  width: 100%;
  padding: var(--sp-3) var(--sp-3-5);
  border: 1px solid var(--line-1);
  border-radius: var(--radius);
  background: var(--overlay-20);
  color: var(--ink-6);
  font-size: var(--fs-md);
  box-sizing: border-box;
}

#sharePwInput {
  margin-bottom: var(--sp-3);
}

.share-pw-error {
  color: var(--danger-bright);
}

.share-pw-error[hidden] {
  display: none;
}


/* Order Structure's random-sample build/compare list, inline in the Cell &
 * Supercell panel (ui/LatticeSupercellPanel.js) — no floating dialog. One row
 * per built decoration; before Compare Energy runs it shows atom count only,
 * after it shows energy/atom, cheapest first, visually marked. */
.order-structure-list {
  max-height: 200px;
  overflow-y: auto;
  margin-bottom: var(--sp-3);
  border: 1px solid var(--line-1);
  border-radius: var(--radius-sm);
}

.order-structure-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3) var(--sp-2) var(--sp-2);
  font-size: var(--fs-sm);
  border-bottom: 1px solid var(--wash-2);
  border-left: 3px solid transparent;
  cursor: pointer;
}

.order-structure-row:last-child {
  border-bottom: none;
}

.order-structure-row.best {
  background: rgb(var(--best-row-rgb) / 0.1);
}

/* The one candidate currently rendered into the 3D view (see showCandidate()
 * in LatticeSupercellPanel.js) — a left accent bar so it reads at a glance
 * even when it's also the .best row (amber background) at the same time. */
.order-structure-row.showing {
  border-left-color: var(--highlight-color, var(--info-bright));
  background: var(--wash-1-5);
}

.order-structure-row.best.showing {
  background: rgb(var(--best-row-rgb) / 0.18);
}

.order-structure-rank {
  width: 24px;
  color: var(--fg-2);
}

.order-structure-energy {
  flex: 1;
  font-family: var(--font-mono);
}

.order-structure-delta {
  color: var(--fg-2);
  font-size: var(--fs-xs);
  min-width: 88px;
  text-align: right;
}

.order-structure-row.best .order-structure-delta {
  color: var(--warn);
}

/* Keyboard shortcuts help modal (ui/KeyboardShortcuts.js) — reuses the
 * .png-export-modal card + .paste-modal-actions button row. */
#shortcutsHelpModal {
  position: fixed;
  inset: 0;
  z-index: var(--z-dialog);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--overlay);
}

#shortcutsHelpModal[hidden] {
  display: none;
}

.shortcuts-help-modal {
  width: min(760px, calc(100vw - 32px));
  max-height: 84dvh;
  display: flex;
  flex-direction: column;
}

.shortcuts-help-title-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}

.shortcuts-help-title-row h3 {
  margin: 0;
}

/* Small "i" toggle next to the title that reveals/hides .shortcuts-help-note
 * below it, so the modal opens straight to the shortcut table instead of a
 * wall of explanatory text. */
.shortcuts-help-info-btn {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--popup-border);
  background: var(--wash-2);
  color: var(--fg-75);
  font-size: var(--fs-sm);
  font-style: italic;
  font-family: var(--font-display);
  line-height: var(--lh-tight);
  cursor: pointer;
  transition: background-color 0.15s, color 0.15s;
}

.shortcuts-help-info-btn:hover,
.shortcuts-help-info-btn[aria-expanded="true"] {
  background: var(--bg-color);
  color: var(--fg-1);
}

.shortcuts-help-note {
  margin: var(--sp-3) 0 var(--sp-4) 0;
  font-size: var(--fs-sm);
  opacity: 0.7;
  line-height: var(--lh-normal);
  text-align: justify;
}

/* Four columns, one per modifier combo (Shift / Space / Shift+Space /
 * Shift+Alt — ui/KeyboardShortcuts.js's TIER). Portrait/narrow: stacked, one
 * shared scroll region. Landscape/wide (see the media query below): side by
 * side, each with its own scroll. */
.shortcuts-help-columns {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 18px;
  margin-bottom: var(--sp-4);
}

.shortcuts-help-column {
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.shortcuts-help-column h4 {
  margin: 0 0 var(--sp-3) 0;
  font-size: var(--fs-base);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  opacity: 0.65;
}

.shortcuts-help-list {
  overflow: visible;
}

/* A real <table> per tier column — a description that needs to wrap does so
 * in its own cell, right next to its shortcut, instead of the two fighting
 * over space in a plain flex row. */
.shortcuts-help-table {
  width: 100%;
  border-collapse: collapse;
}

/* One function-grouped sub-section within a tier's table (e.g. Shift's
 * "Camera" vs "Navigation" vs "Other") — commands stay in the order they're
 * defined in ui/KeyboardShortcuts.js's COMMANDS array, not alphabetized. */
.shortcuts-help-group-row th {
  text-align: left;
  padding: var(--sp-3-5) 0 var(--sp-1) 0;
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  opacity: 0.45;
}

.shortcuts-help-group-row:first-child th {
  padding-top: 0;
}

/* Responsive layout for this modal (previously an orientation:landscape
   media query, folded into the "short" rung) now lives in
   styles/responsive.css, per Wave 4's single-file @media rule. */

.shortcuts-help-row td {
  padding: 3px 0;
  vertical-align: middle;
}

.shortcuts-help-row .shortcuts-help-clear-cell {
  width: 20px;
  padding-right: var(--sp-1);
}

.shortcuts-help-row .shortcuts-help-key-cell {
  width: 1%; /* shrink to the key button's own content, not table-layout's equal split */
  white-space: nowrap;
  padding-right: var(--sp-3-5);
}

.shortcuts-help-desc-cell {
  font-size: var(--fs-base);
  opacity: 0.9;
  line-height: var(--lh-normal);
}

/* Left of each key button — clears that shortcut entirely (stored as "NONE"
 * so it round-trips through JSON export/import as a plain string). Disabled
 * once a shortcut is already unbound. */
.shortcuts-help-clear {
  width: 16px;
  height: 16px;
  padding: 0;
  border-radius: var(--radius-4);
  border: 1px solid var(--wash-4);
  background: var(--wash-1);
  color: var(--fg-2);
  font-size: var(--fs-base);
  line-height: var(--lh-tight);
  cursor: pointer;
  transition: background-color 0.15s, border-color 0.15s, color 0.15s;
}

.shortcuts-help-clear:hover:not(:disabled) {
  background: rgb(var(--shortcuts-danger-rgb) / 0.25);
  border-color: rgb(var(--shortcuts-danger-rgb) / 0.6);
  color: var(--fg-1);
}

.shortcuts-help-clear:disabled {
  opacity: 0.25;
  cursor: default;
}

/* .shortcuts-help-key is a <button> (click to rebind), styled to still read
 * as a key-cap chip like the rest of the app's shortcut labels. */
.shortcuts-help-key {
  min-width: 62px;
  max-width: 140px;
  padding: 2px 7px;
  border-radius: var(--radius-5);
  background: var(--input-bg);
  border: 1px solid var(--line-2);
  color: inherit;
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
  transition: background-color 0.15s, box-shadow 0.15s;
}

.shortcuts-help-key:hover {
  background: var(--line-2);
}

/* Individual tokens inside a key button's default (non-recording,
 * non-conflict) state — modifier names / the bound key itself, spaced out
 * with "+" separators dimmed to grey so they read as punctuation rather
 * than part of the label. */
.shortcuts-help-key-tokens {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
}

.shortcuts-help-key-token {
  color: var(--fg-1);
}

.shortcuts-help-key-sep {
  color: var(--fg-3);
}

.shortcuts-help-key-empty {
  opacity: 0.5;
  font-style: italic;
}

.shortcuts-help-key-recording {
  background: rgb(var(--danger-rgb) / 0.25);
  border-color: rgb(var(--danger-rgb) / 0.7);
  box-shadow: 0 0 0 1px rgb(var(--danger-rgb) / 0.4);
}

/* Shown briefly when a captured combo is already used by another command —
 * the rebind is rejected, not reassigned, so this reads as an error rather
 * than the orange "recording" state above. */
.shortcuts-help-key-conflict {
  background: rgb(var(--shortcuts-danger-rgb) / 0.25);
  border-color: rgb(var(--shortcuts-danger-rgb) / 0.85);
  box-shadow: 0 0 0 1px rgb(var(--shortcuts-danger-rgb) / 0.5);
  color: var(--fg-1);
}

.shortcuts-help-actions {
  flex-wrap: wrap;
}

.shortcuts-help-modal .shortcuts-help-actions #shortcutsHelpClose {
  flex: 1;
  min-width: 100px;
  margin-top: var(--sp-1);
  padding: var(--sp-3-5) var(--sp-5);
  background: var(--wash-2);
  color: var(--fg-1);
  border: none;
  border-radius: var(--radius);
  font-size: var(--fs-lg);
  cursor: pointer;
}

.shortcuts-help-modal .shortcuts-help-actions #shortcutsHelpClose:hover {
  box-shadow: 0 0 4px 2px var(--highlight-color);
}

/* Reset lives here too (not just the Custom User Settings panel) — still
 * gated by the same confirm popup (confirmResetAllShortcuts), just a click
 * closer since this is where the shortcut list itself is visible. */
.shortcuts-help-modal .shortcuts-help-actions #shortcutsHelpReset {
  flex: 1;
  min-width: 100px;
  margin-top: var(--sp-1);
  padding: var(--sp-3-5) var(--sp-5);
  background: var(--wash-1);
  color: var(--fg-85);
  border: 1px solid var(--wash-4);
  border-radius: var(--radius);
  font-size: var(--fs-lg);
  cursor: pointer;
}

.shortcuts-help-modal .shortcuts-help-actions #shortcutsHelpReset:hover {
  background: rgb(var(--shortcuts-danger-rgb) / 0.2);
  border-color: rgb(var(--shortcuts-danger-rgb) / 0.6);
  color: var(--fg-1);
}

/* Reset-all-shortcuts confirmation (ui/KeyboardShortcuts.js's
 * confirmResetAllShortcuts, triggered from the Custom User Settings panel) —
 * reuses the same .png-export-modal card + .paste-modal-actions row as the
 * shortcuts help modal above and RaytraceWarningModal. */
#shortcutsResetConfirmModal {
  position: fixed;
  inset: 0;
  z-index: var(--z-confirm);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--overlay);
}

#shortcutsResetConfirmModal[hidden] {
  display: none;
}

.shortcuts-reset-confirm-modal {
  width: min(420px, calc(100vw - 32px));
}

.shortcuts-reset-confirm-modal p {
  margin: 0 0 14px 0;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  opacity: 0.85;
}

.shortcuts-reset-confirm-modal .paste-modal-actions #shortcutsResetConfirmOk {
  flex: 1;
  min-width: 100px;
  margin-top: var(--sp-1);
  padding: var(--sp-3-5) var(--sp-5);
  background: rgb(var(--shortcuts-danger-rgb) / 0.25);
  border: 1px solid rgb(var(--shortcuts-danger-rgb) / 0.7);
  color: var(--fg-1);
  border-radius: var(--radius);
  font-size: var(--fs-lg);
  cursor: pointer;
}

.shortcuts-reset-confirm-modal .paste-modal-actions #shortcutsResetConfirmOk:hover {
  background: rgb(var(--shortcuts-danger-rgb) / 0.4);
}

.shortcuts-reset-confirm-modal .paste-modal-actions #shortcutsResetConfirmCancel {
  flex: 1;
  min-width: 100px;
  margin-top: var(--sp-1);
  padding: var(--sp-3-5) var(--sp-5);
  background: var(--wash-2);
  color: var(--fg-1);
  border: none;
  border-radius: var(--radius);
  font-size: var(--fs-lg);
  cursor: pointer;
}

.shortcuts-reset-confirm-modal .paste-modal-actions #shortcutsResetConfirmCancel:hover {
  box-shadow: 0 0 4px 2px var(--highlight-color);
}

/* Generic confirm modal (ui/ConfirmModal.js) — used by the camera/Features
 * lock buttons' confirmOnLock prompt in place of window.confirm(), which
 * always prefixes its message with the page's own origin. Reuses the same
 * .png-export-modal card + .paste-modal-actions row as the other confirm
 * dialogs above. */
#confirmModal {
  position: fixed;
  inset: 0;
  z-index: var(--z-confirm);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--overlay);
}

#confirmModal[hidden] {
  display: none;
}

.lock-confirm-modal {
  width: min(440px, calc(100vw - 32px));
}

.lock-confirm-modal p {
  margin: 0 0 14px 0;
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  opacity: 0.85;
}

.lock-confirm-modal .paste-modal-actions #confirmModalOk {
  flex: 1;
  min-width: 100px;
  margin-top: var(--sp-1);
  padding: var(--sp-3-5) var(--sp-5);
  background: var(--bg-color);
  color: var(--fg-1);
  border: none;
  border-radius: var(--radius);
  font-size: var(--fs-lg);
  font-weight: var(--fw-medium);
  cursor: pointer;
}

.lock-confirm-modal .paste-modal-actions #confirmModalOk:hover {
  box-shadow: 0 0 4px 2px var(--highlight-color);
}

.lock-confirm-modal .paste-modal-actions #confirmModalCancel {
  flex: 1;
  min-width: 100px;
  margin-top: var(--sp-1);
  padding: var(--sp-3-5) var(--sp-5);
  background: var(--wash-2);
  color: var(--fg-1);
  border: none;
  border-radius: var(--radius);
  font-size: var(--fs-lg);
  cursor: pointer;
}

.lock-confirm-modal .paste-modal-actions #confirmModalCancel:hover {
  background: var(--hover-color);
}

/* Multi-choice variant of the confirm modal (ui/ConfirmModal.js choiceDialog).
 * Used where the answer is genuinely not binary — opening a WAVECAR whose cell
 * does not match the loaded structure offers three real alternatives. Each
 * option gets its own full-width row so the explanation fits next to it. */
.lock-confirm-modal .paste-modal-actions.confirm-choices {
  flex-direction: column;
  gap: var(--sp-2);
}

.lock-confirm-modal .confirm-choices .confirm-choice {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  width: 100%;
  padding: var(--sp-3-5) var(--sp-5);
  background: var(--wash-2);
  color: var(--fg-1);
  border: none;
  border-radius: var(--radius);
  text-align: left;
  cursor: pointer;
}

.lock-confirm-modal .confirm-choices .confirm-choice:hover {
  background: var(--hover-color);
}

/* The first row is the recommended action, so it carries the accent fill the
 * single-choice dialog gives its Ok button. */
.lock-confirm-modal .confirm-choices .confirm-choice:first-child {
  background: var(--bg-color);
}

.lock-confirm-modal .confirm-choices .confirm-choice:first-child:hover {
  box-shadow: 0 0 4px 2px var(--highlight-color);
}

.lock-confirm-modal .confirm-choice-label {
  font-size: var(--fs-lg);
  font-weight: var(--fw-medium);
}

.lock-confirm-modal .confirm-choice-desc {
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  opacity: 0.8;
}

/* Preformatted context block. Monospaced through --font-mono (the token, not a
 * literal stack — see STYLE.md invariant 2) so the two lattices line up
 * column-by-column and a discrepancy is visible at a glance. */
.lock-confirm-modal .confirm-detail {
  margin: 0 0 var(--sp-3-5) 0;
  font-family: var(--font-mono);
  padding: var(--sp-3) var(--sp-4);
  background: var(--wash-1);
  border-radius: var(--radius);
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
  overflow-x: auto;
}

/* Small Main-dock triggers, positioned immediately left of .theme-switch
 * (ui/KeyboardShortcuts.js; the About trigger is the left sibling of the
 * keyboard-shortcuts trigger). */
.shortcuts-help-trigger,
.about-help-trigger {
  position: absolute;
  top: 10px;
  z-index: 10;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 1px solid var(--wash-4);
  background: var(--wash-1-5);
  color: var(--panel-fg);
  font-size: var(--fs-lg);
  line-height: var(--lh-tight);
  cursor: pointer;
  transition: background-color 0.2s, box-shadow 0.2s;
}

.about-help-trigger {
  left: 210px;
}

.shortcuts-help-trigger {
  left: 246px;
}

.about-help-trigger svg {
  display: block;
  width: 18px;
  height: 18px;
}

.shortcuts-help-trigger:hover,
.about-help-trigger:hover {
  background: var(--bg-color);
  box-shadow: 0 0 4px 2px var(--bg-color);
}

/* A phone has no keyboard, and the dock header there is only wide enough for
 * the theme switch — the two absolutely-positioned triggers slid under it.
 * Class set by ui/panels/PanelManager.js from the phone-screen check. */
body.cv-phone-screen .shortcuts-help-trigger,
body.cv-phone-screen .about-help-trigger {
  display: none;
}

/* Drag&drop hover highlight on the drop targets (Files window / 3D view) */
.cv-drop-hover {
  outline: 2px dashed var(--highlight-color);
  outline-offset: -2px;
}

/* iOS-Photos-style crop selector (ui/CropOverlay.js), positioned to exactly
   cover #view. Four mask divs dim everything outside .cv-crop-rect rather
   than a single overlay with a cut-out — simpler than clip-path/SVG masking
   and just as effective for an axis-aligned rectangle. */
.cv-crop-overlay {
  position: fixed;
  z-index: var(--z-dialog);
  outline: none;
}
.cv-crop-mask {
  position: absolute;
  background: var(--overlay-60);
  pointer-events: none;
}
.cv-crop-rect {
  position: absolute;
  border: 2px solid var(--fg-1);
  box-shadow: 0 0 0 1px rgb(var(--shadow-rgb) / 0.4);
  cursor: move;
  touch-action: none;
}
/* Rule-of-thirds guide lines, purely decorative (matches the iOS crop tool). */
.cv-crop-rect::before,
.cv-crop-rect::after {
  content: "";
  position: absolute;
  background: var(--fg-3);
  pointer-events: none;
}
.cv-crop-rect::before {
  left: 33.333%;
  right: 33.333%;
  top: 0;
  bottom: 0;
  width: 1px;
  box-shadow: calc(33.333% - 1px) 0 0 var(--fg-3);
}
.cv-crop-rect::after {
  top: 33.333%;
  bottom: 33.333%;
  left: 0;
  right: 0;
  height: 1px;
  box-shadow: 0 calc(33.333% - 1px) 0 var(--fg-3);
}
.cv-crop-handle {
  position: absolute;
  width: 22px;
  height: 22px;
  touch-action: none;
}
.cv-crop-handle::before {
  content: "";
  position: absolute;
  width: 100%;
  height: 100%;
  border: 3px solid var(--fg-1);
  border-radius: 2px;
}
.cv-crop-handle-nw { left: -11px; top: -11px; cursor: nwse-resize; }
.cv-crop-handle-ne { right: -11px; top: -11px; cursor: nesw-resize; }
.cv-crop-handle-sw { left: -11px; bottom: -11px; cursor: nesw-resize; }
.cv-crop-handle-se { right: -11px; bottom: -11px; cursor: nwse-resize; }
.cv-crop-handle-nw::before { border-width: 3px 0 0 3px; }
.cv-crop-handle-ne::before { border-width: 3px 3px 0 0; }
.cv-crop-handle-sw::before { border-width: 0 0 3px 3px; }
.cv-crop-handle-se::before { border-width: 0 3px 3px 0; }

.cv-crop-toolbar {
  position: absolute;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  /* Was white-space:nowrap — a single unbreakable row is wider than a phone.
     Wrapping (still centred on the anchor) keeps it on screen. */
  flex-wrap: wrap;
  justify-content: center;
  max-width: calc(100vw - 24px);
  gap: var(--sp-3-5);
  padding: var(--sp-3) var(--sp-3-5);
  border-radius: var(--radius-md);
  background: var(--menu-bg);
  border: 1px solid var(--wash-4);
  box-shadow: var(--shadow-lg);
  backdrop-filter: blur(10px);
}
.cv-crop-hint {
  font-size: var(--fs-base);
  opacity: 0.7;
  color: var(--panel-fg);
}
/* The toolbar's own Lock aspect toggle (mirrors the export dialog's). */
.cv-crop-lock {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-base);
  color: var(--panel-fg);
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
.cv-crop-confirm,
.cv-crop-cancel {
  padding: var(--sp-3) 14px;
  border-radius: var(--radius);
  border: none;
  font-size: var(--fs-md);
  cursor: pointer;
}
.cv-crop-confirm {
  background: var(--bg-color);
  color: var(--fg-1);
  font-weight: var(--fw-medium);
}
.cv-crop-confirm:hover {
  box-shadow: 0 0 4px 2px var(--highlight-color);
}
.cv-crop-cancel {
  background: var(--wash-2);
  color: var(--fg-1);
}

#structureText {
  width: 100%;
  min-height: 160px;
  padding: var(--sp-4);
  border-radius: var(--radius);
  border: 1px solid var(--line-1);
  background: var(--overlay-20);
  color: var(--ink-6);
  resize: vertical;
  font-family: var(--font-mono);
  font-size: var(--fs-md);
  line-height: var(--lh-normal);
  box-sizing: border-box;
}

#structureText::placeholder {
  color: var(--fg-50);
}

#loadTextButton {
  margin-top: var(--sp-4);
  width: 100%;
  padding: var(--sp-4) var(--sp-5);
  background: var( --bg-color);
  color: var(--fg-1);
  border: none;
  border-radius: var(--radius);
  font-size: var(--fs-lg);
  font-weight: var(--fw-medium);
  cursor: pointer;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

#loadTextButton:hover {
  border: 1px solid var(--line-1);
  transform: translateY(-1px);
  box-shadow: 0px 0px 4px 2px  var(--highlight-color);
}

/* #saveButton is the "POSCAR" entry in the Download dropdown; it takes the
   .download-menu button styling. */

.comparison-group {
  backdrop-filter: blur(10px);
}

.comparison-group h1 {
  margin: 0 0 var(--sp-5) 0;
  color:  var(--warn);
  font-size: var(--fs-xl);
  font-weight: var(--fw-medium);
}

.comparison-group h3 {
  margin: 0 0 var(--sp-5) 0;
  color: var(--ok-bright);
  font-size: var(--fs-xl);
  font-weight: var(--fw-medium);
}

.second-input-mode-toggle {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--sp-3);
  margin-bottom: var(--sp-5);
  background: var(--wash-1);
  padding: var(--sp-2);
  border-radius: var(--radius-md);
}

.second-input-mode-btn {
  background: transparent;
  color: var(--ink-4);
  border: none;
  padding: var(--sp-3-5) var(--sp-4);
  border-radius: var(--radius);
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease;
}

.second-input-mode-btn:hover {
  background: var(--bg-color);
  color: var(--fg-1);
}

.second-input-mode-btn.active {
  background: var(--highlight-color);
  color: var(--fg-1);
}

.second-input-mode-panel {

  max-width: 270px;
  display: block;
}

.second-input-mode-panel[hidden] {
  display: none !important;
}

#secondFileInput {
  display: none;
}


#secondFileLabel {
  display: block;
  padding: var(--sp-5) var(--sp-3-5);
  background: var(--bg-color);
  color: var(--fg-1);
  text-align: center;
  border-radius: var(--radius);
  cursor: pointer;
  font-weight: var(--fw-medium);
  transition: all 0.3s ease;
  border: 2px dashed transparent;
}

#secondFileLabel:hover {
  transform: translateY(-2px);
  box-shadow: 0px 0px 4px 2px  var(--hover-color);
}

#secondFileLabel.dragover {
  border: 2px dashed var(--highlight-color);
  background: var(--bg-color);
}

#secondStructureText {
  width: 100%;
  min-height: 160px;
  padding: var(--sp-4);
  border-radius: var(--radius);
  border: 1px solid var(--line-1);
  background: var(--overlay-20);
  color: var(--ink-6);
  resize: vertical;
  font-family: var(--font-mono);
  font-size: var(--fs-md);
  line-height: var(--lh-normal);
  box-sizing: border-box;
}

#secondStructureText::placeholder {
  color: var(--fg-50);
}

#secondLoadTextButton {
  margin-top: var(--sp-4);
  width: 100%;
  padding: var(--sp-4) var(--sp-5);
  background: var( --bg-color);
  color: var(--fg-1);
  border: none;
  border-radius: var(--radius);
  font-size: var(--fs-lg);
  font-weight: var(--fw-medium);
  cursor: pointer;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

#secondLoadTextButton:hover {
  transform: translateY(-1px);
  box-shadow: 0px 0px 4px 2px  rgb(var(--btn-glow-rgb) / 0.35);
}


label {
  display: block;
  margin: var(--sp-5) 0 var(--sp-3) 0;
  color: var(--ink-3);
  /* Multiplied by the Overall font scale (Settings). This is the single source
     for label sizing, so it carries section headlines (.panel-headline) and
     slider labels along with it. */
  font-size: var(--fs-lg);
  font-weight: var(--fw-medium);
}

input[type="range"] {
  width: 100%;
  height: 6px;
  border-radius: var(--radius-3);
  background: var(--track-bg);
  outline: none;
  -webkit-appearance: none;
}

input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--highlight-color);
  cursor: pointer;
  box-shadow: var(--shadow-glow);
}

input[type="range"]::-moz-range-thumb {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--highlight-color);
  cursor: pointer;
  border: none;
  box-shadow: var(--shadow-glow);
}

input[type="checkbox"] {
  margin-right: var(--sp-3-5);
  transform: scale(1.2);
  accent-color: var(--accent-fg);
}

.slider-value {
  color: var(--accent-fg);
  font-weight: var(--fw-bold);
}

#view {
  flex: 1;
  position: relative;
  border-radius: 0;
  overflow: hidden;
}

#aboutOverlay {
  position: fixed;
  inset: 0;
  background: var(--overlay-70);
  backdrop-filter: blur(6px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: var(--z-chrome);
  opacity: 0;
  transition: opacity 0.15s ease;
}

#aboutOverlay.visible {
  opacity: 1;
}

#aboutModal {
  background: var(--about-bg);
  color: var(--ok-tint);
  max-width: 640px;
  max-height: 80dvh;
  overflow-y: auto;
  padding: 28px 32px;
  border-radius: 16px;
  border: 1px solid var(--about-border);
  box-shadow: 0px 0px 4px 2px  rgb(var(--shadow-rgb) / 0.45);
  position: relative;
  font-size: var(--fs-lg);
  line-height: 1.6;
}

#aboutClose {
  position: absolute;
  top: 12px;
  right: 12px;
  background: transparent;
  border: none;
  color: var(--ok-bright);
  font-size: var(--fs-3xl);
  cursor: pointer;
  line-height: var(--lh-tight);
  padding: var(--sp-1);
}

#aboutClose:hover {
  color: var(--about-close-hover);
}

#aboutContent h1,
#aboutContent h2,
#aboutContent h3 {
  color: var(--ok-bright);
  margin-top: 1.4em;
  margin-bottom: 0.6em;
  font-weight: var(--fw-bold);
}

#aboutContent h1 {
  font-size: 1.6rem;
}

#aboutContent h2 {
  font-size: 1.3rem;
}

#aboutContent h3 {
  font-size: 1.1rem;
}

#aboutContent p {
  margin: 0.8em 0;
  color: var(--ok-tint);
}

#aboutContent ul {
  padding-left: 1.2em;
  margin: 0.6em 0;
}

#aboutContent li {
  margin: 0.35em 0;
}

#aboutContent a {
  color: var(--info-bright);
  text-decoration: underline;
}

#aboutContent a:hover {
  color: var(--link-hover);
}

#aboutContent code {
  background: var(--about-code-bg);
  padding: 2px var(--sp-1);
  border-radius: var(--radius-4);
  font-family: var(--font-mono);
  font-size: 0.92em;
}

#aboutContent .about-error {
  color: var(--danger-bright);
}

#aboutOverlay[hidden] {
  display: none;
}

.bond-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    background-color: var( --bg-color);
    border: 1px solid var(--line-1);
    border-radius: var(--radius);
    padding: 0.6em 1em;
    cursor: pointer;
    user-select: none;
    transition: background-color 0.2s, box-shadow 0.2s;
  }

  /* Flat, non-interactive section headline inside a panel window body (see
     ui/panels/sectionHeadline.js): a plain label, only spacing added. */
  .panel-headline {
    display: block;
    margin: 0.9em 0 0.3em 0;
    user-select: none;
  }

  /* Visual panel (Sizes/Scene/Rendering/Colors/Camera): larger, centered
     headlines make its several sections easier to tell apart at a glance. */
  #cvPanelBody-visual .panel-headline {
    font-size: var(--fs-xl);
    font-weight: var(--fw-bold);
    text-align: center;
  }

  /* Framed section box (Visual panel via ui/panels/defaultPanels.js's
     makePanelSection; Cell & Supercell via LatticeSupercellPanel.js): a rounded
     border around each section's headline + controls, so several stacked
     sections read as distinct groups instead of one long flat list. */
  .panel-section {
    border: 1px solid var(--line-2);
    border-radius: var(--radius-12);
    padding: var(--sp-3-5) 14px 14px;
    margin: 0 0 14px 0;
  }

  .panel-section .panel-headline {
    margin-top: 0;
  }

  .panel-section:last-child {
    margin-bottom: 0;
  }

  /* Add Structure panel (ui/addToStructureModule/AddStructureModule.js,
     LatticeInputPanel.js): centered section headlines. */
  #cvPanelBody-addStructure .panel-headline {
    text-align: center;
  }

  .bond-toggle:hover,
  .bond-toggle:focus {
    background-color: var( --bg-color);
    box-shadow: 0px 0px 4px 2px  var( --bg-color);
    border: 1px solid var(--line-1);
    outline: none;
  }

  .bond-toggle h4 {
    margin: 0;
    font-size: var(--fs-xl);
    font-weight: var(--fw-bold);
  }

  .toggle-icon {
    font-size: var(--fs-2xl);
    margin-left: 0.5em;
    transition: transform 0.2s;
  }

  .collapsible-content {
    display: none;
    margin-top: 0.5em;
    padding: 0.5em 1em;
  }

  .collapsible-content.open {
    display: block;
  }


#bondControls {
  max-height: 300px;
  overflow-y: auto;
}

#bondControls::-webkit-scrollbar {
  width: 6px;
}

#bondControls::-webkit-scrollbar-track {
  background: var( --bg-color);
  border-radius: var(--radius-3);
}

#bondControls::-webkit-scrollbar-thumb {
  background: var(--hover-color);
  border-radius: var(--radius-3);
}

.bond-control {
  margin: var(--sp-4) 0;
  padding: var(--sp-4);
  background: var(--wash-1);
  border-radius: var(--radius);
  border-left: 3px solid var(--hover-color);
}

.bond-label {
  color: var(--fg-1);
  font-size: var(--fs-lg);
  font-weight: var(--fw-medium);
  margin-bottom: 5px;
}

.bottonWrapper {
padding: 0px 0px var(--sp-3-5) 0px;
display: flex;
justify-content: center;
}

.reset-btn {
  font-size: var(--fs-lg);
  background: var(--danger); /*rgba(60,60,60,0.8)*/
  color: var(--fg-1);
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-weight: var(--fw-medium);
}

.reset-btn:hover {
  transform: translateY(-1px);
  box-shadow: 0px 0px 4px 2px  rgb(var(--danger-bright-rgb) / 0.1);
  border: 1px solid var(--line-3);
}

/* Destructive variant: "Clear local data" wipes all persisted settings. */
.reset-btn-danger {
  background: var(--danger-fill);
}

.bond-checkbox {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-1);
  padding: var(--sp-1) 0;
}

.bond-checkbox input[type="checkbox"] {
  margin: 0;
}

.spin-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    background-color: var( --bg-color);
    border: 1px solid var(--line-1);
    border-radius: var(--radius);
    padding: 0.6em 1em;
    cursor: pointer;
    user-select: none;
    margin: var(--sp-5) 0;
    transition: background-color 0.2s, box-shadow 0.2s;
  }

  .spin-toggle:hover,
  .spin-toggle:focus {
    background-color: var( --bg-color);
    box-shadow: 0px 0px 4px 2px  var( --bg-color);
    border: 1px solid var(--line-1);
    outline: none;
  }

  .spin-toggle h4 {
    margin: 0;
    font-size: var(--fs-xl);
    font-weight: var(--fw-bold);
  }

  .toggle-icon {
    font-size: var(--fs-2xl);
    margin-left: 0.5em;
    transition: transform 0.2s;
  }

  .collapsible-content {
    display: none;
    margin-top: 0.5em;
    padding: 0.5em 1em;
    border-radius: var(--radius);
    border:1px solid var(--popup-border);
  }

  .collapsible-content.open {
    display: block;
  }


#spinControls {
  max-height: 450px;
  overflow-y: auto;
}

#spinControls::-webkit-scrollbar {
  width: 6px;
}

#spinControls::-webkit-scrollbar-track {
  background: var( --bg-color);
  border-radius: var(--radius-3);
}

#spinControls::-webkit-scrollbar-thumb {
  background: var(--hover-color);
  border-radius: var(--radius-3);
}

.spin-control {
  justify-content: center;
  margin: var(--sp-4) 0;
  padding: var(--sp-4);
  background: var(--wash-1);
  border-radius: var(--radius);
  border-left: 3px solid var(--hover-color);
}

.spin-label {
  color: var(--fg-1);
  font-size: var(--fs-lg);
  font-weight: var(--fw-medium);
  margin-bottom: 5px;
}

.spinInputWrapper {
justify-content: center;
display: grid; 
padding: var(--sp-2);
background: var(--wash-1);
border-radius: var(--radius-md);
gap: var(--sp-3);

}

.spin-input-mode-toggle {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--sp-3);
  margin-bottom: var(--sp-5);
  background: var(--wash-1);
  padding: var(--sp-2);
  border-radius: var(--radius-md);
}

.spin-input-mode-btn {
  background: transparent;
  color: var(--ink-4);
  border: none;
  padding: var(--sp-3-5) var(--sp-4);
  border-radius: var(--radius);
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease;
}

.spin-input-mode-btn:hover {
  background: var(--bg-color);
  color: var(--fg-1);
}

.spin-input-mode-btn.active {
  background: var(--highlight-color);
  color: var(--fg-1);
}

.spin-input-mode-panel {
  display: block;
}

.spin-input-mode-panel[hidden] {
  display: none !important;
}


.bottonWrapper {
padding: 0px 0px var(--sp-3-5) 0px;
display: flex;
justify-content: center;
}

.reset-btn {
  background: var(--danger); /*rgba(60,60,60,0.8)*/
  color: var(--fg-1);
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-weight: var(--fw-medium);
}


.spin-checkbox {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-3);
  padding: var(--sp-1) 0;
}

.spin-checkbox input[type="checkbox"] {
  margin: 0;
}

.comparison-toggle{
    display: flex;
    align-items: center;
    justify-content: space-between;
    background-color: var(--group-bg);
    border: 1px solid var(--line-1);
    border-radius: var(--radius);
    padding: 0.6em 1em;
    cursor: pointer;
    user-select: none;
    margin: var(--sp-5) 0;
    transition: background-color 0.2s, box-shadow 0.2s;
  }

  .comparison-toggle:hover,
  .comparison-toggle:focus {
    background-color: var( --bg-color);
    box-shadow: 0px 0px 4px 2px  var( --bg-color);
    border: 1px solid var(--line-1);
    outline: none;
  }

  .comparison-toggle h4 {
    color: var(--ok-bright);
    margin: 0;
    font-size: var(--fs-xl);
    font-weight: var(--fw-bold);
  }


#status {
  position: fixed;
  top: 20px;
  right: 20px;
  background: var(--float-panel-bg);
  color: var(--fg-1);
  padding: var(--sp-3-5) var(--sp-5);
  border-radius: 20px;
  font-size: var(--fs-base);
  z-index: var(--z-overlay-widget);
  backdrop-filter: blur(10px);
  border: 1px solid var(--line-1);
}

#optimadeWarningDialog {
  box-sizing: border-box;
  width: min(440px, calc(100vw - 32px));
  max-height: calc(100dvh - 32px);
  margin: auto;
  padding: var(--sp-5);
  border: 1px solid var(--danger-bright);
  border-radius: 16px;
  background: var(--float-panel-bg);
  color: var(--fg-1);
  box-shadow: var(--shadow-lg);
  text-align: center;
}

#optimadeWarningDialog::backdrop {
  background: var(--overlay);
}

#optimadeWarningDialog h2 {
  margin: 0 0 var(--sp-3);
  color: var(--danger-bright);
  font-size: var(--fs-xl);
}

#optimadeWarningDialog p {
  margin: 0 0 var(--sp-5);
  line-height: 1.45;
}

#optimadeWarningClose {
  min-width: 88px;
  min-height: 40px;
}

/* compositions and clicked element (content lives inside the unified
   "Structure" panel window, which provides the chrome/positioning) */

/* Formula header box: shows the chemical formula, expands (+/−) into the
   composition details below it. */
/* Cut to the scene-toolbar square (--tb-btn, styles.css "Scene toolbars")
   with the same border, so the header bar is level with the Measure toolbar
   above it and reads as the same family of control. */
#structureInfoContent .structure-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  cursor: pointer;
  user-select: none;
  box-sizing: border-box;
  height: var(--tb-btn);
  padding: 0 var(--sp-1) 0 var(--sp-3);
  border-radius: var(--radius);
  background: var(--bg-color);
  border: 1px solid var(--wash-4);
}

#structureInfoContent .structure-toggle h4 {
  margin: 0 20px;
  font-size: var(--fs-lg);
  font-weight: 800;
  line-height: 1;
  color: var(--fg-75);
}

.section-heading { margin-top:var(--sp-3-5); margin-bottom:var(--sp-2); font-size: var(--fs-lg); font-weight:var(--fw-bold); padding-left:var(--sp-3); }
.toggle-icon { width:30px; height:30px; display:flex; align-items:center; justify-content:center; border-radius: var(--radius-sm); background:var(--overlay); color:var(--accent-color); font-weight:1000; transition:transform .18s ease, background .18s ease, color .18s ease; }

.toggle-icon.open { transform: background:var(--accent-color); color:var(--accent-color); }
.collapsible-content { overflow-y:auto; max-height:0; transition:max-height .28s ease, padding .18s ease; }

.collapsible-content::-webkit-scrollbar {
  width: 6px;
}

.collapsible-content::-webkit-scrollbar-track {
  background: var(--track-bg);
  border-radius: var(--radius-3);
}

.collapsible-content::-webkit-scrollbar-thumb {
  background: var(--hover-color);
  border-radius: var(--radius-3);
}

.collapsible-content.open { justify-content: center;  padding-top:var(--sp-3); max-height:650px; margin: var(--sp-3-5) 0 0px 0;}
#composition { color:var(--info-tint); line-height: var(--lh-normal); }
#composition [hidden] { display: none !important; }
#measure { margin-top:var(--sp-3); }
#measureRow { display:flex; justify-content:space-between; gap:var(--sp-3); color:var(--info-tint); }


/* Custom color picker as a modifiable UI component*/


.custom-color-picker {
  width: 200px;
  font-family: var(--font-ui);
  user-select: none;
}

.custom-color-picker .sb-picker {
  position: relative;
  width: 100%;
  height: 150px;
  background: red;
  cursor: crosshair;
  margin-bottom: var(--sp-3-5);
}

.custom-color-picker .sb-picker::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Saturation/brightness gradient stops must stay literal white/black
     regardless of theme — these are colour-space axes, not themeable UI. */
  background:
    linear-gradient(to right, #fff, transparent),
    linear-gradient(to top, #000, transparent);
}

.custom-color-picker .hue-slider {
  width: 100%;
  margin-bottom: var(--sp-3-5);
}

.custom-color-picker .color-preview {
  width: 100%;
  height: 30px;
  border: 1px solid var(--ink-3);
  background: red;
}




/* Small actions */
.btn-mini { background:transparent; border:1px solid var(--wash-2); color:var(--info-tint); padding:var(--sp-2) var(--sp-3-5); border-radius: var(--radius-sm); cursor:pointer; }
.btn-mini:hover { transform: translateY(-1px);background:var(--wash-0); border: 1px solid var(--accent-color);}

.btn-mini.highlight { background:var(--bg-color); border-color:var(--line-dark); color:var(--fg-1); }
.btn-mini.highlight:hover { transform: translateY(-1px);filter:brightness(1.03); border: 1px solid var(--accent-color);}

#composition h4 {
  margin: 0 0 var(--sp-3) 0;
  font-weight: var(--fw-bold);
  color: var(--fg-1);
  font-size: var(--fs-lg);
  letter-spacing: .3px;
}

.comp-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-2);
  padding: var(--sp-1) 0;
  border-bottom: 1px dashed var(--line-1);
}
.comp-row:last-child { border-bottom: 0; }
.dot {
  width: 10px; height: 10px; border-radius: 50%; margin-right: var(--sp-3);
  border: 1px solid var(--fg-3);
}
/* --sp-2, not --sp-3: the Structure panel's category row shares 266px between
   this cluster and count + Spin/Force + toggle, and 2px per gap here is what
   keeps that right-hand group on one line (structureInfoPanel.css .comp-row). */
.comp-left { display: inline-flex; align-items: center; gap: var(--sp-2); }
#picked {
  display: inline-flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3); background: var(--bg-color);
  border: 1px solid var(--accent-color); border-radius: var(--radius-md);
}
#view canvas {
  cursor: pointer; /* for click affordance */
  /* Three.js's renderer.setSize() sets this canvas's CSS box via an inline
     style, updated only when JS runs (on init, and on the resize handler's
     next animation-frame tick). #view itself (flex: 1) is resized instantly
     by the browser's native layout on every 'resize' event, with no
     background-color of its own — so during the gap before JS catches up,
     the canvas's stale inline size left part of #view uncovered, showing the
     page's background-color (--surface-bg) through it: the reported
     green/black flicker on resize. !important here beats that inline style
     unconditionally, so the canvas's box always instantly matches #view; only
     the internal drawing-buffer resolution (actual render pixels) still
     briefly lags a frame behind, which just reads as a touch of stretching/
     blur, never a gap. */
  width: 100% !important;
  height: 100% !important;
  display: block;
}


#axesGizmo {
/* --split-reserve-bottom is kept in sync with the split view's pane height
   when it's docked to the bottom (see ui/panels/SplitView.js), so the gizmo
   stays clear of the pane instead of sliding underneath it — same idea as
   --split-reserve does for the background-dot's right offset. 0px when the
   pane is closed or docked to the right. */
position: fixed; left: var(--gizmo-left); bottom: calc(var(--gizmo-bottom) + var(--split-reserve-bottom, 0px)); width: var(--gizmo-size); height: var(--gizmo-size);
z-index: var(--z-overlay-widget); overflow: hidden;
/* auto, not none: the whole box is a drag handle (ui/GizmoDrag.js), not
   just its grip — this trades away clicking "through" the gizmo to rotate
   the scene behind it, in exchange for being draggable from anywhere on it. */
pointer-events: auto;
cursor: grab;
touch-action: none;
}
#axesGizmo.cv-gizmo-dragging {
  cursor: grabbing;
}

#axesLegend{
position: fixed; left: calc(var(--gizmo-left) + var(--gizmo-size) + 8px); bottom: calc(var(--gizmo-bottom) + var(--split-reserve-bottom, 0px));
background: var(--float-panel-bg);
border: 1px solid var(--line-2);
border-radius: var(--radius);
padding: var(--sp-2) var(--sp-3);
font-size: var(--fs-base); color:var(--fg-1);
backdrop-filter: blur(10px);
z-index:var(--z-overlay-widget); pointer-events:none;
}
.dot{display:inline-block;width:15px;height:15px;border-radius:50%;margin-right:var(--sp-2);}
.dot-a{width:10px;height:10px;background:var(--axis-a);} .dot-b{width:10px;height:10px;background:var(--axis-b);} .dot-c{width:10px;height:10px;background:var(--axis-c);}
.legend-row{display:flex;align-items:center;gap:var(--sp-2);line-height:1.2;}

/* Axes menu (ui/GizmoDrag.js): a body-level dropdown opened by long press at
   the press coordinates, so the gizmo's overflow clipping cannot affect it. */
.cv-gizmo-menu-wrap {
  position: fixed;
  z-index: var(--z-gizmo-controls);
  pointer-events: none;
}
.cv-gizmo-menu-wrap .cv-colorbar-menu {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: auto;
}
.cv-gizmo-dragging {
  opacity: 0.85;
}

/* Bottom-right resize grip (ui/GizmoDrag.js). The element is a 24px hit zone;
   its visible bracket remains a smaller corner mark. */
.cv-gizmo-resize-handle {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 24px;
  height: 24px;
  cursor: nwse-resize;
  pointer-events: auto;
  opacity: 0.3;
  transition: opacity 0.15s ease;
}
#axesGizmo:hover .cv-gizmo-resize-handle,
#axesGizmo.cv-gizmo-dragging .cv-gizmo-resize-handle {
  opacity: 0.7;
}
.cv-gizmo-resize-handle:hover {
  opacity: 1;
}
/* currentColor, not a hardcoded white: #axesGizmo's own `color` is kept in
   sync with the scene's contrast-safe color by ui/GizmoDrag.js (the same
   currentContrastColor() the floating color bars' tick labels use), so this
   bracket doesn't wash out against a light background. */
.cv-gizmo-resize-handle::before {
  content: "";
  position: absolute;
  right: 0;
  bottom: 0;
  width: 14px;
  height: 14px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  border-radius: 0 0 3px 0;
  opacity: 0.85;
}
#axesGizmo.cv-gizmo-locked .cv-gizmo-resize-handle {
  display: none;
  pointer-events: none;
}



/* ===================================================================
   Scene toolbars — the floating "Measure" (#measurementTools) and "View"
   (#cameraTools) windows share one control language so they read as a pair:

     .cv-tb        the row: segments and standalone buttons, one gap
     .cv-tb-group  a segment: related buttons fused edge to edge, sharing
                   one border and the outer corners (x|y|z, distance|angle|clear)
     .cv-tb-btn    a --tb-btn square; standalone ones keep all four corners

   Colour carries state only: rest is the plain button fill, hover an accent
   ring, and a filled/ringed segment means "this mode is on" (an active
   measure tool, the camera unlocked). No lift on hover — a segment that
   jumps out of its group breaks the group.
   =================================================================== */
/* --tb-btn: the square every scene control is cut to — toolbar buttons, the
   Structure window's formula header, the dock toggle's glyph. --tb-pad: the
   body padding of the scene windows around them, so all of them come out
   the same height (--tb-btn + 2*--tb-pad + border). */
:root {
  --tb-btn: 40px;
  --tb-pad: var(--sp-3);
}

.cv-tb {
  --tb-radius: var(--radius);
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  /* Backstop only: the rows are sized to fit at every rung, but a stray font
     scale still must not push a segment out of the panel body. */
  flex-wrap: wrap;
}

.cv-tb-group {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
}

.cv-tb-btn {
  position: relative;
  box-sizing: border-box; /* <button> already is; the dock toggle's <span> is not */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: var(--tb-btn);
  height: var(--tb-btn);
  min-width: var(--tb-btn);
  min-height: var(--tb-btn);
  padding: 0;
  margin: 0;
  background: var(--bg-color);
  /* --wash-4 rather than the palette's --border-color: on the light palettes
     the accent-tinted border at 0.4 alpha all but vanishes against the
     pastel fill, and the fused seams need to be seen to read as segments.
     --wash-4 is ink-alpha on light and white-alpha on dark, so it holds up
     on both. */
  border: 1px solid var(--wash-4);
  border-radius: var(--tb-radius);
  color: var(--fg-1);
  font-size: var(--fs-xl);
  font-weight: var(--fw-bold);
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}

/* Fused segments: neighbours overlap by the 1px border so the seam is one
   line, and only the two ends keep rounded corners. Camera segments hold
   .camera-axis-stack wrappers (arrows + letter) rather than bare buttons, so
   each rule targets both a direct button and a button inside a wrapper. */
.cv-tb-group > * + * {
  margin-left: -1px;
}

.cv-tb-group > .cv-tb-btn,
.cv-tb-group > * > .cv-tb-btn {
  border-radius: 0;
}

.cv-tb-group > .cv-tb-btn:first-child,
.cv-tb-group > :first-child > .cv-tb-btn {
  border-top-left-radius: var(--tb-radius);
  border-bottom-left-radius: var(--tb-radius);
}

.cv-tb-group > .cv-tb-btn:last-child,
.cv-tb-group > :last-child > .cv-tb-btn {
  border-top-right-radius: var(--tb-radius);
  border-bottom-right-radius: var(--tb-radius);
}

/* Hover/active raise the segment so its ring paints over the neighbours'
   shared border instead of being cut by it. */
.cv-tb-btn:hover,
.cv-tb-btn:focus-visible {
  z-index: 1;
  border-color: var(--accent-fg);
  box-shadow: 0 0 0 1px var(--accent-fg), 0 0 6px 1px rgb(var(--btn-glow-rgb) / 0.35);
}

.cv-tb-btn:focus-visible {
  outline: none;
}

/* Outranks panelWindow.css's `.cv-panel-body button` (--fs-md), which is what
   these render under once adopted into their windows. */
.cv-tb .cv-tb-btn {
  font-size: var(--fs-xl);
}

/* Standing mode ON (measure .active, camera lock .unlocked): a full ring in
   the tool-active colour, kept when hovered so the mode never reads as off
   under the pointer. */
.cv-tb-btn.active,
.cv-tb-btn.active:hover {
  z-index: 2;
  border-color: var(--tool-active);
  box-shadow: 0 0 0 1px var(--tool-active), 0 0 6px 1px rgb(var(--tool-active-rgb) / 0.4);
}

/* Hairline between a segment/action and a control of another kind (the
   camera lock after Reset). */
.camera-separator {
  flex: 0 0 auto;
  width: 1px;
  height: calc(var(--tb-btn) - 10px);
  background: var(--line-2);
}

/* The three scene windows (Measure, View, Structure) share one body padding
   so their rows come out the same height and their contents sit the same
   distance from the frame. */
.cv-panel.cv-floating:is([data-panel-id="measure"], [data-panel-id="view"], [data-panel-id="info"]) > .cv-panel-body {
  padding: var(--tb-pad);
}

/* --- Measurement tools --- (hosted by the unified "Measure" panel window,
   which provides positioning + chrome; this is just the content layout) */
#measurementTools {
  display: flex;
  align-items: center;
}

/* Until ui/panels/defaultPanels.js adopts them into their floating panel
   windows, these start as direct <body> children. <body> is display:flex, so
   keep them out of the layout — otherwise they steal width from #view and the
   renderer sizes its canvas to the squeezed width at startup. The selectors
   stop matching once the elements are reparented into a panel body. */
body > #measurementTools,
body > #cameraTools,
body > #structureInfoContent {
  display: none;
}

/* Icon-only. The full-colour SVGs (white fill, dark stroke, orange accent)
   read on every palette without a filter; the box is a hair smaller than the
   button so the ruler/protractor keep their breathing room. */
.measure-tool-btn .tool-icon {
  width: 28px;
  height: 22px;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* The white-filled shapes wash out on a pastel fill; a hairline shadow in the
   palette's own shadow ink gives them an edge without recolouring them
   (--icon-filter would flip the orange accents too). */
.measure-tool-btn .tool-icon img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 0 0.6px rgb(var(--shadow-rgb) / 0.7));
}

/* Opt-in labels (Visual panel > "Measurement tool labels"): the buttons drop
   the square lock and grow a caption under the icon, so the toolbar reads as
   named tools again. The label is the same .visually-hidden span the icons
   otherwise carry for assistive tech — here it's un-clipped back into flow. */
#measurementTools.show-tool-labels .measure-tool-btn {
  flex-direction: column;
  gap: 3px;
  width: auto;
  height: auto;
  min-height: var(--tb-btn);
  padding: 4px 8px;
}

#measurementTools.show-tool-labels .measure-tool-btn .visually-hidden {
  position: static;
  width: auto;
  height: auto;
  margin: 0;
  overflow: visible;
  clip: auto;
  font-size: var(--fs-sm);
  font-weight: var(--fw-normal);
  line-height: 1;
}

/* --- Camera view tools --- (hosted by the unified "View" panel window) */
#cameraTools {
  display: flex;
  align-items: center;
}

/* Reset | divider | Lock, wrapped by setupCameraButtons
   (WindowAndSceneControls.js) so the trio moves as one unit. */
.camera-reset-stack {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  flex: 0 0 auto;
}

/* The cube glyph is white faces + dark edges, legible on the plain fill. */
#cameraTools .camera-tool-reset-btn .reset-icon {
  display: block;
  width: 20px;
  height: 20px;
}

#cameraTools .camera-tool-btn span {
  display: none;
}

.camera-icon {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Wears the plain .cv-tb-btn fill and border, same as the reset and axis-view
   buttons — the .unlocked ring below is what marks the standing mode. */
#cameraTools .camera-lock-btn:hover {
  border-color: var(--accent-fg);
}

#cameraTools .camera-lock-btn svg {
  width: 18px;
  height: 18px;
}

/* Unlocked = the mode is on: same ring as an active measure tool. */
#cameraTools .camera-lock-btn.unlocked,
#cameraTools .camera-lock-btn.unlocked:hover {
  z-index: 2;
  color: var(--tool-active);
  border-color: var(--tool-active);
  box-shadow: 0 0 0 1px var(--tool-active), 0 0 6px 1px rgb(var(--tool-active-rgb) / 0.4);
}

/* World (x/y/z) and lattice (a/b/c) triplets: each is a fused .cv-tb-group
   of .camera-axis-stack wrappers. The wrapper is only there so the step
   arrows have somewhere to hang; it must stay exactly one letter wide. */
.camera-axis-stack {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  flex: 0 0 auto;
}

/* The small step-rotation arrows flanking each axis button. Their visibility
   is governed by the View window's "Stepwise buttons" menu option — visibility,
   not display, so the stack's own height/gap never shifts when they appear/disappear.
   pointer-events follows visibility so a hidden arrow can't be clicked (or
   caught by the outside-click handler's own hit-testing) while invisible.

   In the default Long press mode (and Off) they are transient popovers, so
   they sit OUT of flow, overlaid just above/below the letter: the row stays
   one button tall, level with the Measure toolbar, and the letter under a
   long-pressing finger never moves. In On mode they are permanent and go
   back in flow (below). */
#cameraTools .camera-axis-stack .axis-step {
  position: absolute;
  left: 0;
  right: 0;
  z-index: 3;
  padding: 0;
  border-width: 2px;
  border-radius: var(--radius-sm);
  font-size: var(--fs-xs);
  line-height: 1;
  height: 25px;
  min-width: 0;
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}

/* Hover matches the camera control buttons (.cv-tb-btn:hover): accent ring +
   glow, so the highlight is theme-dependent rather than a flat wash. */
#cameraTools .camera-axis-stack .axis-step:hover {
  transform: none;
  color: var(--fg-1);
  border-color: var(--accent-fg);
  box-shadow: 0 0 0 1px var(--accent-fg), 0 0 6px 1px rgb(var(--btn-glow-rgb) / 0.35);
}

#cameraTools .camera-axis-stack .axis-step:first-child {
  bottom: calc(100% + 1px);
}

#cameraTools .camera-axis-stack .axis-step:last-child {
  top: calc(100% + 1px);
}

#cameraTools .camera-axis-stack.camera-axis-revealed .axis-step {
  visibility: visible;
  opacity: 1;
  pointer-events: auto;
}

/* On mode: arrows are permanent, so they take real space above and below the
   letters and the whole toolbar grows to hold them. */
#cameraTools.camera-axis-steps-on .camera-axis-stack {
  gap: 1px;
}

#cameraTools.camera-axis-steps-on .camera-axis-stack .axis-step {
  position: static;
  min-width: var(--tb-btn);
}

/* The overlaid arrows spill a few px past the body's padding; the floating
   window body scrolls by default (long panels), which would clip them. This
   toolbar is never tall enough to need the scroll. */
.cv-panel.cv-floating[data-panel-id="view"] > .cv-panel-body {
  overflow: visible;
}

/* Both toolbars are narrower than a floating window's 220px minimum; let the
   window hug the row instead of leaving dead space after the last segment.
   The title bar (title + info/menu/close, ~100px) still fits. */
.cv-panel.cv-floating[data-panel-id="measure"],
.cv-panel.cv-floating[data-panel-id="view"] {
  min-width: 0;
}

/* "Restore All / Cancel" panel anchored below the Hide/Show mode button
 * (ui/MeasurementToolbar.js) — opened by long-pressing (mouse-hold or touch)
 * that button, not shown automatically. Fixed-positioned + placed via JS
 * (getBoundingClientRect on the button) rather than CSS-relative, so it
 * stays correct regardless of where the measurement toolbar itself is
 * docked. */
.restore-popover {
  position: fixed;
  z-index: var(--z-overlay-widget);
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  padding: var(--sp-2);
  min-width: 130px;
  background: var(--menu-bg);
  border: 1px solid var(--panel-border, var(--line-2));
  border-radius: var(--radius);
  box-shadow: 0 6px 18px rgb(var(--shadow-rgb) / 0.35);
}

.restore-popover[hidden] {
  display: none;
}

.restore-popover-btn {
  padding: 7px var(--sp-3-5);
  border-radius: var(--radius-sm);
  border: none;
  font-size: var(--fs-base);
  cursor: pointer;
  color: var(--panel-fg, var(--fg-1));
}

.restore-popover-all {
  background: var(--highlight-color);
}

.restore-popover-all:hover {
  filter: brightness(1.1);
}

.restore-popover-cancel {
  background: var(--wash-2);
}

.restore-popover-cancel:hover {
  background: var(--wash-4);
}

/* Shift+drag rectangle-select over the 3D view (ui/SceneInteraction.js) —
 * plain live outline, no masks/handles/toolbar like .cv-crop-rect above
 * (that one is a whole configure-then-confirm flow for PNG export; this is
 * a single live gesture that commits itself on pointerup). Pointer-events
 * none so it never itself becomes a raycasting/click target. */
.cv-drag-select-rect {
  position: fixed;
  z-index: var(--z-chrome);
  border: 1.5px solid var(--highlight-color, rgb(var(--danger-rgb)));
  background: rgb(var(--danger-rgb) / 0.12);
  pointer-events: none;
}

/* Marquee atom-SELECT variant (Shift+drag in no measure mode): amber accent
 * instead of hide mode's danger red, so a select-drag never looks like a
 * hide-drag. See dragSelectKind === 'select' in ui/SceneInteraction.js. */
.cv-drag-select-rect.select-mode {
  border-color: var(--highlight-color);
  background: rgb(255 140 0 / 0.14);
}

.tool-icon {
  width: 32px;
  height: 24px; /* Set a fixed height to match your tallest icon */
  display: flex;
  align-items: center;
  justify-content: center;
}

.tool-icon img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain; /* Ensures the SVG scales proportionally and is never cropped */
}
.scale-icon {
  transform: rotate(45deg);
}

.protractor-icon {
  transform: rotate(-15deg);
}

/* --- Floating distance label in scene --- */
.measure-label{
  position: relative;
  /* The whole CSS2DRenderer overlay has pointer-events:none (so labels never
   * block clicking through to the 3D scene below them) — opt this one
   * element back in, or its remove button below could never be hovered or
   * clicked at all. */
  pointer-events: auto;
  padding:var(--sp-2) 9px; border-radius: var(--radius-md); font-size: var(--fs-lg); font-weight:700;
  color:var(--fg-1); background:var(--label-bg); border:1px solid var(--line-2);
  backdrop-filter: blur(6px); white-space:nowrap;
}

/* Hover target for a measurement's remove button. The measurement's value
 * label is a depth-tested WebGL sprite now (so it clips correctly behind
 * atoms), which cannot host a DOM child — this invisible box is pinned to the
 * same anchor point purely to give the "x" somewhere to live and something to
 * hover over. */
.measure-remove-hotspot {
  position: relative;
  /* Width/height are set per frame from the label sprite's real on-screen box
   * (render/MeasurementModule.js) so the button pins to the pill's actual
   * top-right corner at any zoom or label size. */
  width: 46px;
  height: 26px;
  pointer-events: auto;
  --measure-remove-size: 16px;
}

/* Per-measurement remove button — hidden until the label is hovered, so it
 * doesn't clutter the view when several measurements are on screen at once. */
.measure-label-remove {
  position: absolute;
  /* Sits ON the pill's top-right corner (half outside), and scales with it —
   * --measure-remove-size is set per frame from the label's on-screen size. */
  top: calc(var(--measure-remove-size, 16px) / -2);
  right: calc(var(--measure-remove-size, 16px) / -2);
  width: var(--measure-remove-size, 16px);
  height: var(--measure-remove-size, 16px);
  font-size: calc(var(--measure-remove-size, 16px) * 0.72);
  padding: 0;
  border-radius: 50%;
  border: 1px solid rgba(0, 0, 0, 0.25);
  /* Solid white, not a theme/backdrop-blur tint — this button floats over
   * the 3D viewport itself, not a panel, and the viewport renders black
   * regardless of UI theme, so anything less than a fully opaque, fixed
   * light color reads as invisible against it. */
  background: #ffffff;
  color: #2a2a2a;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
  transition: opacity 0.15s ease, background 0.15s ease, color 0.15s ease;
}

.measure-label:hover .measure-label-remove,
.measure-remove-hotspot:hover .measure-label-remove {
  opacity: 1;
}

.measure-label-remove:hover {
  background: var(--danger-fill);
  color: #ffffff;
  border-color: rgba(255, 255, 255, 0.6);
}

/* Dock toggle - always top left. One more scene window: the same chrome as
   the floating panel bodies (padding --tb-pad, hairline, --radius-md) around
   a --tb-btn square, so it is exactly as tall as the View toolbar beside it
   and sits on its line. top: the toolbars anchor at 20px but their hidden
   title-bar handle (8px + 3px gap, panelWindow.css .cv-bar-collapsed) sits
   above the body, so the visible row starts at 31px. max() so a notch/rounded
   corner (only relevant with viewport-fit=cover, see index.html) pushes it
   clear instead of rendering underneath — env() is 0 on a rectangular
   screen. The View window's default left anchor (defaultPanels.js) clears
   this box: 12 + 58 + 12. */
#mobileMenuToggle {
  --tb-btn: 40px; /* pinned: does not follow the toolbars' touch size */
  position: fixed;
  top: max(31px, env(safe-area-inset-top));
  left: max(12px, env(safe-area-inset-left));
  z-index: var(--z-chrome);
  box-sizing: border-box;
  padding: var(--tb-pad);
  background: var(--popup-bg);
  border: 1px solid var(--line-1);
  border-radius: var(--radius-md);
  color: var(--fg-1);
  cursor: pointer;
  backdrop-filter: blur(10px);
  box-shadow: var(--shadow-glow);
  transition: transform 0.2s ease;
}

#mobileMenuToggle .cv-tb-btn {
  font-size: var(--fs-xl);
  font-weight: 300;
  pointer-events: none; /* the box is the click target, not the glyph */
}

#mobileMenuToggle:hover .cv-tb-btn {
  border-color: var(--accent-fg);
  box-shadow: 0 0 0 1px var(--accent-fg), 0 0 6px 1px rgb(var(--btn-glow-rgb) / 0.35);
}

#mobileMenuToggle:active {
  transform: scale(0.95);
}

/* Prevent text selection on touch devices */
#view canvas,
.vbtn,
#mobileMenuToggle,
body {
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}

/* Allow text selection only where needed */
#ui input[type="number"],
#ui label,
#ui h2,
#ui h3,
#composition,
#status,
#piSummary,
#piSplitSummary {
  -webkit-user-select: text;
  -moz-user-select: text;
  -ms-user-select: text;
  user-select: text;
}

/* Mobile and Tablet Responsive Styles now live in styles/responsive.css
   (compact/short rungs), per Wave 4's single-file @media rule. The
   .camera-separator/.background-dot/#cameraPanel wrap overrides that used
   to sit in a 1320px breakpoint here were already removed pre-Wave-4 —
   they live unconditionally in the base rules now. */

/* Tracer-only controls (the ray/path-tracing material editors in the
   Structure window, and single rows like the Visual window's "Element
   Materials Map" dropdown) show only while a tracer pipeline is active. The
   body class is driven by ui/ColorPanel.js updateRenderingControlsVisibility().
   The material data itself is always persisted, pipeline regardless. */
body:not(.tracer-pipeline) .material-editor,
body:not(.tracer-pipeline) .tracer-only-control {
  display: none;
}

/* Settings > Measurements (ui/MeasurementSettingsPanel.js). Seven controls,
 * so they are split into labelled sub-groups with comfortable spacing rather
 * than one dense wall of rows. */
.measure-settings-group { display: flex; flex-direction: column; gap: 10px; }
.measure-settings-subgroup { display: flex; flex-direction: column; gap: 6px; }
.measure-settings-subhead {
  font-size: var(--fs-sm);
  opacity: 0.62;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin: 2px 0 0;
}
.measure-settings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  margin: 0;
  line-height: 1.3;
}
.measure-settings-row > span:first-child { flex: 0 0 auto; }
.measure-settings-slider {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1 1 auto;
  justify-content: flex-end;
}
.measure-settings-slider input[type="range"] { width: 104px; margin: 0; }
.measure-settings-slider .slider-value { min-width: 2.6em; text-align: right; }
.measure-settings-row select { padding: 2px 6px; max-width: 130px; }
.measure-settings-row .color-swatch-btn { width: 30px; height: 18px; }

/* WebGL startup-failure alert (ui/WindowAndSceneControls.js). It renders while
   app init is aborting, which is why it was written with inline styles — but
   theme.css and this file are static <link>s in <head>, so both are parsed
   before any module runs. Only the async #theme-active override could be
   missing, and every token below has a base value. */
.cv-webgl-fail-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--z-confirm);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--overlay);
}

.cv-webgl-fail-box {
  max-width: 520px;
  margin: var(--sp-5);
  padding: var(--sp-5) var(--sp-6);
  border-radius: var(--radius-md);
  background: var(--popup-bg);
  color: var(--ink-5);
  font-family: var(--font-ui);
  font-size: var(--fs-lg);
  line-height: var(--lh-normal);
  box-shadow: 0 8px 40px rgb(var(--shadow-rgb) / 0.6);
}

.cv-webgl-fail-title {
  margin: 0 0 var(--sp-3-5) 0;
  font-size: var(--fs-xl);
  color: var(--fg-1);
}

.cv-webgl-fail-text {
  margin: 0 0 var(--sp-3-5) 0;
}

.cv-webgl-fail-text:last-of-type {
  margin-bottom: var(--sp-5);
}

.cv-webgl-fail-close {
  padding: var(--sp-2) var(--sp-5);
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  background: var(--chrome-4);
  color: var(--fg-1);
  font-family: var(--font-ui);
  font-size: var(--fs-lg);
}
