/* MonoLisa 3.000, subject to a EULA: https://www.monolisa.dev/license
   © 2026 FaceType Foundry. All Rights Reserved.

   Folded in from the two generated monolisa.css files, whose src paths are
   site-root absolute and do not resolve where the files actually live. Variable
   faces spanning 1-900, split by unicode range rather than by weight, so one
   file per range covers every weight the app asks for. Italic faces are left
   out: nothing here is italic, so they would never be fetched.

   fonts/README.md says which ranges are needed and why. */

@font-face {
  font-family: MonoLisaText;
  font-style: normal;
  font-weight: 1 900;
  font-display: swap;
  src: url(../fonts/monolisa-text/woff2/0-MonoLisaText-normal.woff2) format("woff2");
  unicode-range: U+0020-007F;
}

@font-face {
  font-family: MonoLisaText;
  font-style: normal;
  font-weight: 1 900;
  font-display: swap;
  src: url(../fonts/monolisa-text/woff2/2-MonoLisaText-normal.woff2) format("woff2");
  unicode-range: U+0080-00FF;
}

@font-face {
  font-family: MonoLisaText;
  font-style: normal;
  font-weight: 1 900;
  font-display: swap;
  src: url(../fonts/monolisa-text/woff2/4-MonoLisaText-normal.woff2) format("woff2");
  unicode-range: U+0370-03FF;
}

@font-face {
  font-family: MonoLisaText;
  font-style: normal;
  font-weight: 1 900;
  font-display: swap;
  src: url(../fonts/monolisa-text/woff2/6-MonoLisaText-normal.woff2) format("woff2");
  unicode-range: U+2000-206F;
}

@font-face {
  font-family: MonoLisaText;
  font-style: normal;
  font-weight: 1 900;
  font-display: swap;
  src: url(../fonts/monolisa-text/woff2/8-MonoLisaText-normal.woff2) format("woff2");
  unicode-range: U+2190-21FF;
}

@font-face {
  font-family: MonoLisaText;
  font-style: normal;
  font-weight: 1 900;
  font-display: swap;
  src: url(../fonts/monolisa-text/woff2/10-MonoLisaText-normal.woff2) format("woff2");
  unicode-range: U+2200-22FF;
}

@font-face {
  font-family: MonoLisaCode;
  font-style: normal;
  font-weight: 1 900;
  font-display: swap;
  src: url(../fonts/monolisa-code/woff2/0-MonoLisaCode-normal.woff2) format("woff2");
  unicode-range: U+0020-007F;
}

@font-face {
  font-family: MonoLisaCode;
  font-style: normal;
  font-weight: 1 900;
  font-display: swap;
  src: url(../fonts/monolisa-code/woff2/2-MonoLisaCode-normal.woff2) format("woff2");
  unicode-range: U+0080-00FF;
}

:root {
  /* Flexoki dark — https://stephango.com/flexoki */
  --bg: #1C1B1A;        /* base-950 */
  --panel: #282726;     /* base-900 */
  --panel-2: #343331;   /* base-850 */
  --border: #403E3C;    /* base-800 */
  --grid: #343331;       /* base-850 */
  --axis: #6F6E69;      /* base-600 */
  --text: #FFFCF0;      /* paper    */
  --text-dim: #878580;  /* base-500 */
  --accent: #4385BE;    /* blue-400 */
  /* Flexoki's eight hues at the 400 step, ordered so the ones reached first
     are the furthest apart. */
  --series-a: #4385BE;  /* blue-400 */
  --series-b: #DA702C;  /* orange-400 */
  --series-c: #3AA99F;  /* cyan-400 */
  --series-d: #CE5D97;  /* magenta-400 */
  --series-e: #879A39;  /* green-400 */
  --series-f: #8B7EC8;  /* purple-400 */
  --series-g: #D0A215;  /* yellow-400 */
  --series-h: #D14D41;  /* red-400 */
  --sans: MonoLisaText, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono: MonoLisaCode, ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  -webkit-text-size-adjust: 100%;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--text);
  font: 13px/1.45 var(--sans);
  overflow: hidden;
}

