/* Shared controls for the structure workspace. */
svg {
  width: 15px;
  height: 15px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  height: 34px;
  padding: 0 13px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: linear-gradient(180deg, rgba(255,255,255,0.075), var(--raised));
  color: var(--text);
  font-size: 12.5px;
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.07), 0 1px 2px rgba(0,0,0,0.2);
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
              transform 0.08s var(--ease), opacity 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.btn:hover:not(:disabled) { background: linear-gradient(180deg, var(--raised-hi), var(--raised)); border-color: var(--line-hi); box-shadow: inset 0 1px 0 rgba(255,255,255,0.1), 0 4px 12px rgba(0,0,0,0.18); }
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn.wide { width: 100%; }
.btn.ghost { background: transparent; border-color: transparent; color: var(--dim); }
.btn.ghost:hover:not(:disabled) { background: var(--raised); color: var(--text); }

/* The one action that commits a change, so it is the one thing that is not
   grey. */
.btn.primary {
  background: linear-gradient(180deg, #55e4f4 0%, var(--accent) 54%, var(--accent-deep) 100%);
  border-color: transparent;
  color: #04222a;
  font-weight: 650;
  box-shadow: 0 2px 12px rgba(34, 211, 238, 0.24);
}
.btn.primary:hover:not(:disabled) { filter: brightness(1.08); }
.btn.primary:disabled { box-shadow: none; }
.btn.busy { position: relative; color: transparent; }
.btn.busy::after {
  content: "";
  position: absolute;
  width: 14px;
  height: 14px;
  border: 2px solid rgba(4, 34, 42, 0.35);
  border-top-color: #04222a;
  border-radius: 50%;
  animation: spin 0.6s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-xs);
  background: transparent;
  color: var(--dim);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s var(--ease), color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.icon:hover:not(:disabled) { background: var(--raised-hi); color: var(--text); }
.icon:disabled { opacity: 0.3; cursor: not-allowed; }
.icon.on { background: var(--raised-hi); color: var(--accent); box-shadow: inset 0 1px 0 rgba(255,255,255,0.1); }

/* Segmented control: one lit option out of a few, which is the right shape
   for "which tool" and "how does this join the selection". */
.seg {
  display: flex;
  gap: 2px;
  padding: 3px;
  border-radius: var(--radius-sm);
  background: var(--sunken);
  border: 1px solid var(--line);
}
.seg button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  flex: 1;
  min-width: 0;
  height: 28px;
  padding: 0 8px;
  border: 0;
  border-radius: var(--radius-xs);
  background: transparent;
  color: var(--dim);
  font-size: 11.5px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.seg button svg { width: 13px; height: 13px; }
.seg button:hover { color: var(--text); }
.seg button.on {
  background: var(--raised-hi);
  color: var(--text);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
.seg .sign { font-weight: 700; opacity: 0.85; }

.chips { display: flex; flex-wrap: wrap; gap: 5px; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 24px;
  padding: 0 9px;
  border: 1px solid var(--line);
  border-radius: 100px;
  background: var(--raised);
  color: var(--dim);
  font-size: 11.5px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.chip.subtle { background: transparent; border-color: transparent; color: var(--faint); }
button.chip { cursor: pointer; transition: all 0.15s var(--ease); }
button.chip:hover:not(:disabled) {
  background: var(--raised-hi);
  border-color: var(--line-hi);
  color: var(--text);
}
button.chip:disabled { opacity: 0.32; cursor: not-allowed; }
button.chip.on { background: var(--accent-wash); border-color: var(--accent); color: var(--accent); }

.text {
  width: 100%;
  height: 32px;
  padding: 0 11px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--sunken);
  color: var(--text);
  font-size: 12.5px;
  outline: none;
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.text::placeholder { color: var(--faint); }
.text:focus {
  border-color: var(--accent-deep);
  box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.12);
}

/* A real switch rather than a checkbox: these two settings change what the
   operation does, and deserve to look like settings. */
.switch {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
}
.switch input { position: absolute; opacity: 0; pointer-events: none; }
.switch .track {
  position: relative;
  flex: none;
  width: 32px;
  height: 18px;
  margin-top: 1px;
  border-radius: 100px;
  background: var(--sunken);
  border: 1px solid var(--line);
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease);
}
.switch .track::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--faint);
  transition: transform 0.22s var(--ease-spring), background 0.18s var(--ease);
}
.switch input:checked + .track { background: rgba(34, 211, 238, 0.22); border-color: var(--accent-deep); }
.switch input:checked + .track::after { transform: translateX(14px); background: var(--accent); }
.switch input:focus-visible + .track { box-shadow: 0 0 0 3px var(--accent-wash); }
.switch-text { display: flex; flex-direction: column; font-size: 12.5px; line-height: 1.35; }
.switch-text small { color: var(--faint); font-size: 11px; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

#panel::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.2); }

.card {
  flex: none;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--glass);
  -webkit-backdrop-filter: blur(20px) saturate(1.6);
  backdrop-filter: blur(20px) saturate(1.6);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 15px 17px 0;
}
.card-title { min-width: 0; }
.card-head h2, .card-head h3 {
  margin: 0;
  font-size: 12px;
  font-weight: 660;
  letter-spacing: 0.01em;
  color: var(--text);
}
.card-title p {
  margin: 2px 0 0;
  color: var(--faint);
  font-size: 10.5px;
  line-height: 1.35;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.selection-group + .selection-group {
  padding-top: 2px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}
.count {
  margin-left: auto;
  min-width: 19px;
  max-width: 104px;
  height: 18px;
  padding: 0 6px;
  border-radius: 100px;
  background: var(--raised);
  color: var(--dim);
  font-size: 10.5px;
  font-weight: 600;
  line-height: 18px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.count.live { background: var(--accent-wash); color: var(--accent); }
.card-body {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 13px 17px 18px;
}

.labelled { display: flex; flex-direction: column; gap: 6px; }
.label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--dim);
}
.stack { display: flex; flex-direction: column; gap: 6px; }
.row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.field-row { display: flex; gap: 6px; }
.field-row .text { flex: 1; min-width: 0; }
.atom-replace {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.atom-replace .field-row .text { max-width: 120px; }

.element-dialog {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(4, 7, 12, 0.78);
  -webkit-backdrop-filter: blur(7px);
  backdrop-filter: blur(7px);
  z-index: 60;
}
.element-dialog[hidden] { display: none; }
.element-dialog-body {
  width: min(920px, 100%);
  padding: 18px;
  border: 1px solid var(--line-hi);
  border-radius: var(--radius);
  background: var(--glass-solid);
  box-shadow: var(--shadow-3);
  animation: modal-in 0.24s var(--ease-spring);
}
.element-dialog-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 16px;
}
.element-dialog-head h2 { margin: 0; font-size: 16px; }
.element-dialog-head p { margin: 3px 0 0; color: var(--dim); font-size: 11.5px; }
.periodic-table {
  display: grid;
  grid-template-columns: repeat(18, minmax(28px, 1fr));
  grid-template-rows: repeat(10, 34px);
  gap: 4px;
  overflow-x: auto;
}
.element-tile {
  min-width: 28px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-xs);
  background: var(--raised);
  color: var(--text);
  font-size: 11px;
  font-weight: 650;
  cursor: pointer;
  transition: background 0.12s var(--ease), border-color 0.12s var(--ease), transform 0.18s var(--ease-spring);
}
.element-tile:hover {
  border-color: var(--accent);
  background: var(--accent-wash);
  color: var(--accent);
  transform: translateY(-1px);
}
.element-tile.f-block { color: var(--dim); }

/* A small block of state - what is selected, what fragment is loaded. */
.readout {
  padding: 10px 11px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--sunken);
  color: var(--dim);
  font-size: 11.5px;
  line-height: 1.5;
  word-break: break-word;
}
.readout.on { border-color: var(--accent); color: var(--text); }
.readout b { color: var(--text); font-weight: 600; }
.readout .mono {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  font-size: 11px;
  color: var(--dim);
}

/* ---------------------------------------------------------------------
 * regions
 *
 * The panel's centrepiece. Each card is one distinct building unit and
 * every instance of it, which is the thing a user substitutes.
 * ------------------------------------------------------------------ */

.regions { display: flex; flex-direction: column; gap: 6px; }
/* The fragment library is a dropdown rather than a wall of chips: the
   catalogue outgrew the space, and a select collapses to one line while
   keeping the category grouping as optgroups. */
.palette-select { width: 100%; }
.palette-note { line-height: 1.45; }

/* The structure-tools bar: four full-width actions rather than four
   icons, because the labels are the point. "Remove guests" is not
   guessable from a glyph, and this is the card someone meets first. */
