/* Celestial Shapes — technical, minimal, black/white UI.
   Sharp corners, flat surfaces, no shadows, thin black lines. */

/* Inter font: put the file in public/fonts/ (Inter.woff2 or InterVariable.woff2).
   If missing, it falls back to the system sans. */
@font-face {
  font-family: 'ABCAreal';
  src: url('ABCAreal-Regular.ttf');
  font-weight: normal;
}

:root {
  --ink: #050505;
  --bg: #e9e7dd; /* white workspace */
  --surface: #e9e7dd;
  --line: #050505;
  --text: #050505;
  --text-muted: #050505;
  --edge-gap: 20px; /* distance of the controls from the viewport edge */
  --thumb-bg: #dcdbd2; /* uniform background of the previews */
  --dim: 0.22; /* opacity of a "hidden" element */
  --fs: 11px; /* SINGLE text size (reference: "zoom") */
  --knob: 10px; /* diameter of the thumb circle = height of the end ticks */
  --kern: 0em;
  --ui-stroke-scale: 1; /* global multiplier of the UI stroke width (set by ui.js) */
  --ui-sw: calc(1px * var(--ui-stroke-scale)); /* base unit of the UI elements' stroke */
}

* {
  box-sizing: border-box;
}

::selection {
  background: var(--thumb-bg);
  color: var(--ink);
}

html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
  overflow: hidden; /* fixed view: never scroll */
  font-family: "ABCAreal", Arial, sans-serif;
}

body {
  background: var(--bg);
  color: var(--text);
  font-size: var(--fs);
}

#stage {
  position: relative;
  width: 100%;
  height: 100vh;
}

/* ---------- Visibility based on the view ---------- */

#stage.view-grid #back-btn,
#stage.view-grid #sign-id,
#stage.view-grid #iso-btn,
#stage.view-grid #iterate-btn,
#stage.view-grid #canvas-holder,
#stage.view-grid #layer-bar {
  display: none;
}

#stage.view-single #project-name,
#stage.view-single #zoom-group,
#stage.view-single #scroll-cluster,
#stage.view-single #seed-group,
#stage.view-single #grid {
  display: none;
}

/* ---------- Visible focus ---------- */

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

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

.text-btn {
  position: fixed;
  z-index: 20;
  padding: 2px 4px;
  background: none;
  border: none;
  font: inherit;
  color: var(--text);
  cursor: crosshair;
  letter-spacing: var(--kern);
  text-decoration: none;
  transition: color 0.12s ease;
}

.text-btn:hover {
  color: var(--text-muted);
}

/* Toggle (off = gray, on = black); no underline. */
.iso-btn,
.iterate-btn,
.scroll-btn {
  color: var(--text-muted);
}

.text-btn.is-active {
  color: var(--ink);
}

/* ---------- Corner labels ---------- */

.project-name {
  position: fixed;
  top: var(--edge-gap);
  left: var(--edge-gap);
  z-index: 30; /* above the info panel, stays clickable to close it */
  letter-spacing: var(--kern);
  cursor: crosshair;
  user-select: none;
}

.project-name:focus-visible {
  outline: 1px solid var(--ink);
  outline-offset: 2px;
}

/* ---------- Info panel (appears on click of the title) ---------- */

.info-panel {
  position: fixed;
  top: 60px; /* below the title AND below the "scroll" cluster (no overlap) */
  left: var(--edge-gap);
  width: calc(50vw - var(--edge-gap)); /* half the screen, stops at the center */
  z-index: 25;
  color: var(--text);
  text-align: left; /* left-aligned / justified */
  line-height: 1.5;
}

.info-panel[hidden] {
  display: none;
}

.info-panel p {
  margin: 0 0 1em;
}

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

/* The title (and thus the panel) exist only in grid view. */
#stage.view-single .info-panel {
  display: none;
}

/* ---------- "Info open" state: blur the signs + disabled controls ----------
   Only "about" and the title (which closes and restores everything) stay active. */

#stage.info-open #grid {
  filter: blur(6px);
  pointer-events: none; /* disable hover/click on the signs */
}

#stage.info-open .seed-group,
#stage.info-open .zoom-group,
#stage.info-open .scroll-cluster,
#stage.info-open .export-group {
  pointer-events: none;
}

.back-btn {
  top: var(--edge-gap);
  left: var(--edge-gap);
}

.about-btn {
  top: var(--edge-gap);
  right: var(--edge-gap);
}

/* sign id (single view): 3-digit position, top-center, gray. */
.sign-id {
  position: fixed;
  top: var(--edge-gap);
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
  color: var(--text-muted);
  letter-spacing: var(--kern);
}

/* iso: bottom-left. */
.iso-btn {
  bottom: var(--edge-gap);
  left: var(--edge-gap);
}