/* --- top bar: title, tabs, global options on one line --- */

.topbar {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  row-gap: 0.25rem;
  padding: 0.25rem 0.85rem;
  min-height: 2.4rem;
  flex: 0 0 auto;
  border-bottom: 1px solid var(--border);
  background: var(--panel);
}

.topbar h1 {
  margin: 0;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
}

.tabs { display: flex; gap: 0.2rem; }

.tabs button {
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-dim);
  font: 500 0.78rem inherit;
  padding: 0.2rem 0.65rem;
  border-radius: 4px;
  cursor: pointer;
}

.tabs button:hover { color: var(--text); background: var(--panel-2); }

.tabs button[aria-selected="true"] {
  color: var(--text);
  background: var(--panel-2);
  border-color: var(--border);
}

.topbar .spacer { flex: 1; }

/* share and reset as one flex item, so a wrap moves the pair together instead
   of splitting them across two lines. */
.topbar .actions { display: inline-flex; gap: 1rem; }

.topbar .opts[hidden] { display: none; }

.topbar .opts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.85rem;
  align-items: center;
  min-width: 0;
  font-size: 0.75rem;
  color: var(--text-dim);
}

/* --- panels fill the remaining viewport --- */

.view { flex: 1 1 auto; min-height: 0; display: none; flex-direction: column; }
.view.active { display: flex; }

/* Eight configs plus the controller row is more vertical space than a
   viewport-filling layout has to give, so the block scrolls rather than
   squeezing the chart out of existence. Together with the readout's cap this
   leaves the chart its floor at any height this layout still applies to. */
.controls {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0.5rem 0.6rem;
  max-height: 45vh;
  overflow-y: auto;
  border-bottom: 1px solid var(--border);
  background: var(--panel);
}

/* One full-width row per config so sliders get real travel. The column count
   is set per row from --param-cols (JS, by how many params this row's model
   actually has), so a two-param model like Legacy throttle splits the row in
   half instead of leaving an empty third column's width unused. Rows with the
   same param count still line up; a model with fewer params trades that
   alignment for sliders worth dragging. */
.config {
  flex: 1 1 100%;
  display: grid;
  grid-template-columns: repeat(var(--param-cols, 3), minmax(0, 1fr));
  align-items: center;
  gap: 0.25rem 1.6rem;
  padding: 0.3rem 0.55rem;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-left: 3px solid var(--slot-color);
  border-radius: 5px;
}

.config.off { opacity: 0.4; }

.config .slot {
  font: 600 0.75rem var(--mono);
  color: var(--slot-color);
}

.config-head {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 0.45rem;
}

/* The label column fits its own text (shrinking below that only if the row
   truly has no room) instead of reserving a fixed 7.8rem for every label
   regardless of how short it is - "Mid" doesn't need the width "Center
   Sensitivity" does, and the slider gets whatever that frees up. */
.param {
  display: grid;
  grid-template-columns: minmax(0, max-content) minmax(40px, 1fr) 4.2rem;
  align-items: center;
  gap: 0.4rem;
}

.param label {
  font-size: 0.78rem;
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: right;
}

.param input[type="range"] { min-width: 0; }

select, input[type="number"] {
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 0.15rem 0.3rem;
  font: 0.78rem var(--mono);
}

input[type="number"] { width: 100%; text-align: right; }
select:focus, input:focus { outline: 1px solid var(--accent); outline-offset: -1px; }

input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  height: 1.75rem;
  margin: 0;
}

input[type="range"]::-webkit-slider-runnable-track { height: 5px; background: var(--border); border-radius: 3px; }
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  margin-top: -6.5px;
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--slot-color, var(--accent));
  cursor: pointer;
}
input[type="range"]::-moz-range-track { height: 5px; background: var(--border); border-radius: 3px; }
input[type="range"]::-moz-range-thumb {
  width: 18px; height: 18px; border: 0; border-radius: 50%;
  background: var(--slot-color, var(--accent));
  cursor: pointer;
}

/* Touchscreens get no hover or fine-pointer precision, so the thumb needs a
   real fingertip-sized target rather than the mouse-tuned one above. */