.tool-bar { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; }
.tool-action {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 7px;
  min-height: 34px;
  padding: 0 11px;
  text-align: left;
}
.tool-action svg { flex: none; width: 15px; height: 15px; }
#structure-tools-note:empty { display: none; }
#structure-tools-note { margin-top: 9px; }
@media (max-width: 1180px) {
  .tool-bar { grid-template-columns: 1fr; }
}

/* The experimental comparison. A compact drop target, since the card
   already carries a plot, two tables and a caveat. */
.drop.compact { padding: 13px; gap: 3px; }
.drop.compact svg { width: 18px; height: 18px; }
.drop.over { border-color: var(--accent); background: var(--accent-wash); }
.field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
.warning-note {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 11px;
  border-radius: 9px;
  border: 1px solid var(--line-hi);
  background: var(--orbit-wash);
  color: var(--text);
  font-size: 11px;
  line-height: 1.5;
}
.warning-note .chip { flex: none; }
.caveat { margin: 4px 0 0; font-size: 10.5px; line-height: 1.55; }
.key-chip { display: inline-flex; align-items: center; gap: 5px; }
.key-chip i { width: 9px; height: 9px; border-radius: 2px; display: block; }
.key-chip:not(.on) { opacity: 0.45; text-decoration: line-through; }
#experimental-summary:empty { display: none; }
@media (max-width: 1180px) {
  .field-grid { grid-template-columns: 1fr; }
}

/* One card per analysis, each with its own compute action and its own
   empty state. */
.analysis-card .card-head .chip { margin-left: auto; }
.analyse-hint { margin: 0; font-size: 11px; }
/* `.info > div` is a two-column grid, so an empty-state line placed in it
   lands in the 86px label column and wraps. It is one message, not a
   label and a value. */
.info > .analysis-empty {
  display: block;
  grid-column: 1 / -1;
  font-size: 11px;
  color: var(--faint);
  font-style: italic;
  padding: 2px 0;
}
.table-wrap { overflow-x: auto; }
.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}
.data-table th {
  text-align: left;
  font: 600 9.5px/1.6 inherit;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--dim);
  padding: 0 8px 4px 0;
  white-space: nowrap;
}
.data-table td {
  padding: 5px 8px 5px 0;
  border-top: 1px solid var(--hairline, rgba(128, 128, 128, 0.18));
  color: var(--text);
  white-space: nowrap;
}
.data-table td:first-child { font-weight: 550; }
.data-table tr td:last-child, .data-table tr th:last-child { padding-right: 0; }

/* The (i) disclosure. A button rather than a `title` tooltip: `title` never
   shows on a touch screen, is read inconsistently by screen readers, and
   cannot be reached from the keyboard. */
.info-dot {
  flex: none;
  width: 15px;
  height: 15px;
  margin-left: 6px;
  padding: 0;
  border: 1px solid var(--hairline, rgba(128, 128, 128, 0.35));
  border-radius: 50%;
  background: transparent;
  color: var(--dim);
  font: 600 9.5px/1 var(--font-mono, monospace);
  cursor: pointer;
}
.info-dot:hover { color: var(--accent); border-color: var(--accent); }
.info-dot[aria-expanded="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.info-note {
  margin: 0 0 2px;
  padding: 9px 11px;
  border-radius: 9px;
  background: var(--raised);
  color: var(--dim);
  font-size: 11px;
  line-height: 1.55;
}
.card-head .info-dot { margin-right: auto; }
.card-title { display: flex; align-items: center; }

/* A glossary inside a disclosure. The dropdown it belongs to lists only
   the names of the cuts, so this is where each one says what it does; a
   definition list is what that is, and it reads as one where an em-dash
   gloss on six option lines did not. */
.info-terms {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  gap: 3px 10px;
  margin: 8px 0 0;
}
.info-terms dt { font-weight: 600; color: var(--text); }
.info-terms dd { margin: 0; }

/* A long canonical key: enough of it to recognise, all of it on the
   clipboard. */
#out-topology-key > div,
#out-fingerprint-hash > div { grid-template-columns: 66px minmax(0, 1fr) auto; }
.truncated { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.copy-chip { margin-left: 8px; }



/* The region chooser now lives inside the selection card, so its label
   carries the count the way a card header used to. */
#regions-card .label { display: flex; align-items: center; gap: 8px; }

.region {
  --tint: var(--accent);
  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-rows: auto auto;
  align-items: center;
  gap: 2px 10px;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--tint);
  border-radius: var(--radius-sm);
  background: var(--raised);
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
              transform 0.18s var(--ease-spring);
}
.region:hover { background: var(--raised-hi); transform: translateX(1px); }
.region.on {
  background: color-mix(in srgb, var(--tint) 15%, transparent);
  border-color: var(--tint);
}

.region .swatch {
  grid-row: 1 / 3;
  width: 11px;
  height: 11px;
  border-radius: 3px;
  background: var(--tint);
  box-shadow: 0 0 9px color-mix(in srgb, var(--tint) 55%, transparent);
}
.region .name {
  font-size: 12.5px;
  font-weight: 570;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.region .meta {
  grid-column: 2;
  font-size: 10.5px;
  color: var(--dim);
  font-variant-numeric: tabular-nums;
}
.region .tally {
  grid-row: 1 / 3;
  padding: 3px 8px;
  border-radius: 100px;
  background: var(--sunken);
  color: var(--dim);
  font-size: 11px;
  font-weight: 620;
  font-variant-numeric: tabular-nums;
}
.region.on .tally { background: color-mix(in srgb, var(--tint) 24%, transparent); color: var(--text); }

/* The kind badge - node or linker - reads at a glance without colour, which
   matters because colour is already carrying region identity. */
.region .kind {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 5px;
  border-radius: 4px;
  background: var(--sunken);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--dim);
  vertical-align: 1px;
}

/* Individual instances within a region, revealed when one is focused. Rarely
   needed - the point of regions is not to need them - so they are compact. */
.sites {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  padding-top: 2px;
}
.site {
  padding: 3px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-xs);
  background: var(--sunken);
  color: var(--dim);
  font-size: 10.5px;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: all 0.12s var(--ease);
}
.site:hover { border-color: var(--line-hi); color: var(--text); }
.site.on { background: var(--accent-wash); border-color: var(--accent); color: var(--accent); }

/* ---------------------------------------------------------------------
 * fragment drop zone
 * ------------------------------------------------------------------ */

.drop {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 15px 12px;
  border: 1px dashed var(--line-hi);
  border-radius: var(--radius-sm);
  background: var(--sunken);
  color: var(--dim);
  font-size: 12px;
  text-align: center;
  cursor: pointer;
  transition: all 0.15s var(--ease);
}
.drop svg { width: 17px; height: 17px; margin-bottom: 2px; color: var(--faint); }
.drop small { color: var(--dim); font-size: 10.5px; }
.drop:hover { border-color: var(--accent-deep); color: var(--text); }
.drop.over {
  border-color: var(--accent);
  background: var(--accent-wash);
  color: var(--text);
}

