/* style.css -- Lunar Modulator's page: the virtual FM-1. One dark theme, the
 * Rosé Pine Moon palette (rosepinetheme.com, MIT; the values are
 * rose-pine/palette's) and Lunar Modulator's four sound hues, which the
 * firmware's screen uses too (src/fm1_look.h), with one meaning per colour
 * (../PALETTE.md; tools/palette.py checks both files).
 * Audiowide (Astigmatic, SIL OFL 1.1) for display type, unmodified, in
 * fonts/audiowide/ with its licence; Exo 2 (Natanael Gama, SIL OFL 1.1) for
 * small text, unmodified, in fonts/exo2/ with its licence.
 * MIT licence, like the rest of this repository. */

@font-face {
  font-family: "Audiowide";
  src: url("fonts/audiowide/Audiowide-Regular.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Exo 2";
  src: url("fonts/exo2/Exo2%5Bwght%5D.ttf") format("truetype");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Rosé Pine Moon */
  --rp-base: #232136;
  --rp-surface: #2a273f;
  --rp-overlay: #393552;
  --rp-muted: #6e6a86;
  --rp-subtle: #908caa;
  --rp-text: #e0def4;
  --rp-love: #eb6f92;
  --rp-gold: #f6c177;
  --rp-rose: #ea9a97;
  --rp-pine: #3e8fb0;
  --rp-foam: #9ccfd8;
  --rp-iris: #c4a7e7;
  --rp-highlight-low: #2a283e;
  --rp-highlight-med: #44415a;
  --rp-highlight-high: #56526e;

  /* Lunar Modulator's own hues, the sounds' colours (fm1_look.h's LUNAR_*) */
  --lunar-nebula: #63a6ff;
  --lunar-nova: #ef8a4a;
  --lunar-aurora: #52d2a5;
  --lunar-comet: #c5df7b;

  /* One meaning per colour, as on the screen (PALETTE.md). Only these name
   * an accent; every rule below names the role. */
  --select: var(--rp-iris);     /* selection, focus, the pressed toggle, accents */
  --held: var(--rp-gold);       /* held or latched: a lit LED */
  --live: var(--rp-foam);       /* live signal: the power lever on */
  --refuse: var(--rp-love);     /* refusal, recording, over the limit */
  --context: var(--rp-rose);    /* context */
  --sound-1: var(--lunar-nebula);
  --sound-2: var(--lunar-nova);
  --sound-3: var(--lunar-aurora);
  --sound-4: var(--lunar-comet);

  /* the page */
  --bg: var(--rp-base);
  --surface: var(--rp-surface);
  --text: var(--rp-text);
  --muted: var(--rp-subtle);
  /* Subtle on surface is 4.46:1, just under WCAG's 4.5 for body text; a
   * tenth of text lifts it to 4.9:1 (on base, subtle alone is 4.9:1). */
  --muted-on-surface: color-mix(in srgb, var(--rp-subtle) 90%, var(--rp-text));
  --border: var(--rp-highlight-med);
  --accent: var(--select);
  --accent-text: var(--rp-base);
  --focus: var(--select);
  --display: "Audiowide", "Exo 2", system-ui, sans-serif;
  --small: "Exo 2", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  /* the device */
  --case: var(--rp-overlay);
  --case-edge: var(--rp-highlight-high);
  --case-inner: var(--rp-highlight-med);
  --print: var(--rp-text);
  --print-rear: var(--rp-subtle);
  --frame: var(--rp-highlight-high);
  --knob: var(--rp-base);
  --knob-ring: var(--rp-muted);
  --knob-mark: var(--rp-text);
  --cap: var(--rp-highlight-med);
  --cap-edge: var(--rp-highlight-high);
  --cap-text: var(--rp-text);
  --cap-lit: var(--held);
  --cap-lit-text: var(--rp-base);
  --key-white: var(--rp-text);
  --key-white-edge: var(--rp-subtle);
  --key-black: var(--rp-base);
  --key-black-edge: var(--rp-highlight-low);
  --key-slot: var(--rp-muted);
  --key-lit: var(--held);
  --combo: var(--rp-subtle);
  --bezel: var(--rp-surface);
  --screen: var(--rp-base);
  --port: var(--rp-base);
  --lever: var(--rp-muted);
  --lever-on: var(--live);
  color-scheme: dark;
}

* { box-sizing: border-box; }

html, body { margin: 0; }

body {
  background: var(--bg);
  color: var(--text);
  font: 16px/1.5 var(--small);
}

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

.top, main { max-width: 1400px; margin: 0 auto; padding: 0 16px; }
.top { padding-top: 24px; }
.brand {
  font: 400 clamp(1.5rem, 1rem + 2.6vw, 2.6rem)/1.15 var(--display);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin: 0;
  color: var(--text);
}
.tagline {
  font: 400 clamp(0.62rem, 0.45rem + 0.9vw, 0.95rem)/1.4 var(--display);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--accent);
  margin: 8px 0 12px;
}
h2 { font: 400 1.1rem/1.3 var(--display); letter-spacing: 0.03em; margin: 0 0 12px; }
.lede { margin: 0 0 20px; color: var(--muted); max-width: 72ch; }

