/* This file is part of Chaos Accelerator, licensed under the Common Public
   Attribution License, Version 1.0 (CPAL-1.0) - see LICENSE in the project
   root, or https://chaosaccelerator.com/license for a hosted copy. */

:root {
  --bg: #14161c;
  --bg-panel: #1b1e27;
  --bg-input: #0f1116;
  --border: #2c3040;
  --text: #e6e8ef;
  --text-dim: #9399ad;
  --accent: #5b8cff;
  --accent-hover: #75a0ff;
  --danger: #ff5f56;
  --danger-hover: #ff7a72;
  --panel-width: 320px;
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

#app {
  height: 100vh;
  display: flex;
  flex-direction: column;
}



.status-ok, .status-playing {
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 999px;
  white-space: nowrap;
}
.status-ok { background: #21273a; color: #9db0e8; }
.status-playing { background: #163a25; color: #7ee0a0; }
.status-error { background: #3a1418; color: #ff9a9a; }

/* ---- Panel head: all the chrome there is ----
   The old #topbar (page title, status chip, settings/back buttons) is gone
   from both pages so the canvas can run edge to edge. What survived lives
   in this row at the top of #panel. (#editor-view only - the fractal page
   keeps its own chrome in a menu column instead; see #grid-menu-column in
   fractal-grid.css.) */
.panel-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}
/* Errors only. "Editing", "Playing", "Ready" were the whole reason the old
   status chip needed permanent space, and none of them told anyone
   anything they couldn't see; a compile failure or an unreadable scene
   very much does, so the element stays and only shows for those. */
#status:not(.status-error) {
  display: none;
}
#status.status-error {
  flex: 1;
  min-width: 0;
}

#main {
  flex: 1;
  display: flex;
  min-height: 0;
}

#panel {
  width: var(--panel-width);
  flex-shrink: 0;
  background: var(--bg-panel);
  border-right: 1px solid var(--border);
  overflow-y: auto;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.panel-section h2 {
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-dim);
  margin: 0 0 10px 0;
}

.tool-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

button {
  font-family: inherit;
  font-size: 13px;
  padding: 8px 14px;
  border-radius: 6px;
  border: 1px solid transparent;
  background: var(--accent);
  color: white;
  cursor: pointer;
}
button:hover { background: var(--accent-hover); }
button:disabled { opacity: 0.5; cursor: not-allowed; }
button.secondary {
  background: transparent;
  border-color: var(--border);
  color: var(--text);
}
button.secondary:hover { background: var(--bg-input); }
button.danger {
  background: transparent;
  border-color: var(--danger);
  color: var(--danger);
}
button.danger:hover { background: var(--danger); color: white; }