.ketcher-panel {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.ketcher-panel .muted {
  min-height: 16px;
  text-align: center;
}



.drawer-col { padding: 12px 16px; min-width: 0; display: flex; flex-direction: column; }
.drawer-col + .drawer-col { border-left: 1px solid var(--hairline-soft, rgba(255,255,255,0.06)); }
.drawer-head {
  display: flex; align-items: center; gap: 8px; margin-bottom: 9px; flex: none;
}
.drawer-head .label { margin: 0; }
.drawer-head .count { margin-left: auto; }

/* history as a timeline, so the order of edits is legible at a glance */
.timeline { overflow-y: auto; display: flex; flex-direction: column; }
.t-item {
  display: flex; gap: 10px; position: relative; padding-bottom: 13px;
  cursor: pointer; border: 0; background: none; text-align: left;
  color: inherit; font: inherit; padding-left: 0;
}
.t-item:last-child { padding-bottom: 0; }
.t-item::before {
  content: ""; position: absolute; left: 4px; top: 13px; bottom: -2px;
  width: 1px; background: var(--line);
}
.t-item:last-child::before { display: none; }
.t-dot {
  width: 9px; height: 9px; border-radius: 50%; background: var(--faint);
  flex: none; margin-top: 3px; z-index: 1;
}
.t-item.now .t-dot { background: var(--accent); box-shadow: 0 0 0 4px rgba(34, 211, 238, 0.18); }
.t-item:hover .t-label { color: var(--accent); }
.t-label { font-size: 12px; font-weight: 550; color: var(--text); }
.t-time { font-size: 10px; color: var(--faint); margin-top: 1px; }

/* the two structures */
.compare { display: flex; align-items: stretch; gap: 12px; flex: 1; min-height: 0; }
.compare-panel {
  flex: 1; min-width: 0; position: relative; border-radius: var(--radius-sm);
  background: rgba(2, 5, 9, 0.4); border: 1px solid var(--line); overflow: hidden;
}
.compare-panel canvas { width: 100%; height: 100%; display: block; cursor: grab; }
.compare-panel canvas:active { cursor: grabbing; }
.compare-tag {
  position: absolute; top: 7px; left: 7px; z-index: 2;
  font-size: 9.5px; font-weight: 650; letter-spacing: 0.04em;
  padding: 2px 8px; border-radius: 20px;
  background: rgba(255, 255, 255, 0.1); color: var(--dim);
}
.compare-tag.after { background: var(--good-wash); color: var(--good); }
.compare-arrow { display: flex; align-items: center; color: var(--faint); flex: none; }
.compare-empty {
  flex: 1; display: flex; align-items: center; justify-content: center;
  font-size: 11.5px; color: var(--faint); text-align: center; padding: 0 20px;
}
.compare-delta {
  position: absolute; bottom: 7px; left: 7px; right: 7px; z-index: 2;
  font-size: 10px; color: var(--dim); font-variant-numeric: tabular-nums;
  text-align: center;
}

.pxrd-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.pxrd-head .label { margin: 0; }
.pxrd-head button { margin-left: auto; height: 20px; font-size: 10.5px; }

.pxrd-plot { position: relative; cursor: crosshair; }
.pxrd-plot svg { display: block; width: 100%; }

.pxrd-tick { stroke: var(--accent); stroke-width: 1; opacity: 0.55; }
.pxrd-cursor { stroke: var(--accent); stroke-width: 1; opacity: 0.8; stroke-dasharray: 2 2; }

/* Anchored to the reflection, lifted clear of the curve, and never
   intercepting the pointer - it sits under the cursor by construction. */
.pxrd-tip {
  position: absolute;
  bottom: calc(100% + 6px);
  transform: translateX(-50%);
  pointer-events: none;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 7px 9px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-hi);
  background: var(--glass-solid);
  box-shadow: var(--shadow);
  font-size: 10.5px;
  line-height: 1.45;
  color: var(--dim);
  white-space: nowrap;
  z-index: 4;
}
.pxrd-tip[hidden] { display: none; }
.pxrd-tip b { font-size: 12px; color: var(--accent); font-variant-numeric: tabular-nums; }
.pxrd-tip em { font-style: normal; color: var(--faint); font-size: 9.5px; margin-top: 2px; }

.pxrd-plane { margin-top: 7px; font-size: 10.5px; line-height: 1.5; }
.pxrd-plane[hidden] { display: none; }
.pxrd-plane b { color: var(--accent); }

/* --- fragment library palette -------------------------------------------
   Grouped by what a fragment is for, because that is how the choice is
   made: you are replacing a linker, or hanging a substituent, and the two
   lists never mix. An entry that cannot fill the current selection is
   dimmed rather than removed - it still previews, and seeing that the
   library holds a three-connected core is worth more than a shorter list. */
.palette-head { display: flex; align-items: center; gap: 8px; }
.palette-head .label { margin: 0; }
.palette-head button { margin-left: auto; height: 20px; font-size: 10.5px; }
/* .chip sets an explicit display, which beats the [hidden] attribute on its
   own - the toggle is meaningless until a selection gives it something to
   filter against, so it has to be told to stay down. */
.palette-head [hidden] { display: none; }

.palette {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-height: 208px;
  overflow-y: auto;
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--sunken);
}
.palette-group { display: flex; flex-direction: column; gap: 6px; }
.palette-group .label { opacity: 0.72; }

.palette .chip .n {
  opacity: 0.5;
  font-variant-numeric: tabular-nums;
}
/* Deliberately not :disabled - these stay clickable, because previewing a
   fragment that does not fit the current sites is a reasonable thing to do
   on the way to selecting different ones. */
button.chip.off { opacity: 0.34; }
button.chip.off:hover { opacity: 0.72; }

.fragment-preview {
  position: relative;
  height: 178px;
  overflow: hidden;
  border: 1px solid rgba(34, 211, 238, 0.28);
  border-radius: var(--radius-sm);
  background: #0b0f17;
}
.fragment-preview[hidden] { display: none; }
.fragment-preview canvas {
  display: block;
  width: 100%;
  height: 100%;
  cursor: grab;
  touch-action: none;
}
.fragment-preview canvas:active { cursor: grabbing; }
.fragment-preview-caption {
  position: absolute;
  left: 8px;
  right: 8px;
  bottom: 7px;
  display: flex;
  justify-content: space-between;
  gap: 7px;
  color: var(--faint);
  font-size: 9.5px;
  line-height: 1;
  pointer-events: none;
}
.fragment-preview-caption span {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.fragment-preview-caption i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 6px rgba(34, 211, 238, 0.75);
}
.fragment-preview.unavailable canvas { display: none; }
.fragment-preview.unavailable::before {
  content: "3D preview unavailable in this browser";
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--faint);
  font-size: 11.5px;
}

/* ---------------------------------------------------------------------
 * the drawing modal
 *
 * Ketcher is a 30 MB iframe with its own light-themed interface, so the
 * modal frames it rather than trying to restyle it: dark chrome around a
 * light canvas, with the two decisions - cancel, or use this - in the head
 * where they are found without scrolling the editor.
 * ------------------------------------------------------------------ */

.modal {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(4, 7, 12, 0.72);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  z-index: 50;
  animation: fade 0.16s var(--ease);
}
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

@keyframes modal-in { from { opacity: 0; transform: scale(.97) translateY(4px); } to { opacity: 1; transform: scale(1) translateY(0); } }
.modal-body {
  display: flex;
  flex-direction: column;
  width: min(1180px, 100%);
  height: min(820px, 100%);
  border: 1px solid var(--line-hi);
  border-radius: var(--radius);
  background: var(--glass-solid);
  box-shadow: var(--shadow-3);
  overflow: hidden;
  animation: modal-in 0.24s var(--ease-spring);
}
.modal-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  border-bottom: 1px solid var(--line);
  background: var(--raised);
}
.modal-head .muted { max-width: 46ch; line-height: 1.4; }
.modal-foot {
  padding: 8px 14px;
  border-top: 1px solid var(--line);
  background: var(--raised);
  font-size: 11.5px;
}

/* The editor itself. White because Ketcher is a light-themed application
   and a dark backing shows through its transparent canvas as grey. */
#ketcher-frame {
  flex: 1;
  width: 100%;
  border: 0;
  background: #fff;
}

/* Small text utilities, used by panels that build their own markup. */
.muted { color: var(--dim); font-size: 11.5px; }
.small { font-size: 11.5px; }
code {
  padding: 1px 5px;
  border-radius: 4px;
  background: var(--sunken);
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  font-size: 11px;
  color: var(--accent);
}

/* ---------------------------------------------------------------------
 * report and structure info
 * ------------------------------------------------------------------ */

.report { display: flex; flex-direction: column; gap: 3px; font-size: 11.5px; }
.report:empty { display: none; }
.report > * { white-space: pre-wrap; }
.good { color: var(--good); }
.warn { color: var(--orbit); }
.bad { color: var(--danger); }

.info { display: flex; flex-direction: column; gap: 5px; }
.info div {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  font-size: 11.5px;
  color: var(--faint);
}
.info b {
  color: var(--text);
  font-weight: 550;
  font-variant-numeric: tabular-nums;
}

/* The simulated pattern, drawn as an outline rather than a stick plot:
   what it is compared against is a measured curve. */
.pxrd {
  margin: 8px 0 0;
  padding: 8px 8px 6px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.18);
}
.pxrd svg { display: block; width: 100%; height: 96px; overflow: visible; }
.pxrd-fill { fill: var(--accent-wash); stroke: none; }
.pxrd-line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.1;
  vector-effect: non-scaling-stroke;
  stroke-linejoin: round;
}
.pxrd figcaption { margin-top: 5px; text-align: center; }