/* iterate: above the thumbnails, centered at the bottom. */
.iterate-btn {
  bottom: 148px;
  left: 50%;
  transform: translateX(-50%);
}

/* ---------- Seed (regenerate + field with ticks), bottom-left ---------- */

.seed-group {
  position: fixed;
  left: var(--edge-gap);
  bottom: var(--edge-gap);
  z-index: 20;
  display: flex;
  flex-direction: row; /* "seed" and the number on the same line */
  align-items: center;
  gap: 8px;
}

.seed-group .text-btn {
  position: static;
}

.seed-label {
  display: inline-flex;
  align-items: center;
}

/* Circle next to "seed": outline, fills on hover of the text or the circle. */
.knob {
  display: inline-block;
  width: var(--knob);
  height: var(--knob);
  margin-left: 5px;
  border: 1px solid var(--ink);
  border-radius: 50%;
  background: transparent;
  vertical-align: middle;
}

.seed-field {
  position: relative;
  padding: 0 5px; /* 5px before and after the text */
  border-bottom: var(--ui-sw) solid var(--text-muted);
}

.seed-input {
  width: 4ch; /* resized via JS to its content */
  padding: 0;
  font: inherit;
  color: var(--text-muted); /* gray number, like "zoom" */
  text-align: center;
  background: none;
  border: none;
}

.seed-input:focus {
  outline: none;
}

/* Ticks at the ends of the line, like in the slider. */
.seed-tick {
  position: absolute;
  bottom: 0;
  width: var(--ui-sw);
  height: var(--knob);
  background: var(--text-muted);
}
.seed-tick-l {
  left: 0;
}
.seed-tick-r {
  right: 0;
}

/* ---------- Export (button + view / full set menu, opens upward) ---------- */

.export-group {
  position: fixed;
  /* aligned right like "about" (same --edge-gap from the edge). */
  right: var(--edge-gap);
  bottom: var(--edge-gap);
  z-index: 30;
}

.export-group .text-btn {
  position: static;
}

.export-menu {
  position: absolute;
  bottom: 100%; /* opens upward */
  right: 0; /* right-justified, aligned with "export" (and thus with "about") */
  left: auto;
  margin-bottom: 6px;
  width: max-content; /* don't shrink to the button → "full set" stays on one line */
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
}

.export-menu[hidden] {
  display: none;
}

.export-item {
  position: static;
  color: var(--text-muted);
  white-space: nowrap;
  text-align: right;
}

.export-item:hover {
  color: var(--ink);
}

/* ---------- Slider (zoom) ---------- */

.slider-group {
  position: fixed;
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.zoom-group {
  bottom: var(--edge-gap);
  left: 50%;
  transform: translateX(-50%) translateY(-1px);
  align-items: center;
}

.slider-label {
  color: var(--ink);
  letter-spacing:var(--kern);
}

/* Custom slider: line + ticks + circle thumb.
   Width proportional to the window (~12vw), with min/max breakpoints. */
.slider {
  position: relative;
  width: clamp(88px, 12vw, 180px);
  height: 12px;
  cursor: crosshair;
  touch-action: none;
}

.slider:focus {
  outline: none;
}

.slider-track {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background-color: var(--ink);
}

.slider-ticks {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--knob);
}

/* Ticks: each tick is a "box" as wide as the circle (--knob) with the 1px line
   drawn at its center. This way tick and circle have the SAME width and the SAME
   sub-pixel rounding → they are always perfectly aligned in every browser (Firefox
   included). Intermediate ones: half height. */
.slider-tick {
  position: absolute;
  bottom: 0;
  width: var(--knob);
  height: 5px;
  transform: translateX(-50%);
  background: linear-gradient(var(--ink), var(--ink)) no-repeat center bottom / var(--ui-sw) 100%;
}

/* End ticks (min/max): as tall as the circle. */
/* .slider-tick.is-end {
  height: var(--knob);
} */

/* Thumb: circle (diameter = height of the end ticks), outline by default;
   hover → 50% black center, click/drag → 100% black. */
.slider-thumb {
  position: absolute;
  bottom: 0;
  left: 0;
  width: var(--knob);
  height: var(--knob);
  border: var(--ui-sw) solid var(--ink);
  border-radius: 50%;
  background: var(--ink);
  transform: translateX(-49%) translateY(-2%);
  pointer-events: none;
}

.slider:active .slider-thumb,
.slider:focus-visible .slider-thumb {
  background: var(--ink);
}

/* ---------- Alternative slider style: ticks only, no base line ----------
   Enabled by the .sliders-ticks-only class on #stage (see SLIDER_STYLE in ui.js).
   No track; uniform comb: all ticks (end and intermediate) are the same height as
   the circle and centered on it, so the thumb slides over them. */