.tool-btn {
  background: var(--bg-input);
  border: 1px solid var(--border);
  color: var(--text);
}
.tool-btn:hover { background: #171a22; }
.tool-btn.active {
  background: var(--accent);
  border-color: var(--accent);
  color: white;
}

/* Native <details>/<summary> - collapsed by default (no `open` attribute
   needed), so "advanced" needs no JS state of its own. Collapsed, it's just
   an arrow + label; open, it grows a dashed fieldset-style box around its
   contents with "advanced" overlapping the top border as the legend -
   summary is pulled out of flow and repositioned there, background-matched
   to the panel so it "erases" the dashed line behind the text the same way
   a real <fieldset>/<legend> pair does natively. */
.collapsible {
  margin-top: 12px;
}
.collapsible > summary {
  cursor: pointer;
  font-size: 12px;
  color: var(--text-dim);
  list-style: none;
  display: flex;
  align-items: center;
  gap: 6px;
  user-select: none;
}
.collapsible > summary::-webkit-details-marker { display: none; }
.collapsible > summary::before {
  content: "\25b8";
  display: inline-block;
  font-size: 16px;
  line-height: 1;
  transition: transform 0.15s ease;
}
.collapsible[open] > summary::before {
  transform: rotate(90deg);
}
.collapsible > summary:hover { color: var(--text); }
.collapsible[open] {
  position: relative;
  border: 1px dashed var(--border);
  border-radius: 6px;
  padding: 10px;
}
.collapsible[open] > summary {
  position: absolute;
  top: -9px;
  left: 8px;
  padding: 0 6px;
  background: var(--bg-panel);
}
.collapsible-body {
  margin-top: 4px;
}
.collapsible-body .field:last-child { margin-bottom: 0; }

.field {
  display: block;
  font-size: 13px;
  margin-bottom: 10px;
}
.field span {
  display: flex;
  justify-content: space-between;
  margin-bottom: 6px;
  color: var(--text-dim);
}
.field span b { color: var(--text); font-weight: 600; }
/* The Simulation Duration readout, typed into: sits where the bold number
   was and reads like it, until it is focused. 16px on a phone, or iOS zooms
   the page the moment it is tapped. */
.steps-number {
  width: 5.2em;
  padding: 1px 4px;
  font: inherit;
  font-weight: 600;
  text-align: right;
  color: var(--text);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 4px;
}
.steps-number:focus { outline: none; border-color: var(--accent, #5b8cff); }
html.input-coarse .steps-number { font-size: 16px; }

input[type="range"] {
  width: 100%;
  accent-color: var(--accent);
}
input[type="range"]:disabled { opacity: 0.5; }

select {
  font-family: inherit;
  font-size: 13px;
  padding: 6px 8px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg-input);
  color: var(--text);
}
select:disabled { opacity: 0.5; }

/* A select that is the whole field (Edge Handling), rather than one of the
   pair in .dual-select below - it takes the full width the way a slider in
   the same position would, instead of shrinking to its longest option. */
.field select {
  width: 100%;
}

.dual-select {
  display: flex;
  gap: 8px;
}
.dual-select select {
  flex: 1;
  min-width: 0;
}
/* An author `display` beats the UA's own [hidden] rule, so a .dual-select
   toggled with el.hidden would stay on screen without this - the same trap
   .modal-backdrop[hidden] below already exists for. Output's optional
   second-object row is toggled that way (see refreshOutputPairUI). */
.dual-select[hidden] {
  display: none;
}
/* The Spring section is toggled the same way (see refreshSpringPanel), and
   gets the same guarantee rather than relying on .panel-section never being
   given a `display` of its own. */
#spring-section[hidden] {
  display: none;
}

.editor-actions {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}

.field-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 4px;
}
/* Simulation's Advanced box sits directly above the playback controls - the
   ordinary 4px above reads as cramped right after a bordered box, unlike
   after a plain .field, so this one spot gets more room. Scoped to the
   sibling pairing itself rather than bumping .collapsible's own margin
   everywhere, since Tools' and Input/Output's Advanced boxes are each
   followed by a new .panel-section (which already has its own spacing),
   not a .field-row/.playback-controls. */
.collapsible + .field-row,
.collapsible + .playback-controls {
  margin-top: 18px;
}
.field-row button { flex: 1; }

.readout {
  font-size: 12px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  margin: 8px 0 0 0;
}
.readout:empty { display: none; }
.readout.error { color: var(--danger); }

#canvas-area {
  flex: 1;
  position: relative;
  min-width: 0;
  background: #0c0d11;
  /* Centers #physics-canvas, which resizeCanvas() sizes explicitly in JS
     (not 100%/100% below) - exactly canvasArea's own size while editing,
     but a locked frame's own aspect ratio, scaled to fit, while playing. */
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Floats over the scene rather than living in the side panel - the whole
   point is that it's the one button you can't miss. Positioned against
   #canvas-area (not the viewport), so it stays centered over the scene
   specifically in both the desktop (side panel) and portrait (bottom panel)
   layouts. pointer-events:none on the wrapper + auto on its children keeps
   the empty space around the button/error message from blocking clicks on
   the canvas underneath. */
#fractalize-wrap {
  position: absolute;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  z-index: 10;
  pointer-events: none;
}
#fractalize-wrap > * {
  pointer-events: auto;
}
#fractalize-wrap .readout {
  background: rgba(12, 13, 17, 0.92);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 12px;
  max-width: 320px;
  text-align: center;
  margin: 0;
  color: var(--text);
}
/* A genuine error (e.g. a splitter scene) - shown only right after an
   actual send attempt fails. */
#fractalize-wrap .readout.error {
  border-color: var(--danger);
  color: var(--danger);
}

/* The Play transport, floated top-center over the scene instead of docked
   in #panel (see chaos.html's comment on #playback-toolbar) - same
   #canvas-area-relative centering #fractalize-wrap uses above, mirrored to
   the top edge. Needs its own translucent card (--bg-panel-float, the same
   fill fractal-grid.css's floating panel/settings-panel use) since
   .playback-controls was designed to sit directly on #panel's own solid
   background, and now floats over the canvas instead. */