/* ---- the device ---------------------------------------------------------- */

/* The power overlay sits on the wrap, so it covers what is visible however
 * far the panel has been scrolled. */
.device-wrap { position: relative; }
.device-scroll { overflow-x: auto; overscroll-behavior-x: contain; }

.device {
  position: relative;
  width: 100%;
  aspect-ratio: 167.5 / 107;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}

.narrow-hint { display: none; margin: 8px 0 0; color: var(--muted); font-size: 0.9rem; }

/* Narrower than 800 px, the to-scale panel would leave its smallest targets
 * (OCT-/OCT+, 5.2 mm tall) under 24 px, WCAG 2.5.8's minimum. It keeps
 * 800 px (4.8 px/mm: keys 35 px and 31 px wide, buttons 34 px, OCT 25 px
 * tall) and scrolls sideways in its own box, never the page; app.js pans it
 * when the case is dragged. */
@media (max-width: 831px) {
  .device { width: 800px; }
  .narrow-hint { display: block; }
}

#panel { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }

/* The TFT's active area: 27.72 mm square at (48.96, 10.89) mm in a viewBox
 * that starts at (-3, -8) and spans 167.5 x 107 mm. */
#tft {
  position: absolute;
  left: calc((48.96 + 3) / 167.5 * 100%);
  top: calc((10.89 + 8) / 107 * 100%);
  width: calc(27.72 / 167.5 * 100%);
  height: calc(27.72 / 107 * 100%);
  image-rendering: pixelated;
  background: var(--screen);
}