.sliders-ticks-only .slider-track {
  display: none;
}

.sliders-ticks-only .slider-tick {
  height: var(--knob); /* uniform comb: intermediate ones as tall as the end ones */
  bottom: auto;
  top: 50%;
  transform: translate(-50%, -50%);
  background-position: center center;
}

/* ---------- Alternative shapes style: "misc" (triangle / diamond) ----------
   Enabled by .shapes-misc on #stage (see SHAPES_STYLE in ui.js). The shapes are
   inline SVG (black stroke, filled on hover/active like the circles). The layer
   boxes (.layer-check-box) stay circles: not touched here. */

/* Slider thumb → equilateral triangle (pointing down). Rendered with inline SVG
   (.thumb-tri, not background-image): vector and laid out in the same space as the
   ticks → identical alignment across all browsers. The box is as tall as the ticks
   (--knob) and the triangle fills it: same height, coinciding center. The
   equilateral base is wider than the height → box width --knob*1.1547. */
.slider-thumb .thumb-tri {
  display: none; /* "circles" style: the thumb stays the circle (border) */
}

.shapes-misc .slider-thumb {
  width: calc(var(--knob) * 1.1547);
  height: var(--knob);
  border: none;
  border-radius: 0;
  background: none;
}

.shapes-misc .slider-thumb .thumb-tri {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible; /* the stroke isn't clipped at the box edges */
  fill: var(--ink);
  stroke: var(--ink);
  stroke-linejoin: round;
  /* stroke-width and vector-effect are attributes on the <path> (see slider.js). */
}

/* Width of the thumb triangle scaled by the global UI multiplier. The CSS rule on
   the <path> wins over the stroke-width attribute (0.85) from slider.js; the
   vector-effect: non-scaling-stroke stays an attribute → the stroke isn't distorted. */
.shapes-misc .slider-thumb .thumb-tri path {
  stroke-width: calc(0.85px * var(--ui-stroke-scale));
}

/* Hover/active: changes ONLY the triangle's fill. The square that used to appear
   was the base theme's circle background (.slider:hover .slider-thumb): here I
   reset it, so the thumb box stays transparent. */
.shapes-misc .slider:hover .slider-thumb,
.shapes-misc .slider:active .slider-thumb,
.shapes-misc .slider:focus-visible .slider-thumb {
  background: none;
}

.shapes-misc .slider:hover .slider-thumb .thumb-tri,
.shapes-misc .slider:active .slider-thumb .thumb-tri,
.shapes-misc .slider:focus-visible .slider-thumb .thumb-tri {
  fill: var(--ink);
}

/* Seed → diamond (square rotated 45°). */
.shapes-misc .knob {
  border: none;
  border-radius: 0;
  background: center / contain no-repeat
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M6 1 L11 6 L6 11 L1 6 Z' fill='none' stroke='%23000' stroke-width='1'/%3E%3C/svg%3E");
}

.shapes-misc .seed-label:hover .knob {
  background: center / contain no-repeat
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M6 1 L11 6 L6 11 L1 6 Z' fill='%23000' stroke='%23000' stroke-width='1'/%3E%3C/svg%3E");
}

/* ---------- Scroll indicator + toggle (bottom-center) ---------- */

.scroll-cluster {
  position: fixed;
  top: var(--edge-gap);
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}

.scroll-cluster .text-btn {
  position: static;
}

/* Reuses the sliders' look (.slider .slider-track .slider-ticks .slider-thumb).
   Flipped vertically relative to the zoom slider: base line AT THE TOP, ticks
   pointing down and the triangle thumb pointing UP. The scaleY(-1) inverts only the
   vertical axis; scrubbing stays horizontal (uses clientX) → no impact. */
.scroll-indicator {
  cursor: crosshair;
  transform: scaleY(-1);
}

/* ---------- Grid view (full-bleed on the sides) ---------- */