@media (pointer: coarse) {
  input[type="range"] { height: 2.25rem; }
  input[type="range"]::-webkit-slider-runnable-track { height: 6px; }
  input[type="range"]::-webkit-slider-thumb { width: 24px; height: 24px; margin-top: -9px; }
  input[type="range"]::-moz-range-track { height: 6px; }
  input[type="range"]::-moz-range-thumb { width: 24px; height: 24px; }
}

/* A floor rather than the 0 a pure flex layout would allow: eight configs and
   eight readout rows, both at their cap, must still leave a usable chart at the
   shortest viewport this layout applies to. */
.chart-wrap { flex: 1 1 auto; min-height: 120px; position: relative; }

.chart-wrap canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  cursor: crosshair;
  touch-action: pan-y;
}

/* --- readout strip pinned to the bottom --- */

.readout {
  flex: 0 0 auto;
  display: flex;
  align-items: stretch;
  gap: 0 1.1rem;
  min-height: 3rem;
  max-height: 30vh;
  overflow: hidden;
  padding: 0.25rem 0.7rem;
  border-top: 1px solid var(--border);
  background: var(--panel);
  font: 0.78rem var(--mono);
}

.readout .hint { color: var(--text-dim); }
.readout .k { color: var(--text-dim); }
.readout .v { font-weight: 600; }
.readout .sw { display: inline-block; width: 0.55rem; height: 0.55rem; border-radius: 2px; margin-right: 0.3rem; }

label.check { display: inline-flex; align-items: center; gap: 0.3rem; cursor: pointer; white-space: nowrap; }
input[type="checkbox"] { accent-color: var(--accent); margin: 0; }

.topbar .opts input[type="number"] { width: 3.9rem; }

.topbar .btn-group,
.topbar .btn-group .confirm {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.75rem;
  color: var(--text-dim);
  white-space: nowrap;
}

/* The parts swap by the hidden attribute, which inline-flex would override. */
.topbar .btn-group [hidden] { display: none; }

.topbar .btn-group button {
  background: var(--panel-2);
  color: var(--text-dim);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 0.15rem 0.5rem;
  font: 0.75rem inherit;
  cursor: pointer;
}

.topbar .btn-group button:hover { color: var(--text); border-color: var(--axis); }

/* The one button here that throws something away. */
.topbar #opt-reset-yes:hover { color: var(--text); border-color: var(--series-h); }

.pad {
  flex: 1 1 100%;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.15rem 0.5rem;
  font-size: 0.75rem;
  color: var(--text-dim);
}

.pad .pad-name { font: 0.75rem var(--mono); color: var(--text); }
.pad .hint {
  color: var(--text-dim);
  cursor: help;
  text-decoration: underline dotted var(--border);
  text-underline-offset: 3px;
}