/* The reference workspace: restrained surfaces, an action rail and a contextual inspector. */
@font-face { font-family: Ubuntu; src: url('/app/assets/fonts/Ubuntu/Ubuntu-Regular.ttf') format('truetype'); font-style: normal; font-weight: 400; font-display: swap; }
@font-face { font-family: Ubuntu; src: url('/app/assets/fonts/Ubuntu/Ubuntu-Medium.ttf') format('truetype'); font-style: normal; font-weight: 500 600; font-display: swap; }
@font-face { font-family: Ubuntu; src: url('/app/assets/fonts/Ubuntu/Ubuntu-Bold.ttf') format('truetype'); font-style: normal; font-weight: 700 900; font-display: swap; }
:root {
  color-scheme: light;
  --text: #111318; --dim: #3f424a; --faint: #5c606a;
  --shell: #f7f7f9; --glass: #fff; --glass-solid: #fff;
  --raised: #eeeef0; --raised-hi: #e4e4e8; --sunken: #f8f8fa;
  --line: #e4e4e6; --line-hi: #d4d4d9; --canvas-bg: #fff; --cell-colour: #b9c2cf;
  --accent: #0088cc; --accent-deep: #0077b3; --accent-wash: rgba(0,136,204,.09);
  --good: #2fa36b; --good-wash: rgba(47,163,107,.12); --orbit: #c9820f; --danger: #d64545;
  --orbit-wash: rgba(201,130,15,.11);
  --desktop-1: #e8e8ea; --desktop-2: #c6c6cb;
  --radius: 16px; --radius-sm: 9px; --radius-xs: 7px;
  --shadow: 0 2px 8px #18233a08; --shadow-lift: 0 20px 65px #18233a25;
  --ease: cubic-bezier(.22,.61,.36,1);
  /* Used only for things that appear or settle into place - a popover, a
     modal, a switch knob - never for hover states, which should stay crisp. */
  --ease-spring: cubic-bezier(.34,1.56,.64,1);
  --rail-width: 224px; --inspector-width: 328px;
  --drawer-height: 0px; --font-mono: 'SF Mono', ui-monospace, Menlo, monospace;
  /* A 4px scale, so spacing stops being picked by eye per component. Not
     yet threaded through every rule below - adopt gradually - but every new
     rule should reach for one of these six instead of a bespoke value. */
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 20px; --space-6: 24px;
  /* Named type roles rather than per-component sizes. Same adoption note
     as spacing: the scale exists now so new and touched-up rules can move
     onto it, without a blanket find-replace across rules already tuned by
     eye against this exact layout. */
  --text-caption: 10.5px; --text-footnote: 11.5px; --text-body: 13px; --text-headline: 15px; --text-title: 20px;
  /* Elevation, named: base card, then a popover/menu level, then the modal
     lift. Each is one step up in blur and spread from the last, so the
     'how high off the page is this' answer stays consistent everywhere it
     is asked, instead of drifting component by component. */
  --shadow-1: var(--shadow);
  --shadow-2: 0 1px 3px #18233a24, 0 14px 38px #18233a2e;
  --shadow-3: var(--shadow-lift);
}
[data-theme="dark"] {
  color-scheme: dark;
  --text: #ededee; --dim: #a4a8b1; --faint: #858c98;
  --shell: #242426; --glass: #2c2c2e; --glass-solid: #2c2c2e;
  --raised: #38383c; --raised-hi: #45454a; --sunken: #252528;
  --line: #ffffff17; --line-hi: #ffffff26; --canvas-bg: #1c1c1e; --cell-colour: #555e70;
  --accent: #0a84ff; --accent-deep: #3399ff; --accent-wash: rgba(10,132,255,.16);
  --good: #32d74b; --good-wash: rgba(50,215,75,.12); --orbit: #ff9f0a; --danger: #ff453a;
  --orbit-wash: rgba(255,159,10,.14);
  --desktop-1: #4a4a50; --desktop-2: #1e1e20;
  --shadow-lift: 0 20px 65px #0006;
  --shadow-2: 0 1px 3px #0007, 0 14px 38px #0008;
  --shadow-3: var(--shadow-lift);
}
* { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body { margin: 0; height: 100%; overflow: hidden; }
body {
  font: 400 13px/1.5 Ubuntu, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  color: var(--text); -webkit-font-smoothing: antialiased;
  background: radial-gradient(ellipse at 10% 0%, var(--desktop-1), transparent 70%), linear-gradient(145deg, var(--desktop-1), var(--desktop-2));
  display: flex; align-items: center; justify-content: center; padding: 2.4vh 2.4vw;
}
button, input, select { font: inherit; color: inherit; }
button, label[for] { -webkit-tap-highlight-color: transparent; }
button { cursor: pointer; }
button:disabled { cursor: not-allowed; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
button, a, input, select { touch-action: manipulation; }
button, .card, #bar, #panel, #workflow { transition: background-color .18s, color .18s; }
svg { width: 16px; height: 16px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.65; stroke-linecap: round; stroke-linejoin: round; }
/* Keep the status bar at the bottom and let only the inner panels scroll.
   Unlike hidden, clip cannot scroll the entire workspace out of view and
   expose an empty block beneath the status bar when a control gets focus. */
.window { width: 100%; max-width: 1600px; height: 95.2vh; max-height: 1120px; display: grid; grid-template-rows: auto minmax(0, 1fr) auto; border-radius: 18px; background: var(--glass); box-shadow: 0 30px 80px #14202e33, 0 0 0 1px #14202e0a; overflow: clip; }
#stage { position: relative; }
#bar { height: 60px; flex: none; display: flex; align-items: center; gap: 12px; padding: 0 20px; border-bottom: 1px solid var(--line); background: var(--shell); }
.identity { display: flex; align-items: center; gap: 10px; flex: none; }
.brand-mark { width: 27px; height: 27px; display: grid; place-items: center; border-radius: 7px; color: white; background: linear-gradient(150deg,#37a6e0,var(--accent)); box-shadow: 0 1px 3px #0088cc33; }
.brand-mark svg { width: 19px; height: 19px; }
.brand-lockup { display: flex; flex-direction: column; line-height: 1.3; }
/* An h1 now, for the heading outline: the page had h2s and no h1 above
   them. It must not change size for that. */
.brand { margin: 0; font-size: 14px; font-weight: 500; letter-spacing: -.2px; }
.brand-caption { font-size: 9px; color: var(--faint); }
.primary-nav { display: flex; gap: 3px; border-radius: 8px; padding: 3px; background: var(--raised); margin-left: 12px; }
.primary-nav a { padding: 5px 12px; font-size: 12px; color: var(--dim); text-decoration: none; border-radius: 6px; white-space: nowrap; }
.primary-nav a[aria-current] { color: var(--text); background: var(--glass); box-shadow: 0 1px 3px #0001; }
.workspace-context { min-width: 0; max-width: 230px; display: flex; flex-direction: column; margin-left: 8px; }
.eyebrow { color: var(--faint); font-size: 9px; }
#formula { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: 10.5px/1.7 var(--font-mono); }
.spacer { flex: 1; }
.server-state { display: flex; gap: 6px; align-items: center; font-size: 10px; color: var(--dim); white-space: nowrap; }
.server-state i, .status-dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--good); flex: none; }
.server-state.offline i { background: var(--danger); }
#bar .group { display: flex; gap: 2px; padding-left: 10px; border-left: 1px solid var(--line); }

/* Accounts. Present only on the hosted build - the local one never loads the
   module that fills the account area, so it collapses to nothing.

   Every rule that hides something here is written `[hidden]`-first, because
   `hidden` loses to any rule that sets `display` - the same trap `.chip`
   documents further down this file, and the one that would otherwise leave a
   Sign in button showing next to a signed-in user. */
.account-area { display: flex; align-items: center; gap: 8px; padding-left: 10px; border-left: 1px solid var(--line); }
.account-area:empty { border: 0; padding: 0; }
.account-area [hidden] { display: none !important; }

/* The allowance pill. A button, not a label: it is the most obvious thing to
   click when you want to know where your allowance went. */
.allowance {
  display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 10px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--sunken);
  font-size: 10.5px; color: var(--dim); white-space: nowrap; cursor: pointer;
  font-family: inherit;
}
.allowance:hover { border-color: var(--line-hi); color: var(--text); }
.allowance::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--good); }
.allowance.low { color: var(--orbit); border-color: color-mix(in srgb, var(--orbit) 40%, var(--line)); }
.allowance.low::before { background: var(--orbit); }
.allowance.spent { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, var(--line)); background: color-mix(in srgb, var(--danger) 8%, var(--sunken)); }
.allowance.spent::before { background: var(--danger); }

/* One consistent entry point; personal details stay inside the panel. */
.account-control {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  flex: none; gap: 7px; min-width: 40px; min-height: 36px; padding: 0 10px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--glass);
  color: var(--text); font-family: inherit; font-size: 12px; cursor: pointer;
}
.account-control:hover { border-color: var(--line-hi); background: var(--raised); }
.account-control[aria-expanded="true"] { border-color: var(--accent); background: var(--accent-wash); }