.grid {
  position: absolute;
  top: 60px;
  left: 0; /* full-bleed: no side bands */
  right: 0;
  bottom: 60px;
  display: grid;
  grid-auto-flow: column;
  align-content: center; /* vertically center the band of rows */
  justify-content: safe center;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  /* Default scrollbar hidden: only the indicator at the bottom is used. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.grid::-webkit-scrollbar {
  display: none;
}

/* During automatic scroll I control the position on every frame: native snap would
   conflict, so I disable it (the steps still land on the sign anyway). */
.grid.is-autoscroll {
  scroll-snap-type: none;
}

.grid-cell {
  position: relative;
  scroll-snap-align: start;
  cursor: crosshair;
  border: 1px solid transparent;
  background-color: transparent;
}

/* Hover: inverted square around the symbol (bg/ink swap). */
.grid-cell:hover,
.grid-cell:focus-visible {
  background-color: var(--ink);
  outline: none;
}

.grid-cell:hover .symbol-svg,
.grid-cell:focus-visible .symbol-svg {
  filter: invert(1);
}

.grid-cell .symbol-svg {
  display: block;
  width: 100%;
  height: 100%;
  /* The whole cell must register the hover: without this, over the transparent
     areas of the SVG the hover "jumps" (the SVG captures the pointer). */
  pointer-events: none;
}

/* sign id at the bottom-left, gray, on hover. FIXED text size (doesn't scale with
   the zoom) because it's a DOM node with a font in px. */
.cell-id {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  justify-content: flex-start;
  padding: 10px;
  color: var(--text-muted);
  font-size: var(--fs);
  letter-spacing: var(--kern);
  pointer-events: none;
  opacity: 0;
}

.grid-cell:hover .cell-id,
.grid-cell:focus-visible .cell-id {
  opacity: 1;
}

/* ---------- Anti "conflation" of overlapping strokes ----------
   Multiple black layers overlapping, composited normally (source-over), sum their
   anti-aliased edges: at the overlap points a gray seam forms and the lines look
   thicker. With mix-blend-mode:darken each layer takes the MINIMUM with the
   background (black on black = black, black on white = black): the overlaps merge
   into full black, with no thickening or seams. isolation:isolate on the container
   confines the blend TO THE SIGN'S LAYERS ONLY (not the page behind).
   In single view I apply it to the tiles' .content group, so the translucent gray
   backgrounds of the iso view (siblings, not .content) stay in normal compositing. */
.grid-cell .symbol-svg,
#canvas-holder > svg {
  isolation: isolate;
}

.grid-cell .symbol-svg > .symbol-layer,
#canvas-holder > svg > .tile > .content {
  mix-blend-mode: darken;
}

/* ---------- Single view ---------- */

.canvas-holder {
  position: absolute;
  top: 56px;
  left: 50%;
  transform: translateX(-50%);
  line-height: 0;
  cursor: crosshair; /* click on the symbol = randomize */
}

.canvas-holder .symbol-svg {
  display: block;
  width: 100%;
  height: 100%;
}

.layer-bar {
  position: fixed;
  bottom: var(--edge-gap);
  left: 50%;
  transform: translateX(-50%);
  max-width: calc(100vw - var(--edge-gap) * 2);
  display: flex;
  align-items: stretch;
  gap: 16px;
  z-index: 20;
}

.layer-card {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 84px;
}

.layer-preview {
  width: 84px;
  height: 84px;
  padding: 0;
  border: none;
  background: var(--thumb-bg);
  cursor: crosshair;
  overflow: hidden;
}

.layer-preview .symbol-svg {
  display: block;
  width: 100%;
  height: 100%;
}

.layer-card.is-hidden .layer-preview {
  opacity: var(--dim);
}

/* "Checkbox" as wide as the preview. */
.layer-check {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 84px;
  padding: 3px 4px;
  background: none;
  border: 1px solid var(--line);
  color: var(--text);
  cursor: crosshair;
  font: inherit;
}

/* Small circle like the sliders': filled = visible, outline = hidden. */
.layer-check-box {
  flex: 0 0 auto;
  width: var(--knob);
  height: var(--knob);
  border: 1px solid var(--ink);
  border-radius: 0%;
  background: var(--ink);
}

.layer-card.is-hidden .layer-check-box,
.layer-check.is-off .layer-check-box {
  background: transparent;
}

.layer-check.is-off {
  color: var(--text-muted);
}

.layer-check-label {
  letter-spacing: var(--kern);
}


@media screen and (min-width: 1000px) {
  .info-panel {
     width: calc(25vw - var(--edge-gap));
  }
}

/* ---------- Narrow viewports (phones) ----------
   At the default 84px card width the 4-wide layer-bar (336px + 3×16px gaps =
   384px) overflows any viewport under ~424px, spilling over the iso/export
   buttons in the bottom corners. Below that, shrink the cards/gap to fit and
   raise the whole bar so the corner buttons sit in clear space beneath it. */
@media screen and (max-width: 480px) {
  .layer-bar {
    bottom: 56px;
    gap: 8px;
  }

  .layer-card,
  .layer-preview,
  .layer-check {
    width: 72px;
  }

  .iterate-btn {
    bottom: 172px;
  }

  /* Info panel (grid view, title click): 50vw is too narrow on phones — widen
     it to fill the available width instead of wrapping into a tall, cramped
     column with dead space on the right. */
  .info-panel {
    width: calc(100vw - var(--edge-gap) * 2);
  }
}