.case { fill: var(--case); stroke: var(--case-edge); stroke-width: 0.5; }
.case-inner { fill: none; stroke: var(--case-inner); stroke-width: 0.35; }
.print { fill: var(--print); font-family: var(--small); font-weight: 600; font-size: 2.15px; text-anchor: middle; letter-spacing: 0.08px; }
.print.small { font-size: 1.7px; }
.print.rear { fill: var(--print-rear); }
.frame { fill: none; stroke: var(--frame); stroke-width: 0.45; }
.bezel { fill: var(--bezel); stroke: var(--frame); stroke-width: 0.4; }
.port { fill: var(--port); }
.port-hole { fill: #000; }

.knob { cursor: ns-resize; }
.knob .body { fill: var(--knob); }
.knob .ring { fill: none; stroke: var(--knob-ring); stroke-width: 0.6; stroke-dasharray: 0.45 0.35; }
.knob .mark { stroke: var(--knob-mark); stroke-width: 0.55; stroke-linecap: round; }
.knob:focus { outline: none; }
.knob:focus-visible .body { stroke: var(--focus); stroke-width: 0.5; }

.btn { cursor: pointer; }
.btn .cap { fill: var(--cap); stroke: var(--cap-edge); stroke-width: 0.3; }
.btn .label { fill: var(--cap-text); font: 700 1.85px var(--small); text-anchor: middle; }
.btn .label.two { font-size: 1.55px; }
.btn .divider { stroke: var(--cap-text); stroke-width: 0.18; }
.btn.lit .cap { fill: var(--cap-lit); stroke: color-mix(in srgb, var(--cap-lit) 65%, var(--rp-base)); filter: drop-shadow(0 0 0.8px var(--cap-lit)); }
.btn.lit .label { fill: var(--cap-lit-text); }
.btn.lit .divider { stroke: var(--cap-lit-text); }
.btn.down .cap { transform-box: fill-box; transform-origin: center; transform: scale(0.94); }
.btn:focus { outline: none; }
.btn:focus-visible .cap { stroke: var(--focus); stroke-width: 0.6; }

.key { cursor: pointer; }
.key .cap { stroke-width: 0.3; }
.key.white .cap { fill: var(--key-white); stroke: var(--key-white-edge); }
.key.black .cap { fill: var(--key-black); stroke: var(--key-black-edge); }
.key .slot { fill: var(--key-slot); }
.key .combo { fill: var(--combo); font: 600 1.35px var(--small); text-anchor: middle; }
.key.lit .slot { fill: var(--key-lit); filter: drop-shadow(0 0 0.7px var(--key-lit)); }
.key.lit.white .cap { fill: color-mix(in srgb, var(--key-lit) 22%, var(--rp-text)); }
.key.lit.black .cap { fill: var(--rp-highlight-med); }

.power .lever { fill: var(--lever); transition: transform 120ms; }
.power.on .lever { transform: translateX(2.2px); fill: var(--lever-on); }
.power { cursor: pointer; }

.power-overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--bg) 60%, transparent);
  border-radius: 12px;
  padding: 16px;
}
.power-overlay[hidden] { display: none; }
.power-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  max-width: 24rem;
  padding: 20px 24px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 8px 28px rgb(0 0 0 / 0.35);
  text-align: center;
}
.power-card p { margin: 0; color: var(--muted-on-surface); font-size: 0.92rem; }

button, select {
  font: inherit;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 6px 12px;
  min-height: 36px;
}
button { cursor: pointer; }
button:hover:not(:disabled), select:hover:not(:disabled) { border-color: var(--rp-highlight-high); }
button:disabled, select:disabled { opacity: 0.55; cursor: default; }
button:focus-visible, select:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
button[aria-pressed="true"] { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }
.power-button {
  font: 400 1.2rem/1.2 var(--display);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: var(--accent);
  color: var(--accent-text);
  border-color: var(--accent);
  padding: 12px 28px;
  min-height: 48px;
}
/* hover lifts the accent toward text: still the selection colour, and base
 * text on it gains contrast */
.power-button:hover {
  background: color-mix(in srgb, var(--accent) 75%, var(--rp-text));
  border-color: color-mix(in srgb, var(--accent) 75%, var(--rp-text));
}

/* ---- controls and help ------------------------------------------------- */

.controls {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 16px;
  align-items: flex-end;
  margin-top: 16px;
}
.field { display: flex; flex-direction: column; gap: 4px; min-width: 160px; }
.field label { font-size: 0.85rem; color: var(--muted); }
.buttons { display: flex; flex-wrap: wrap; gap: 8px; }
.status { color: var(--muted); margin: 10px 0 0; min-height: 1.5em; font-variant-numeric: tabular-nums; }

/* ---- files (files.js) ---------------------------------------------------- */