.readout-cursor {
  flex: 0 0 8.5rem;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.readout-rows {
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* Eight rows fit the cap at every size this layout applies to, so scrolling
     is the fallback. `safe` keeps them centred until they overflow, at which
     point centring would put the first row out of reach. */
  justify-content: safe center;
  min-width: 0;
  min-height: 0;
  overflow-x: hidden;
  overflow-y: auto;
}

/* Both strips clip rather than grow, and macOS gives Chrome an overlay
   scrollbar that stays invisible until scrolled, leaving a half-shown row
   looking like a layout fault rather than a hint that there is more. Chromium
   honours these pseudo-elements only while scrollbar-width and scrollbar-color
   are left alone; Firefox draws a real scrollbar without being asked. */
.controls::-webkit-scrollbar,
.readout-rows::-webkit-scrollbar { width: 9px; }

.controls::-webkit-scrollbar-track,
.readout-rows::-webkit-scrollbar-track { background: transparent; }

.controls::-webkit-scrollbar-thumb,
.readout-rows::-webkit-scrollbar-thumb {
  background: var(--axis);
  background-clip: content-box;
  border: 2px solid transparent;
  border-radius: 5px;
}

.readout-rows > div, .readout-cursor > div {
  display: flex;
  gap: 0 1.1rem;
  white-space: nowrap;
  line-height: 1.35;
}

.param label.has-help {
  cursor: help;
  text-decoration: underline dotted var(--border);
  text-underline-offset: 3px;
}

.topbar .opts label.check[title] {
  cursor: help;
  text-decoration: underline dotted var(--text-dim);
  text-underline-offset: 3px;
}

.topbar .opts label.check[title] input { text-decoration: none; }

.config button, .pad button {
  background: var(--bg);
  color: var(--text-dim);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 0.15rem 0.45rem;
  font: 0.72rem var(--mono);
  cursor: pointer;
}

.config button:hover:enabled, .pad button:hover:enabled {
  color: var(--text);
  border-color: var(--slot-color, var(--axis));
}

.config button:disabled, .pad button:disabled { opacity: 0.4; cursor: default; }

/* Stands in for the fit button while a target is being picked, so it sits
   where that button was. The letters carry their curve's chart colour, which
   is the only thing tying them to what they will fit against. */
.config .fit-pick { display: inline-flex; align-items: center; gap: 0.25rem; }
.config .fit-pick[hidden] { display: none; }
.config .fit-pick > span { color: var(--text-dim); }
.config button.target {
  border-color: var(--slot-color);
  /* Matching the row's own letter, which is coloured the same way. */
  color: var(--slot-color);
  padding: 0 0.4rem;
}

/* A link holds two curves fitted to each other, so each end's button carries
   the other curve's colour, the way the picker's letters do. */
.config button.link.on {
  color: var(--slot-color);
  border-color: var(--slot-color);
}

/* The trailing group sits at the far end of its row, which puts reset and
   remove in the config's top right corner whether or not the head has wrapped
   onto its own line. Whichever of the two comes first does the pushing. */
.config button.reset, .config button.del, .pad button.add { margin-left: auto; }
.config button.reset + button.del { margin-left: 0; }

.config button.reset, .config button.del {
  padding: 0 0.4rem;
  font-size: 0.95rem;
  line-height: 1.15;
}

/* A parameter wants well over the ~14.5rem its label, slider and value strictly
   need, since the slider is the control being read. Three per row is the widest
   layout used; below 820px even that squeezes them, so they stack. */
@media (max-width: 820px) {
  .config { grid-template-columns: minmax(0, 1fr); }

  /* One param per row here, so max-content would size each label to its own
     text - "Expo" narrow, "Center Sensitivity" wide - putting every slider at
     a different starting x and throwing off any comparison between a row's
     middle (or any shared value) and the next row's. A fixed width (fitting
     the longest label in use, "Center Sensitivity", plus some slack) keeps
     every slider starting at the same x instead. */
  .param { grid-template-columns: 7.5rem minmax(40px, 1fr) 4.2rem; }
}

/* Small screens, and short ones: a landscape phone is wide but only ~390px
   tall, so the viewport-filling layout has nowhere to put the controls and
   clips them with no way to scroll. Become an ordinary scrolling document
   instead, with the chart at a definite height since flex no longer sizes it. */
@media (max-width: 820px), (max-height: 620px) {
  body {
    height: auto;
    min-height: 100%;
    overflow-y: auto;
    overflow-x: hidden;
  }

  .view.active { display: block; }

  .chart-wrap {
    flex: none;
    height: max(280px, 55vh);
    min-height: 280px;
  }

  /* The page scrolls here, so capping either strip only clips it. */
  .controls {
    max-height: none;
    overflow: visible;
  }

  /* The readout has to grow and wrap here; clipping it is what cut the text
     off, and the chart is no longer resizing underneath the cursor anyway. */
  .readout {
    flex-direction: column;
    align-items: stretch;
    min-height: 3rem;
    max-height: none;
    overflow: visible;
    padding: 0.4rem 0.7rem;
    gap: 0.15rem 0;
  }

  .readout-cursor {
    flex: 0 0 auto;
    flex-direction: row;
    gap: 0 1rem;
  }

  .readout-rows { overflow: visible; }

  .readout-rows > div,
  .readout-cursor > div {
    flex-wrap: wrap;
    white-space: normal;
  }

}