/* Signed out, this is the one call to action in the header, and a neutral
   outline icon among a row of neutral outline icons is not one - it was the
   easiest control on the page to miss. Filled in the product's own accent it
   reads as "do this", while a signed-in user keeps the quiet treatment above:
   they have already done it, and an account button that keeps shouting is
   just noise. */
.account-control.signed-out {
  border-color: var(--accent-deep);
  background: var(--accent);
  color: #fff;
  font-weight: 500;
}
.account-control.signed-out:hover { border-color: var(--accent-deep); background: var(--accent-deep); }
.account-control.signed-out .account-chevron { color: rgba(255, 255, 255, 0.8); }
/* The error dot is red on blue, which is legible but muddy; a white ring
   lifts it off the fill. */
.account-control.signed-out.has-error::after { box-shadow: 0 0 0 1.5px #fff; }
.account-control > svg { width: 18px; height: 18px; }
.account-control .account-chevron { width: 12px; height: 12px; color: var(--faint); }
.account-control[aria-expanded="true"] .account-chevron { transform: rotate(180deg); }
.account-control.has-error::after { content: ""; position: absolute; top: 3px; right: 3px; width: 5px; height: 5px; border-radius: 50%; background: var(--danger); }
.account-name { white-space: nowrap; font-weight: 500; }
.account-avatar {
  display: grid; place-items: center; width: 22px; height: 22px; flex: none;
  border-radius: 50%; background: var(--accent) center/cover no-repeat;
  color: #fff; font-size: 10.5px; font-weight: 600; text-transform: uppercase;
}
.account-avatar.lg { width: 34px; height: 34px; font-size: 14px; }

/* Keep the dropdown on screen at narrow widths and allow long details to scroll. */
.account-panel {
  position: fixed; z-index: 60; width: min(306px, calc(100vw - 24px)); padding: 16px;
  box-sizing: border-box; overflow-y: auto;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--glass);
  box-shadow: 0 24px 60px rgba(0,0,0,0.22); color: var(--text);
}
.account-panel[hidden], .account-panel [hidden] { display: none !important; }
.account-welcome strong { display: block; font-size: 14px; font-weight: 500; }
.account-welcome p { margin: 6px 0 16px; font-size: 12px; line-height: 1.5; color: var(--dim); }
.account-panel-actions:has(> button:not([hidden])) { margin-top: 12px; }
.account-panel-actions .btn { min-height: 36px; }
.account-signout { margin-top: 6px; }
.account-panel-head { display: flex; align-items: center; gap: 11px; }
.account-panel-who { min-width: 0; }
.account-panel-name { display: block; font-size: 13px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-panel-email { display: block; font-size: 11px; color: var(--faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-panel-workspace { margin-top: 10px; font-size: 10.5px; color: var(--faint); text-transform: capitalize; }

.account-plan { margin: 14px 0 10px; padding: 12px; border-radius: var(--radius-sm); background: var(--sunken); border: 1px solid var(--line); }
.account-plan-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.account-plan-label { font-size: 13px; font-weight: 500; }
.account-plan-price { font-size: 11px; color: var(--dim); }
.account-meter { height: 5px; margin: 10px 0 8px; border-radius: 999px; background: var(--raised); overflow: hidden; }
.account-meter i { display: block; height: 100%; width: 0; border-radius: 999px; background: var(--accent); transition: width 0.3s var(--ease); }
.account-meter i.low { background: var(--orbit); }
.account-meter i.spent { background: var(--danger); }
.account-plan-price[hidden], .account-panel-email[hidden] { display: none; }
.account-plan-usage { margin: 0; font-size: 10.5px; color: var(--faint); }
.account-plan-points { margin: 0 0 14px; padding-left: 16px; font-size: 11px; color: var(--dim); line-height: 1.85; }

.account-panel-actions { display: grid; gap: 6px; }
.account-panel-actions .btn[hidden] { display: none; }
.account-signout { color: var(--dim); }
.account-signout:hover:not(:disabled) { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, var(--line)); }
.account-panel-note { margin: 12px 0 0; font-size: 10.5px; color: var(--faint); line-height: 1.6; }
.account-panel-note.bad { color: var(--danger); }
.account-panel-note[hidden] { display: none; }

/* Export, for somebody who cannot yet export. Deliberately quiet: it is a
   note about what happens next, not a refusal - the edit itself works.
   Suppressed while the button is disabled, because there is no structure to
   export yet and "Export Account" on a greyed-out control reads as a glitch
   rather than as a hint. */
.btn.needs-account:not(:disabled)::after { content: "Account"; font-size: 9px; opacity: 0.75; letter-spacing: 0.3px; }

/* The upgrade button when the allowance is gone. Not a colour change - the
   accent is already the loudest colour on the page - but a nudge that says
   this is the control that unblocks you. */
.account-upgrade.urgent { animation: account-nudge 2.4s var(--ease) 1; }
@keyframes account-nudge {
  0%, 100% { transform: none; }
  8%, 24% { transform: translateY(-2px); }
  16% { transform: none; }
}
@media (prefers-reduced-motion: reduce) { .account-upgrade.urgent { animation: none; } }

.account-prompt { max-width: 400px; border: 1px solid var(--line); border-radius: 14px; padding: 22px; background: var(--glass); color: var(--text); box-shadow: 0 30px 80px #14202e33; }
.account-prompt::backdrop { background: #14202e4d; }
.account-prompt h2 { margin: 0 0 8px; font-size: 16px; font-weight: 500; letter-spacing: -0.2px; }
.account-prompt-body { margin: 0 0 14px; font-size: 12px; color: var(--dim); line-height: 1.55; }
.account-prompt-points { margin: 0 0 18px; padding-left: 18px; font-size: 12px; color: var(--dim); line-height: 1.9; }
.account-prompt-points[hidden] { display: none; }
.account-prompt-plan { margin: 0 0 18px; padding: 14px 16px; border: 1px solid var(--accent); border-radius: var(--radius-sm); background: var(--accent-wash); }
.account-prompt-plan[hidden] { display: none; }
.account-prompt-plan-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
.account-prompt-plan-name { font-size: 13px; font-weight: 600; }
.account-prompt-plan-price { font-size: 15px; font-weight: 600; color: var(--accent); }
.account-prompt-plan .account-prompt-points { margin: 0; color: var(--text); font-size: 11.5px; line-height: 1.8; }
.account-prompt-actions { display: flex; justify-content: flex-end; gap: 8px; }
.account-prompt-actions .btn[hidden] { display: none; }

/* The cadence chooser. Every row buys the same thing, so the row shows what
   distinguishes them - the commitment, the monthly equivalent and the saving -
   rather than repeating the feature list three times. */
.account-cadences { display: grid; gap: 6px; margin-top: 14px; }
.account-cadences[hidden] { display: none; }
.account-cadence {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas: "label price" "permonth saving";
  gap: 2px 10px;
  padding: 10px 12px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--glass); color: var(--text);
  font-family: inherit; text-align: left; cursor: pointer;
}
.account-cadence:hover { border-color: var(--line-hi); }
.account-cadence.chosen { border-color: var(--accent); background: var(--glass); box-shadow: inset 0 0 0 1px var(--accent); }
.account-cadence-label { grid-area: label; font-size: 12.5px; font-weight: 600; }
.account-cadence-price { grid-area: price; font-size: 12.5px; font-weight: 600; }
.account-cadence-permonth { grid-area: permonth; font-size: 10.5px; color: var(--faint); }
.account-cadence-saving {
  grid-area: saving; justify-self: end; font-size: 10px; font-weight: 600;
  color: var(--good); background: var(--good-wash);
  padding: 1px 7px; border-radius: 999px;
}

.account-prompt-contact { margin: 14px 0 0; font-size: 11px; color: var(--faint); line-height: 1.6; }
.account-prompt-contact[hidden] { display: none; }
.account-prompt-contact a { color: var(--accent); }
.btn { height: 32px; padding: 0 11px; border: 1px solid var(--line); background: var(--glass); box-shadow: 0 1px 2px #00000006; font-size: 12px; font-weight: 400; }
.btn:hover:not(:disabled) { background: var(--raised); border-color: var(--line-hi); box-shadow: none; }
.btn.primary { color: white; background: var(--accent); font-weight: 500; box-shadow: 0 2px 6px #0088cc20; }
.btn.primary:hover:not(:disabled) { background: var(--accent-deep); filter: none; }
.btn:disabled { opacity: .45; }
.btn.busy::after { border-color: #ffffff55; border-top-color: white; }
.icon { border-radius: 7px; }
.icon.on { box-shadow: none; background: var(--accent-wash); }
.seg { border: 0; background: var(--raised); border-radius: 9px; }
.seg button { font-size: 11px; height: 27px; }
.seg button.on { background: var(--glass); box-shadow: 0 1px 3px #0002; }
.chip { height: 26px; border-radius: 7px; border-color: transparent; background: var(--raised); font-size: 11px; }
.chip.on, button.chip.on { color: var(--accent); background: var(--accent-wash); border-color: transparent; }
.text { background: var(--sunken); font-size: 12px; }
.switch { gap: 9px; }
.switch .track { background: var(--raised-hi); border: 0; }
.switch input:checked + .track { background: var(--good); }
.switch .track::after, .switch input:checked + .track::after { background: white; }
.switch input:focus-visible + .track { outline: 2px solid var(--accent); outline-offset: 3px; }
.switch-text { font-size: 12px; }
.switch-text small { color: var(--faint); font-size: 10.5px; line-height: 1.4; margin-top: 3px; }
.workspace-grid { flex: 1; min-height: 0; display: grid; grid-template-columns: var(--rail-width) minmax(0,1fr) var(--inspector-width); grid-template-rows: minmax(0,1fr) 34px var(--drawer-height); grid-template-areas: 'rail viewer inspector' 'rail disclosure disclosure' 'rail drawer drawer'; }
body.drawer-open { --drawer-height: 212px; }
#workflow { grid-area: rail; padding: 18px 12px 24px; background: var(--shell); border-right: 1px solid var(--line); overflow-y: auto; }
.load-action { width: 100%; justify-content: flex-start; height: 35px; }
.structure-file-actions { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 6px; }
.structure-file-actions .btn { min-width: 0; height: 36px; padding: 0 9px; justify-content: center; white-space: nowrap; font-size: 11px; }
.structure-file-actions .needs-account::after { display: none; }
.rail-group { padding: 12px 0 8px; margin-top: 8px; border-top: 1px solid var(--line); }
.rail-group h2 { font-size: 12px; font-weight: 500; margin: 0; padding: 3px 9px 7px; }
.rail-action { display: flex; align-items: center; width: 100%; padding: 7px 9px; color: var(--dim); border: 0; background: none; border-radius: 8px; text-align: left; font-size: 12px; line-height: 1.4; }
.rail-action:hover { background: var(--raised); color: var(--text); }
.rail-action.current { background: var(--accent-wash); color: var(--accent); }
.rail-action { color: var(--text); }
.rail-action:disabled { opacity: .4; }
#viewer { grid-area: viewer; display: flex; flex-direction: column; min-width: 0; min-height: 0; background: var(--canvas-bg); }
.viewbar { display: flex; align-items: center; gap: 8px; padding: 16px 18px 8px; flex: none; flex-wrap: wrap; }
.viewbar .icon { display: flex; gap: 6px; width: auto; height: 27px; font-size: 11px; white-space: nowrap; font-weight: 400; padding: 0 2px; background: none; color: var(--dim); }
.viewbar .seg button { white-space: nowrap; flex: auto; }
/* The structure-edit menu on the viewer bar. A popover rather than more
   buttons: the two operations that need no setup earn their own place on
   the bar, and the rest would only crowd it. */
.menu { position: relative; display: flex; }
.menu > .icon .chevron { width: 13px; height: 13px; opacity: 0.7; }
@keyframes menu-in { from { opacity: 0; transform: translateY(-4px) scale(.98); } to { opacity: 1; transform: translateY(0) scale(1); } }
.menu-list {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 40;
  min-width: 224px;
  padding: 5px;
  border: 1px solid var(--line-hi);
  border-radius: 11px;
  background: var(--glass-solid);
  /* The popover elevation level - one step above a card, one below a modal.
     Named now instead of hand-tuned per component, and it themes for free:
     the dark override below no longer needs its own copy of this shadow. */
  box-shadow: var(--shadow-2);
  transform-origin: top left;
  animation: menu-in 0.18s var(--ease-spring);
}
.menu-list button {
  display: block;
  width: 100%;
  padding: 7px 10px;
  border: 0;
  border-radius: 7px;
  background: none;
  color: var(--text);
  font: inherit;
  font-size: 12px;
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
}
.menu-list button:hover:not(:disabled) { background: var(--raised); }
.menu-list button:disabled { color: var(--faint); cursor: default; }
.menu-list hr {
  margin: 5px 8px;
  border: 0;
  border-top: 1px solid var(--line);
}
/* Edits sit left of the bar's spacer and the view toggles sit right of
   it, so "Add hydrogens" is never read as a sibling of "Show H". */
.menu > .icon[aria-expanded="true"] { background: var(--raised); color: var(--text); }

.mini-switch { width: 25px; height: 15px; background: var(--raised-hi); border-radius: 10px; position: relative; }
.mini-switch::after { content: ''; width: 11px; height: 11px; border-radius: 50%; position: absolute; top: 2px; left: 2px; background: white; box-shadow: 0 1px 2px #0003; transition: left .15s; }
.on .mini-switch { background: var(--good); }
.on .mini-switch::after { left: 12px; }
.scene { position: relative; flex: 1; min-height: 0; overflow: hidden; }
#canvas { display: block; width: 100%; height: 100%; touch-action: none; }
.viewer-controls { display: flex; align-items: center; justify-content: center; gap: 2px; padding: 8px 12px 16px; flex: none; }
.tool, .camera-reset { display: flex; flex-direction: column; align-items: center; gap: 5px; color: var(--dim); border: 0; background: transparent; border-radius: 9px; padding: 9px 12px; font-size: 10px; min-width: 42px; }
.tool:hover, .camera-reset:hover { background: var(--raised); }
.tool.on { background: var(--accent-wash); color: var(--accent); }
.hint { position: absolute; inset: 0; display: grid; place-items: center; pointer-events: none; }
.hint-card { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 24px; max-width: 100%; }
.hint-glyph { width: 48px; height: 48px; color: var(--accent); opacity: .65; }
.hint-card h1 { margin: 18px 0 6px; font-size: 20px; font-weight: 500; letter-spacing: -.5px; }
.hint-card p { margin: 0 0 20px; font-size: 12px; color: var(--faint); }
.hint-card .btn { pointer-events: auto; }
.band { display: none; position: absolute; z-index: 4; background: var(--accent-wash); border: 1px solid var(--accent); pointer-events: none; }
.hover { position: absolute; right: 16px; top: 16px; padding: 12px 14px; min-width: 160px; background: color-mix(in srgb, var(--glass) 90%, transparent); backdrop-filter: blur(15px); border-radius: 12px; box-shadow: var(--shadow-lift); opacity: 0; pointer-events: none; font: 11px/1.6 var(--font-mono); }
.hover.on { opacity: 1; }
.hover strong { font-family: Ubuntu, sans-serif; font-size: 13px; display: block; margin-bottom: 7px; }
.hover div { display: flex; justify-content: space-between; gap: 16px; }
.hover span { color: var(--dim); }
.legend { position: absolute; bottom: 5px; right: 16px; display: flex; gap: 10px; flex-wrap: wrap; max-width: 75%; pointer-events: none; font-size: 10px; color: var(--dim); }
.legend span { display: flex; align-items: center; gap: 4px; }
.legend i { width: 7px; height: 7px; border-radius: 50%; }
#axes { position: absolute; left: 18px; bottom: 8px; width: 66px; height: 66px; pointer-events: none; }
#axes text { stroke: none; }
#measure-readout { position: absolute; top: 16px; left: 16px; background: var(--glass); box-shadow: var(--shadow); border: 1px solid var(--line); border-radius: 8px; padding: 8px 12px; font-size: 11px; pointer-events: none; }
#panel { grid-area: inspector; min-height: 0; display: flex; flex-direction: column; background: var(--shell); border-left: 1px solid var(--line); }
.insp-tabs { flex: none; display: flex; padding: 0 12px; border-bottom: 1px solid var(--line); }
.insp-tabs button { flex: 1; border: 0; border-bottom: 2px solid transparent; background: none; padding: 16px 4px 13px; color: var(--dim); font-size: 11.5px; }
.insp-tabs button[aria-selected="true"] { color: var(--accent); border-bottom-color: var(--accent); }
.insp-tabs button { font-size: 12px; font-weight: 500; }
.inspector-scroll { overflow-y: auto; padding: 16px; flex: 1; min-height: 0; }
.card { background: none; border: 0; box-shadow: none; backdrop-filter: none; border-radius: 0; margin-bottom: 20px; overflow: visible; }
.card-head { padding: 0 2px 9px; gap: 8px; }
.card-head h2, .card-head h3 { font-size: 12px; font-weight: 500; letter-spacing: 0; }
.card-title p { white-space: normal; }
.card-body { border-radius: 12px; background: var(--glass); padding: 14px; gap: 12px; }
.label { font-size: 10.5px; letter-spacing: 0; text-transform: none; font-weight: 400; }
.count { color: var(--dim); font-weight: 400; background: var(--raised); }
.count.live { background: var(--accent-wash); color: var(--accent); }
.structure-meta { display: flex; align-items: center; gap: 12px; margin-bottom: 5px; }
.structure-icon { width: 46px; height: 46px; flex: none; border-radius: 9px; background: var(--sunken); display: grid; place-items: center; color: var(--accent); }
.structure-icon svg { width: 28px; height: 28px; }
.structure-meta > div { min-width: 0; }
.structure-meta strong { display: block; font-size: 13px; font-weight: 500; overflow-wrap: anywhere; }
.badge { display: inline-block; color: var(--accent); background: var(--accent-wash); border-radius: 20px; padding: 2px 7px; font-size: 9px; margin-top: 4px; }
.info { gap: 0; }
.info > div { display: grid; grid-template-columns: 86px minmax(0,1fr); gap: 8px; padding: 4px 0; font-size: 11px; color: var(--dim); }
.info b { font: 400 10.5px/1.65 var(--font-mono); overflow-wrap: anywhere; text-align: right; }
.selection-overview { color: var(--dim); font-size: 11px; }
.selection-overview strong { color: var(--text); font-weight: 500; }
.readout { padding: 8px 10px; border: 0; font-size: 11px; }
.readout:empty { display: none; }
.selection-group + .selection-group { border: 0; }
#clear { min-height: 28px; height: 28px; }
.atom-finders { padding-bottom: 12px; border-bottom: 1px solid var(--line); }
#find-sbu { color: var(--accent); background: var(--accent-wash); border-color: transparent; font-weight: 500; min-height: 36px; }
.edit-tools-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.edit-shortcut { display: flex; align-items: center; gap: 9px; min-height: 44px; padding: 10px; text-align: left; border: 1px solid var(--line); border-radius: 9px; background: var(--glass); color: var(--text); font-size: 11.5px; line-height: 1.35; }
.edit-shortcut svg { color: var(--accent); }
.edit-shortcut:hover { background: var(--accent-wash); border-color: var(--accent); }
.edit-shortcut span { overflow-wrap: anywhere; }
#structure-information { margin-top: 24px; }
#elements:empty::after { content: 'Elements appear when a structure is loaded.'; color: var(--faint); font-size: 10.5px; }
.palette { background: var(--sunken); border-color: var(--line); max-height: 280px; }
.palette .chip { white-space: normal; height: auto; min-height: 26px; text-align: left; }
.fragment-preview { background: var(--canvas-bg); border-color: var(--line); }
.fragment-preview canvas { background: var(--canvas-bg); }
.fragment-preview-caption { font-size: 9px; gap: 7px; flex-wrap: wrap; }
.drop { border-color: var(--line-hi); }
.job-status { display: flex; align-items: center; gap: 8px; color: var(--dim); font-size: 11px; }
progress { width: 100%; height: 4px; accent-color: var(--accent); }
body.busy .job-status .status-dot, body.busy #statusbar .status-dot { background: var(--orbit); animation: pulse 1s infinite alternate; }
@keyframes pulse { to { opacity: .3; } }
#drawer { grid-area: drawer; display: grid; grid-template-columns: minmax(220px, .8fr) minmax(0,1.6fr); min-height: 0; overflow: hidden; border-top: 1px solid var(--line); background: var(--shell); }
body:not(.drawer-open) #drawer { display: none; }
.drawer-col { padding: 16px 20px; min-width: 0; min-height: 0; }
.drawer-col + .drawer-col { border-color: var(--line); }
.drawer-head { margin-bottom: 12px; }
.drawer-head .label { font-size: 12px; font-weight: 500; }
.drawer-head .muted { font-size: 10px; margin-left: auto; }
.timeline { gap: 0; }
.t-item { padding-bottom: 12px; }
.t-label { font-size: 11px; font-weight: 400; }
.t-time { display: block; font-size: 9.5px; }
.t-item.now .t-dot { background: var(--accent); box-shadow: 0 0 0 3px var(--accent-wash); }
.empty-copy { color: var(--faint); font-size: 11px; }
.unsaved { color: var(--orbit); font-size: 10px; padding: 8px 0 0 19px; }
.compare { gap: 10px; }
.compare-panel { border-radius: 12px; background: var(--canvas-bg); }
.compare-tag { background: var(--raised); letter-spacing: 0; }
.compare-delta { padding: 2px 5px; background: color-mix(in srgb, var(--glass) 85%, transparent); border-radius: 4px; font-size: 9px; }
.compare-empty { border-radius: 12px; background: var(--sunken); border: 1px solid var(--line); line-height: 1.7; }
#statusbar { display: flex; align-items: center; gap: 16px; flex: none; height: 30px; padding: 0 16px; border-top: 1px solid var(--line); background: var(--shell); font-size: 9.5px; color: var(--faint); }
#statusbar > span { display: flex; align-items: center; gap: 6px; white-space: nowrap; }
#statusbar b { color: var(--dim); font-weight: 400; }
.drawer-disclosure { grid-area: disclosure; display: flex; align-items: center; gap: 9px; width: 100%; padding: 0 18px; border: 0; border-top: 1px solid var(--line); background: var(--shell); color: var(--dim); text-align: left; font-size: 11px; }
.drawer-disclosure:hover { background: var(--raised); color: var(--text); }
.drawer-disclosure .disclosure-chevron { width: 13px; height: 13px; transition: transform .15s; }
.drawer-disclosure[aria-expanded="true"] .disclosure-chevron { transform: rotate(90deg); }
#drawer-action { margin-left: auto; color: var(--accent); font-size: 10px; }
.shortcut-hint { font-size: 9px; }
@keyframes toast-in { from { opacity: 0; transform: translate(-50%, 10px); } to { opacity: 1; transform: translate(-50%, 0); } }
.toast { position: fixed; bottom: 42px; left: 50%; transform: translateX(-50%); max-width: min(560px,90vw); padding: 12px 18px; border: 1px solid var(--line); border-radius: 10px; background: var(--glass); box-shadow: var(--shadow-3); color: var(--text); font-size: 12px; z-index: 70; animation: toast-in 0.28s var(--ease-spring); }
.toast.bad { border-color: var(--danger); color: var(--danger); }
/* Completion messages must not intercept scrolling over the inspector. */
.toast { pointer-events: none; }
.element-dialog-body { max-height: 90vh; overflow: auto; }
.element-tile:hover { background: var(--accent-wash); }
.modal-head, .modal-foot { background: var(--shell); }
.pxrd { background: var(--sunken); border-color: var(--line); margin: 0; }
.pxrd-fill { fill: var(--accent-wash); }
.pxrd-tip { background: var(--glass); }
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-thumb { background: var(--line-hi); border-radius: 8px; }
::-webkit-scrollbar-track { background: transparent; }
@media (max-width: 1250px) {
  :root { --rail-width: 200px; --inspector-width: 304px; }
  #bar { gap: 9px; padding: 0 14px; }
  .server-state { display: none; }
  .brand-caption { font-size: 8px; }
  .primary-nav { margin-left: 0; }
  .workspace-context { max-width: 180px; }
  .viewbar { padding: 12px 12px 4px; gap: 5px; }
  .viewbar .spacer { display: none; }
  .viewbar .seg { width: 100%; }
  .viewbar .icon { margin-top: 4px; }
  .tool, .camera-reset { padding: 8px; min-width: 36px; }
  .inspector-scroll { padding: 12px; }
  .shortcut-hint { display: none !important; }
}
@media (max-width: 1000px) {
  body { padding: 0; }
  .window { height: 100dvh; max-height: none; border-radius: 0; }
  :root { --rail-width: 176px; --inspector-width: 280px; }
  body.drawer-open { --drawer-height: 180px; }
  #workflow { padding: 14px 8px; }
  .structure-file-actions { grid-template-columns: 1fr; }
  .rail-action { padding-left: 12px; font-size: 11px; }
  .rail-group h2 { font-size: 11.5px; }
  .workspace-context { display: none; }
  .brand-caption { display: none; }
  .primary-nav a { padding: 5px 9px; }
  .tool, .camera-reset { min-width: 30px; padding: 7px 5px; font-size: 9px; }
  .viewer-controls { padding: 7px 4px 12px; gap: 0; }
  #drawer { grid-template-columns: 190px minmax(0,1fr); }
  .drawer-col { padding: 12px; }
  .drawer-head .muted { display: none; }
}
@media (max-width: 760px) {
  #bar { height: 52px; padding: 0 10px; gap: 7px; }
  .primary-nav { display: none; }
  .brand { font-size: 13px; }
  #bar .btn { min-width: 31px; padding: 0 7px; font-size: 11px; gap: 0; }
  #bar .group { border: 0; padding: 0; }
  .workspace-grid { grid-template-columns: 150px minmax(0,1fr); grid-template-rows: minmax(230px,44dvh) minmax(0,1fr) 34px; grid-template-areas: 'viewer viewer' 'rail inspector' 'disclosure disclosure'; }
  #workflow { border-top: 1px solid var(--line); }
  #panel { border-top: 1px solid var(--line); }
  .viewbar { padding: 8px 12px 0; flex-wrap: nowrap; }
  .viewbar .seg { width: auto; flex: 1; }
  .viewbar .icon { font-size: 9px; }
  .viewbar .seg button { font-size: 9px; padding: 0 5px; }
  .viewbar .mini-switch { display: none; }
  .viewbar .icon.on { color: var(--good); }
  .viewer-controls { padding: 4px 12px 6px; gap: 6px; }
  .tool, .camera-reset { min-width: 32px; padding: 5px 6px; }
  .tool svg, .camera-reset svg { width: 14px; height: 14px; }
  .hint-card { padding: 12px; }
  .hint-glyph { width: 28px; height: 28px; }
  .hint-card h1 { font-size: 17px; margin-top: 8px; }
  .hint-card p { font-size: 10.5px; margin-bottom: 10px; }
  #axes { width: 48px; height: 48px; left: 8px; }
  .rail-action { gap: 7px; font-size: 10.5px; padding: 8px 6px; }
  .rail-group h2 { font-size: 11px; padding-left: 6px; }
  .load-action { padding: 0 7px; font-size: 10.5px; gap: 5px; }
  .inspector-scroll { padding: 10px; }
  .card-body { padding: 11px; }
  .insp-tabs { padding: 0 4px; }
  .insp-tabs button { font-size: 10px; padding: 12px 2px; }
  .info > div { grid-template-columns: 64px minmax(0,1fr); }
  .field-row { flex-wrap: wrap; }
  #drawer { display: none; }
  body.drawer-open #drawer { position: absolute; display: grid; left: 0; right: 0; bottom: 64px; height: 200px; z-index: 12; box-shadow: 0 -8px 30px #0002; }
  #statusbar { gap: 10px; padding: 0 10px; }
  #history-status { display: none !important; }
  .drawer-disclosure { font-size: 10px; padding: 0 12px; }
  .element-dialog { padding: 8px; }
  .modal { padding: 8px; }
}
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition: none !important; animation: none !important; } }


/* Text for assistive technology only: present in the accessibility tree,
   never painted. `display: none` would remove it from both. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
/* The viewer is focusable now, so it needs a focus ring of its own. */
#canvas:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.empty-title { margin: 0; font: 600 15px/1.3 inherit; color: var(--text); }

/* The measure readout is a stack of lines now, not one string. */
#measure-readout { display: flex; flex-direction: column; gap: 2px; }
#measure-readout span { display: block; }
#measure-readout .measure-hint { color: var(--faint); font-size: 10px; margin-top: 2px; }


/* Drawn repeats, in the viewport toolbar. Narrow number fields rather than
   a dropdown, because a,b,c are three independent answers and a menu of
   every combination would be twenty-seven entries. */
.repeat-seg {
  display: flex;
  align-items: center;
  gap: 4px;
  padding-inline: 6px;
}

.repeat-seg .seg-label {
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  opacity: 0.65;
  padding-inline-end: 2px;
}

.repeat-input {
  width: 2.6rem;
  padding: 3px 4px;
  text-align: center;
  font: inherit;
  font-size: 12px;
  color: inherit;
  background: var(--panel, transparent);
  border: 1px solid var(--line, #d5dae2);
  border-radius: 5px;
}

.repeat-input:focus-visible {
  outline: 2px solid var(--accent, #2f6fed);
  outline-offset: 1px;
}

/* Lit while more than one cell is drawn, so it is obvious at a glance that
   the view is showing repeats and not a structure that got bigger. */
.repeat-seg.on .repeat-input {
  border-color: var(--accent, #2f6fed);
}


/* The deconstruction, as pieces rather than a table. Each unit is a card
   because it has three things to offer - an identity, a selection and a
   download - and a table row has nowhere to put the last two. */
.unit-grid {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 8px;
}

/* A grid, not a row. The inspector is a narrow column, and three action
   chips beside the text left about ten characters for it - so "10 atoms
   2 attachment points" wrapped over three lines and the card was mostly
   whitespace. Identity on top, actions underneath, full width for both. */
.unit {
  display: grid;
  grid-template-columns: 10px 1fr;
  align-items: start;
  gap: 3px 8px;
  padding: 8px 10px;
  border: 1px solid var(--line, #d5dae2);
  border-radius: 8px;
  background: var(--panel, transparent);
}

.unit-swatch { margin-top: 5px; }
.unit-main { grid-column: 2; }
.unit-actions { grid-column: 2; margin-top: 4px; }

/* What the download will be, when it is not a molecule. */
.unit-badge {
  align-self: start;
  margin-top: 3px;
  padding: 1px 6px;
  border-radius: 999px;
  font-size: 10px;
  line-height: 1.6;
  background: color-mix(in srgb, var(--tint, #888) 16%, transparent);
  color: inherit;
  opacity: 0.9;
}

/* The colour this unit is drawn in, so the panel and the structure refer
   to each other without a legend. */
.unit-swatch {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--tint, #888);
}

.unit-main {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  min-width: 0;
}

.unit-label {
  font-weight: 600;
  font-size: 13px;
}

.unit-meta {
  font-size: 11px;
  opacity: 0.72;
}

.unit-actions {
  display: flex;
  align-items: center;
  gap: 4px;
}

/* The download links are chips like their neighbours, but they are
   anchors, so they need the parts a chip gets from being a button. */
.unit-actions a.chip {
  text-decoration: none;
  display: inline-flex;
  align-items: center;
}

/* Visible before the 3D modules arrive; becomes a compact process indicator. */
.activity { position: fixed; inset: 0; display: grid; place-items: center; z-index: 90; pointer-events: none; }
.activity[hidden] { display: none; }
.activity-card { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 10px; max-width: min(370px, calc(100vw - 48px)); padding: 20px 28px; border: 1px solid var(--line); border-radius: 18px; background: var(--glass); color: var(--text); box-shadow: 0 12px 40px #071b3026; }
.activity-art { position: relative; flex: 0 0 76px; width: 76px; height: 76px; }
#activity-canvas { width: 100%; height: 100%; display: none; }
.activity-3d #activity-canvas { display: block; }
.activity-3d .activity-fallback { display: none; }
.activity-copy strong { display: block; font-size: 13px; line-height: 1.5; }
.activity-copy p { margin: 3px 0; color: var(--dim); font-size: 11px; line-height: 1.5; }
.activity-copy small { color: var(--dim); font-size: 10px; font-variant-numeric: tabular-nums; }
.activity-copy button { margin-top: 12px; pointer-events: auto; }
.activity-startup { inset: 0; display: grid; place-items: center; background: var(--shell); z-index: 2000; pointer-events: auto; }
.activity-startup .activity-card { flex-direction: column; padding: 30px 38px; border: 0; background: transparent; box-shadow: none; text-align: center; }
.activity-startup .activity-art { width: 128px; height: 128px; flex-basis: 128px; }
.activity-startup .activity-copy strong { font-size: 19px; }
.activity-startup .activity-copy p { font-size: 13px; }
.activity-fallback { position: absolute; inset: 23%; border: 2px solid #52b5c580; border-radius: 24%; animation: lattice-turn 4s linear infinite; }
.activity-fallback i { position: absolute; width: 12px; height: 12px; border-radius: 50%; background: #269ac1; box-shadow: 0 0 16px #39d8c040; }
.activity-fallback i:nth-child(1) { left: -6px; top: -6px; }
.activity-fallback i:nth-child(2) { right: -6px; top: -6px; background: #39d8c0; }
.activity-fallback i:nth-child(3) { right: -6px; bottom: -6px; }
.activity-fallback i:nth-child(4) { left: -6px; bottom: -6px; background: #39d8c0; }
.activity-error .activity-fallback { animation: none; }
@keyframes lattice-turn { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .activity-fallback, .btn.busy::after, body.busy .status-dot { animation: none !important; }
}