.files {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 16px;
  align-items: center;
  margin-top: 12px;
}
.save-group { display: flex; gap: 8px; flex-wrap: wrap; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* The banner over the status line: a load, a refusal, a save. A refusal is
 * the refusal colour (PALETTE.md), on its border and its first line. */
.file-notice {
  margin: 12px 0 0;
  padding: 10px 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 4px solid var(--select);
  border-radius: 10px;
  max-width: 72rem;
}
.file-notice[hidden] { display: none; }
.file-notice.loaded { border-left-color: var(--live); }
.file-notice.refused { border-color: color-mix(in srgb, var(--refuse) 55%, var(--border)); border-left-color: var(--refuse); }
.file-notice.refused .notice-text { color: var(--refuse); }
.file-notice p { margin: 0; }
.file-notice .notice-sub { margin-top: 4px; font-size: 0.88rem; color: var(--muted-on-surface); }
.notice-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.link-out { display: block; width: 100%; margin-top: 8px; font: 0.85rem ui-monospace, Menlo, Consolas, monospace;
  color: var(--text); background: var(--bg); border: 1px solid var(--border); border-radius: 6px; padding: 6px 8px; }

.target-card {
  margin: 12px 0 0;
  padding: 12px 14px;
  background: var(--surface);
  border: 1px solid var(--select);
  border-radius: 10px;
  max-width: 40rem;
}
.target-card[hidden] { display: none; }
.target-card p { margin: 0 0 10px; }
.target-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.target-row .field { min-width: 0; }
button.primary { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }

.library { margin: 14px 0 0; }
.library summary {
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  color: var(--accent);
  font-weight: 600;
}
.library summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.library-lists { display: grid; gap: 14px; margin-top: 8px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); }
.lib-section { padding: 12px 14px; background: var(--surface); border: 1px solid var(--border); border-radius: 10px; }
.lib-section h3 { font: 400 0.95rem/1.3 var(--display); letter-spacing: 0.03em; margin: 0 0 8px; }
.lib-section ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.lib-section li { display: grid; grid-template-columns: 1fr auto; gap: 2px 12px; align-items: center; }
.lib-name { overflow-wrap: anywhere; }
.lib-meta { grid-column: 1; font-size: 0.82rem; color: var(--muted-on-surface); }
.lib-actions { grid-column: 2; grid-row: 1 / span 2; display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.lib-actions button { min-height: 32px; padding: 4px 10px; font-size: 0.9rem; }
.lib-empty { margin: 0; font-size: 0.88rem; color: var(--muted-on-surface); }

/* A link's file, on the power card. */
.arrival { display: grid; gap: 6px; text-align: center; }
.arrival[hidden] { display: none; }
.arrival-kind { margin: 0; font-size: 0.8rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--context); }
.arrival-title { margin: 0; font: 400 1.25rem/1.25 var(--display); }
.power-card .arrival p:not([hidden]) { display: block; }
.quiet-button { background: none; border-color: transparent; color: var(--muted-on-surface); }

/* A control a link or the guide points at: a ring in the selection colour,
 * clear of the control's edge and of its printed name. */
.hl-ring { fill: none; stroke: var(--select); stroke-width: 0.5; pointer-events: none;
  animation: hl-pulse 1.6s ease-in-out infinite; }