#playback-toolbar {
  position: absolute;
  left: 50%;
  top: var(--panel-inset);
  transform: translateX(-50%);
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 8px 14px;
  background: var(--bg-panel-float);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
}
/* Both were designed with a margin-top for the space above them inside
   #panel's flowing column - meaningless (and lopsided) as the first things
   in this floating card. */
#playback-toolbar .playback-controls {
  margin-top: 0;
}
#playback-toolbar .readout {
  margin-top: 0;
}
/* Guidance toward what to do next - shown while hovering the button, or
   briefly after clicking it, whenever the scene isn't ready yet (see
   gridReadiness/showNotReadyHint in physics-ui.js). A distinct, calmer
   color from .error: this isn't something broken, just not finished yet. */
#fractalize-wrap .readout.hint {
  border-color: var(--accent);
  color: var(--accent-hover);
}

/* Loud on purpose: warm, saturated, animated - the opposite of every other
   button in this app, which is exactly the point (this is the one action
   that actually leaves the editor). Always this color, whether or not the
   scene is actually ready - see .not-ready below for the one exception. */
.fractalize-btn {
  font-family: inherit;
  font-size: 17px;
  font-weight: 700;
  padding: 14px 34px;
  border-radius: 999px;
  border: none;
  color: #200b00;
  background: linear-gradient(135deg, #ffd23f, #ff6b35 55%, #ff2e63);
  box-shadow: 0 4px 18px rgba(255, 107, 53, 0.55);
  cursor: pointer;
  white-space: nowrap;
  animation: fractalizePulse 2.2s ease-in-out infinite;
  transition: transform 0.12s ease, box-shadow 0.12s ease, filter 0.12s ease;
}
.fractalize-btn:hover {
  transform: translateY(-2px) scale(1.05);
  filter: brightness(1.08);
  box-shadow: 0 6px 26px rgba(255, 46, 99, 0.8);
}
.fractalize-btn:active {
  transform: translateY(0) scale(0.97);
}
/* The ONE time the button isn't bright: while hovering, or briefly after
   clicking, a scene that isn't ready to send yet - a class toggled from JS
   rather than the native :disabled state, since a truly disabled button
   stops firing the hover events this whole interaction depends on. */
.fractalize-btn.not-ready,
.fractalize-btn.not-ready:hover {
  animation: none;
  cursor: not-allowed;
  color: var(--text-dim);
  background: var(--border);
  box-shadow: none;
  transform: none;
  filter: none;
}
@keyframes fractalizePulse {
  0%, 100% { box-shadow: 0 4px 18px rgba(255, 107, 53, 0.55); }
  50% { box-shadow: 0 4px 30px rgba(255, 46, 99, 0.9); }
}

/* Points at whichever button would actually move the scene toward being
   fractal-ize-able (see gridReadiness's highlight list) - an outward pulse
   in the same warm hue as the Fractal-ize button itself, so the connection
   between "why can't I click that" and "click THIS instead" reads clearly. */
@keyframes highlightHintPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 107, 53, 0.65); }
  50% { box-shadow: 0 0 0 6px rgba(255, 107, 53, 0); }
}
.tool-btn.highlight-hint {
  border-color: #ff6b35;
  animation: highlightHintPulse 1.1s ease-out infinite;
}

#physics-canvas {
  display: block;
  cursor: default;
}
#physics-canvas.tool-select { cursor: default; }
#physics-canvas.tool-circle,
#physics-canvas.tool-line,
#physics-canvas.tool-velocity { cursor: crosshair; }
#physics-canvas.tool-anchor,
#physics-canvas.tool-hinge,
#physics-canvas.tool-input,
#physics-canvas.tool-input-velocity,
#physics-canvas.tool-output { cursor: pointer; }
#physics-canvas.dragging { cursor: grabbing; }

/* Gear button in the panel head, and its × twin inside the settings panel -
   square, icon-only, otherwise matching button.secondary's look. */
.icon-button {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  padding: 0;
  font-size: 16px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text);
}
.icon-button:hover {
  background: var(--bg-input);
}

/* .settings-panel and its header/body are styled in app-shell.css now - the
   settings button and the panel it opens both moved to a shared, cross-view
   floating design (see .floating-settings-btn there) instead of each page
   keeping its own copy of a slide-in drawer. */