@keyframes hl-pulse { 50% { stroke-opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) { .hl-ring { animation: none; } }

/* ?embed=1: the panel and its controls only, inside a guide page. */
body.embed .top, body.embed .help, body.embed .narrow-hint { display: none; }

/* Files dragged over the page: where to drop them. */
.drop-hint {
  position: fixed;
  inset: 12px;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  border: 2px dashed var(--accent);
  border-radius: 16px;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  pointer-events: none;
}
.drop-hint[hidden] { display: none; }
.drop-hint p {
  margin: 0;
  max-width: 28rem;
  padding: 16px 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  font: 400 1.1rem/1.4 var(--display);
  text-align: center;
  text-wrap: balance;
}

.mirror { margin-top: 16px; }
.mirror canvas {
  width: min(480px, 100%);
  aspect-ratio: 1;
  image-rendering: pixelated;
  background: var(--screen);
  border: 1px solid var(--border);
  border-radius: 6px;
  display: block;
}

.help {
  margin: 24px 0 40px;
  padding: 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.help dl { margin: 0; display: grid; gap: 10px; }
.help dl > div { display: grid; grid-template-columns: 9rem 1fr; gap: 12px; }
.help dt { font-weight: 600; color: var(--accent); }
.help dd { margin: 0; }
.help .fine { margin: 14px 0 0; font-size: 0.85rem; color: var(--muted-on-surface); }
.licence-note { margin: 14px 0 0; padding-left: 10px; border-left: 2px solid var(--context);
  font-size: 0.85rem; color: var(--muted-on-surface); }
kbd {
  font: 0.85em/1 ui-monospace, Menlo, Consolas, monospace;
  color: var(--text);
  border: 1px solid var(--border);
  border-bottom-width: 2px;
  border-radius: 4px;
  padding: 1px 5px;
  background: var(--bg);
}
code { font: 0.9em ui-monospace, Menlo, Consolas, monospace; color: var(--text); }

@media (max-width: 640px) {
  .power-card { padding: 14px 16px; }
  .power-card p { display: none; }
  .help dl > div { grid-template-columns: 1fr; gap: 2px; }
  .field { min-width: 0; flex: 1 1 140px; }
}

/* A phone on its side: the panel, not the header, gets the first screenful,
 * and Power on sits where it can be seen without scrolling. */
@media (max-height: 520px) {
  .top { padding-top: 12px; }
  .brand { font-size: 1.4rem; }
  .tagline { margin: 4px 0 10px; }
  .lede { display: none; }
  /* below the rear ports' labels, over the case's top row */
  .power-overlay { align-items: flex-start; padding-top: 6%; }
}

/* ---- the Advanced editor's layouts (stage ED2, notes/2026-10-06-web-editor.md §4) ----
 * The switch sits in the page's header in Panel and moves into the editor's
 * app bar in Workbench and Editor (editor/editor.js); the editor's own look
 * is editor/editor.css, loaded with it. */
.top { position: relative; }
.layout-switch {
  display: inline-flex; gap: 2px; padding: 2px; border: 1px solid var(--border);
  border-radius: 8px; background: var(--surface); float: right; margin: 0 0 8px 16px;
}
.layout-switch button {
  border: 0; background: transparent; color: var(--text); padding: 4px 12px; min-height: 28px;
  border-radius: 6px; font: 500 0.9rem/1.2 var(--small);
}
.layout-switch button[aria-checked="true"] { background: var(--select); color: var(--accent-text); }
.layout-switch .ly-short { display: none; }
/* Phones (stage ED5a): two tabs, Panel and Edit; the Workbench, which puts
 * the panel and the editor in one page, is for screens that have the room. */
@media (max-width: 640px) {
  .layout-switch [data-layout="workbench"] { display: none; }
  .layout-switch .ly-long { display: none; }
  .layout-switch .ly-short { display: inline; }
}
body[data-layout="workbench"] .top .lede, body[data-layout="editor"] .top .lede { display: none; }
body[data-layout="workbench"] .top, body[data-layout="workbench"] main,
body[data-layout="editor"] .top, body[data-layout="editor"] main { max-width: 1720px; }
.editor-host[hidden] { display: none; }
.editor-host { margin-top: 16px; min-width: 0; }
/* Side by side from 1,400 px: the panel keeps the 800 px under which its
 * smallest targets would drop below 24 px (above); narrower, it stacks. */
@media (min-width: 1400px) {
  body[data-layout="workbench"] .stage {
    display: grid; grid-template-columns: minmax(800px, 46fr) minmax(0, 54fr); gap: 0 20px; align-items: start;
  }
  body[data-layout="workbench"] .stage > .device-wrap { grid-column: 1; grid-row: 1; position: sticky; top: 8px; }
  body[data-layout="workbench"] .stage > .narrow-hint { grid-column: 1; grid-row: 2; }
  body[data-layout="workbench"] .stage > .editor-host { grid-column: 2; grid-row: 1 / span 2; margin-top: 0; }
}
/* Editor: the panel folds to its screen, which the editor's outline draws.
 * The power overlay goes with it; the editor has its own Power on. */
body[data-layout="editor"] .stage > .device-wrap,
body[data-layout="editor"] .stage > .narrow-hint { display: none; }