/* Apple-style toggle switch - under the hood this is still the exact same
   <input type="checkbox" id="sticky-edges-checkbox">, just visually hidden
   (see .toggle-switch input below) with .toggle-slider drawn on top of it
   and styled off its :checked/:disabled state, so all of physics-ui.js's
   existing .checked/.disabled/change-event logic needs no changes at all. */
.toggle-field {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  font-size: 13px;
  margin-bottom: 10px;
  cursor: pointer;
}
.toggle-switch {
  position: relative;
  display: inline-block;
  width: 44px;
  height: 24px;
  flex-shrink: 0;
}
.toggle-switch input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}
.toggle-slider {
  position: absolute;
  inset: 0;
  background: var(--border);
  border-radius: 999px;
  transition: background 0.15s ease;
  pointer-events: none;
}
.toggle-slider::before {
  content: "";
  position: absolute;
  width: 18px;
  height: 18px;
  left: 3px;
  top: 3px;
  background: white;
  border-radius: 50%;
  transition: transform 0.15s ease;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}
.toggle-switch input:checked + .toggle-slider {
  background: var(--accent);
}
.toggle-switch input:checked + .toggle-slider::before {
  transform: translateX(20px);
}
.toggle-switch input:disabled + .toggle-slider {
  opacity: 0.5;
}
.toggle-switch input:disabled {
  cursor: not-allowed;
}

/* Reusable modal (see openModal in physics-ui.js) - a centered dialog over
   a dimmed backdrop, used for both Export and Import. */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 950;
}
.modal-backdrop[hidden] {
  display: none;
}
.modal {
  width: 520px;
  max-width: calc(100vw - 48px);
  max-height: calc(100vh - 48px);
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
  display: flex;
  flex-direction: column;
}
.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.modal-header h2 {
  font-size: 14px;
  margin: 0;
}
.modal-body {
  padding: 16px;
  overflow-y: auto;
}
.modal-body p {
  margin: 0 0 10px 0;
  font-size: 13px;
  color: var(--text);
}
.modal-textarea {
  width: 100%;
  height: 260px;
  resize: vertical;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11.5px;
  line-height: 1.5;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg-input);
  color: var(--text);
  white-space: pre-wrap;
  word-break: break-all;
}


/* ---- The Output's color key, on the scrubber ----
   With Scene Lifespan as the Output, the playback scrubber's track IS the
   key: the color a run gets is the one under the point where it stops (see
   "The Output's color key" in physics-ui.js, which sets the class and the
   --output-key gradient). A native range input's track can't be painted
   from outside, so with the class on the input gives up its native look
   for one that can: the gradient as the input's own background, a
   transparent track over it, and a plain white thumb that reads against
   every hue. Without the class nothing here applies. */
#playback-progress-slider.output-key {
  -webkit-appearance: none;
  appearance: none;
  height: 10px;
  border-radius: 5px;
  background: var(--output-key);
  outline-offset: 4px;
}
#playback-progress-slider.output-key::-webkit-slider-runnable-track {
  -webkit-appearance: none;
  height: 10px;
  background: transparent;
}
#playback-progress-slider.output-key::-moz-range-track {
  height: 10px;
  background: transparent;
}
#playback-progress-slider.output-key::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  margin-top: -3px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid #14161c;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
#playback-progress-slider.output-key::-moz-range-thumb {
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid #14161c;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
/* Before Play the scrubber is disabled, which would dim it to half - but
   the key is most use exactly then, before there is a run to watch. So the
   track keeps its full color and only the thumb says "not yet". */
#playback-progress-slider.output-key:disabled {
  opacity: 1;
}
#playback-progress-slider.output-key:disabled::-webkit-slider-thumb {
  background: #9399ad;
}
#playback-progress-slider.output-key:disabled::-moz-range-thumb {
  background: #9399ad;
}

/* ...and on the Set Output button, which once an Output is set IS a swatch
   of its whole range, low to high from left to right. background-IMAGE, so
   the button's own idle/hover/active background COLORS underneath are left
   to their rules; what says "armed" over a swatch is a white ring instead
   of the blue fill it would otherwise hide. */
button.tool-btn.output-key {
  background-image: var(--output-key);
  border-color: rgba(255, 255, 255, 0.35);
  color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.75);
}
button.tool-btn.output-key.active {
  border-color: #fff;
  box-shadow: 0 0 0 2px #fff;
}
