/* CHROMATICA — control-room / colour-grading panel.
   Cold, precise, monospace interface against warm, organic, historical content.
   The contrast is the point; do not soften it.

   The one rule about the stage: no line, no border, no cell and no grid inside it.
   Black there is not a background, it is the void the colour emerges from. All the
   interface chrome lives in the two HUD bars, never on the field. */

:root {
  --bg: #0a0a0a;
  --bg-panel: #0d0d0d;
  --accent: #00ff9d;          /* the single UI accent. never applied to artwork colour */
  --accent-dim: #00ff9d26;
  --line: #1c1c1c;
  --text: #6f7671;
  --text-bright: #c8d2cb;
  --mono: "JetBrains Mono", "IBM Plex Mono", ui-monospace, SFMono-Regular,
          Menlo, Consolas, monospace;
  --hud-h: 62px;
  --foot-h: 96px;
}

* { box-sizing: border-box; }

/* A `display` rule outranks the hidden attribute, and several elements here are
   flex containers that get hidden. This keeps `hidden` meaning hidden. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: var(--bg);
  color: var(--text);
  font-family: var(--mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body { display: flex; flex-direction: column; }

/* ---------- boot sequence ---------- */
.boot {
  position: fixed; inset: 0; z-index: 90;
  background: var(--bg);
  display: flex; align-items: center; justify-content: center;
  transition: opacity .5s linear;
}
.boot--done { opacity: 0; pointer-events: none; }
.boot__log {
  font-family: var(--mono); font-size: 11px; line-height: 1.7;
  color: var(--accent); margin: 0; white-space: pre;
  text-shadow: 0 0 12px #00ff9d40;
}

/* ---------- HUD ---------- */
.hud {
  flex: 0 0 auto;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  z-index: 20;
}
.hud--top {
  /* A floor, not a height: the blurb wraps differently at every width and the bar
     has to grow with it rather than crop the title off the top. */
  min-height: var(--hud-h);
  display: flex; align-items: center; gap: 20px;
  padding: 7px 14px;
}
.hud--bottom {
  border-bottom: none; border-top: 1px solid var(--line);
  padding: 8px 14px 10px;
  display: flex; flex-direction: column; gap: 7px;
}

.hud__brand { display: flex; flex-direction: column; gap: 2px; margin-right: auto; }
.hud__title {
  color: var(--text-bright); font-weight: 700;
  letter-spacing: .34em; font-size: 12px;
}
/* The one piece of prose in the interface. Wider than the labels around it and a
   shade brighter, so it reads as a sentence rather than as another readout. */
.hud__blurb {
  font-size: 9px; line-height: 1.6; letter-spacing: .05em;
  color: #7d857f; max-width: 64ch;
}
.hud__blurb span { display: block; color: #575e59; }
.hud__sub { letter-spacing: .16em; font-size: 9px; color: #4a504c; margin-top: 2px; }

.readout { display: flex; gap: 20px; margin: 0; }
.readout > div { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
.readout dt { font-size: 8px; letter-spacing: .16em; color: #4a504c; }
.readout dt, .readout dd, .hud__sub { white-space: nowrap; }
/* Each dd is held at the width of its widest possible value (see lockWidths), so
   the text inside it has to be pinned to the right or the numbers would jitter
   inside their own reserved box while you scrub. */
.readout dd { margin: 0; color: var(--text-bright); font-size: 12px; font-weight: 500; text-align: right; }

/* Author and source: same weight as the readout, lit only when pointed at. */
.credit { display: flex; align-items: center; gap: 3px; }
.credit__link {
  display: flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; color: #4a504c;
  transition: color .15s linear;
}
.credit__link svg { width: 12px; height: 12px; display: block; }
.credit__link:hover, .credit__link:focus-visible { color: var(--accent); outline: none; }

/* ---------- stage ---------- */
.stage {
  position: relative; flex: 1 1 auto; min-height: 0;
  overflow: hidden; cursor: crosshair; touch-action: none;
}
#field { display: block; width: 100%; height: 100%; }
.stage.is-scrubbing { cursor: ew-resize; }

/* Says what the surface is, then gets out of the way. */
.stage__hint {
  position: absolute; left: 0; right: 0; bottom: 10px; margin: 0;
  text-align: center; font-size: 8px; letter-spacing: .22em; color: #343a36;
  pointer-events: none; transition: opacity .4s linear;
}
.stage.is-scrubbing .stage__hint { opacity: 0; }

/* Names the two axes, because nothing is drawn on the field to name them. Set a
   step brighter than the hint: which measurement is up and which is across is not
   a footnote, it is the difference between a picture and a decoration. */
.stage__axes {
  position: absolute; left: 0; right: 0; top: 10px; margin: 0;
  text-align: center; font-size: 8px; letter-spacing: .18em; color: #59615c;
  pointer-events: none;
}

.hoverchip {
  position: absolute; pointer-events: none; z-index: 15;
  background: #050505f2; border: 1px solid var(--accent);
  padding: 5px 8px; max-width: 260px;
  font-size: 10px; line-height: 1.5; color: var(--text-bright);
  box-shadow: 0 6px 26px #000000cc;
}
.hoverchip b { color: var(--accent); font-weight: 500; display: block; }
.hoverchip i { color: #6f7671; font-style: normal; }
.hoverchip code { color: var(--text); letter-spacing: .06em; }

/* ---------- bottom controls ---------- */
.controls { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }

/* min-height so the row keeps its box whatever is in it, and centred rather than
   baseline-aligned. The year swaps between 21px digits and a 13px
   word, and two type sizes on a shared baseline sit at different heights in the
   same 26px line box — enough to drop the label beside it by 3px every time the
   mode changed. Both boxes are 26px tall, so centring them holds everything. */
.cursor { display: flex; align-items: center; gap: 8px; min-height: 26px; }
.cursor__range { display: flex; align-items: center; gap: 6px; margin-left: 6px; }
.cursor__range b { color: var(--text-bright); font-size: 11px; font-weight: 500; }
.cursor__label { font-size: 8px; letter-spacing: .16em; color: #4a504c; }
/* Present in the layout, absent from the picture. The footer sits directly under
   the field and the field is a canvas: anything that changes the height of this
   bar changes the size of the canvas above it, and the whole cloud jumps at the
   moment you press a mode button. `hidden` would have taken the box out of flow
   and done exactly that. visibility also keeps these out of the tab order and
   out of the accessibility tree, which is what hidden was there for. */
.is-vacant { visibility: hidden; }

.cursor__year {
  font-size: 21px; font-weight: 500; color: var(--accent);
  letter-spacing: .06em;
  /* Fixed, because the two variants below are 21px and 13px and an auto line box
     would hand the footer two different heights — the same jump again. */
  line-height: 26px;
  /* tabular figures so the digits never shift horizontally while scrubbing */
  font-variant-numeric: tabular-nums;
  text-shadow: 0 0 14px #00ff9d40;
}
/* The whole span is a state, not a number: it should not pretend to be a reading. */
.cursor__year.is-word { font-size: 13px; letter-spacing: .2em; text-shadow: none; }

.filter { display: flex; align-items: center; gap: 7px; margin-left: auto; }
.filter--vs { margin-left: 0; }
.filter--find { margin-left: auto; }
/* The answer, by name. Anchored to the FIND control and opening upward over the
   field, because it is a reading of the field and belongs beside it rather than
   in a page of its own. Narrow and capped in height: it names what matched, it
   does not become the piece. */
.filter--find { position: relative; }
.findlist {
  position: absolute; right: 0; bottom: calc(100% + 9px); z-index: 30;
  margin: 0; padding: 0; list-style: none;
  width: 330px; max-height: 46vh; overflow-y: auto;
  background: #050505f2; border: 1px solid var(--accent);
  box-shadow: 0 8px 34px #000000d9;
}
.findlist li {
  display: flex; gap: 8px; align-items: baseline;
  padding: 4px 9px; cursor: pointer; border-bottom: 1px solid #ffffff0d;
}
.findlist li:last-child { border-bottom: none; }
.findlist li:hover, .findlist li.is-at { background: var(--accent-dim); }
/* The year first and in the accent: this collection is read along time, so the
   date is the coordinate you are looking the work up by. */
.findlist b { color: var(--accent); font-weight: 500; font-size: 10px; flex: 0 0 62px; }
.findlist span { color: var(--text-bright); font-size: 10px; line-height: 1.45; min-width: 0; }
.findlist i { display: block; color: #6f7671; font-style: normal; font-size: 9px; }
.findlist .findlist__more { color: #4a504c; cursor: default; justify-content: center; font-size: 9px; letter-spacing: .14em; }
.findlist .findlist__more:hover { background: none; }
/* What a colour search asked, kept at the top of its own answer. Sticky rather
   than placed above the list: it is the only statement of the question — the
   FIND box is empty during one — and scrolling the results must not scroll it
   away. In the colour itself plus its hex, the same pairing the panel's swatches
   use, so the swatch you pressed and the header naming it read as one thing. */
.findlist .findlist__head {
  position: sticky; top: 0; z-index: 1;
  background: #0d0d0d; border-bottom: 1px solid var(--accent-dim);
  color: #6f7671; cursor: default; font-size: 9px; letter-spacing: .1em;
  align-items: center; padding: 5px 9px;
}
.findlist .findlist__head:hover { background: #0d0d0d; }
.findlist .findlist__head i { width: 18px; height: 11px; flex: 0 0 18px; display: block; }
.findlist__x {
  margin-left: auto; border: none; background: none; color: #575e59;
  font-size: 14px; line-height: 1; padding: 0 2px; min-width: 0;
}
.findlist__x:hover { color: var(--accent); border: none; }
#search {
  appearance: none; background: #111; color: var(--text-bright);
  border: 1px solid var(--line); border-radius: 0;
  font-family: var(--mono); font-size: 10px; letter-spacing: .08em;
  padding: 4px 8px; width: 138px;
}
#search::placeholder { color: #4a504c; letter-spacing: .12em; }
#search:focus-visible { outline: 1px solid var(--accent); outline-offset: 1px; }
/* Height fixed rather than derived from the content: this span is empty until
   you search, and an empty inline box is zero pixels tall, so the first keystroke
   used to shove the row it sits in. Its width is pinned in JS. */
.filter__count { font-size: 8px; letter-spacing: .14em; color: var(--accent); white-space: nowrap;
  display: inline-block; height: 14px; line-height: 14px; }
.filter__label { font-size: 8px; letter-spacing: .16em; color: #4a504c; }
/* Which select owns which chroma line in the strip. The two curves are the same
   near-white — a second colour in there would compete with the histogram — so
   they are told apart by stroke, and these are the same stroke at label size. */
.key { display: inline-block; width: 13px; vertical-align: middle; }
.key--a { border-top: 1px solid var(--text-bright); }
select:disabled { opacity: .4; cursor: not-allowed; }
select {
  appearance: none; background: #111; color: var(--text-bright);
  border: 1px solid var(--line); border-radius: 0;
  font-family: var(--mono); font-size: 10px; letter-spacing: .08em;
  padding: 4px 22px 4px 8px; cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, #4a504c 50%),
                    linear-gradient(135deg, #4a504c 50%, transparent 50%);
  background-position: calc(100% - 12px) 50%, calc(100% - 8px) 50%;
  background-size: 4px 4px, 4px 4px; background-repeat: no-repeat;
}
select:focus-visible, button:focus-visible { outline: 1px solid var(--accent); outline-offset: 1px; }

.btns { display: flex; gap: 5px; flex-wrap: wrap; }
button {
  background: #111; color: var(--text); border: 1px solid var(--line);
  border-radius: 0; font-family: var(--mono); font-size: 10px;
  letter-spacing: .1em; padding: 5px 10px; min-width: 30px; cursor: pointer;
  transition: color .12s linear, border-color .12s linear;
}
button:hover { color: var(--accent); border-color: var(--accent); }
/* A mode that is on has to look on: this is the only latching control here. */
button[aria-pressed="true"] {
  color: var(--accent); border-color: var(--accent); background: #00ff9d0f;
}

/* ---------- span overview ----------
   Works per year on a linear axis: the only place the collection's real, ~60×
   uneven shape in time is visible — and the way into the timelapse. */
/* Relative so the chip that names a mark can be placed against the strip. It has
   to be its own element rather than the field's `.hoverchip`, which is positioned
   in coordinates local to `.stage` and would land in the middle of the cloud. */
.timeline { display: flex; align-items: center; gap: 9px; position: relative; }
.timeline__label {
  font-size: 8px; letter-spacing: .14em; color: #3c423e;
  white-space: nowrap; flex: 0 0 auto;
}
#timelineCanvas {
  flex: 1 1 auto; height: 26px; display: block;
  cursor: ew-resize; touch-action: none;
}
#timelineCanvas.is-onmark { cursor: pointer; }

/* The name of the mark under the pointer. Sits above the strip rather than below
   it — the strip is the last thing on the page, and a chip hanging off the bottom
   would be clipped by the window on every reading. Bordered in the curve's own
   near-white, not the accent: the mark labels the line, and the accent in this
   footer already means the histogram and the timelapse handle. */
.markchip {
  position: absolute; bottom: calc(100% + 7px); left: 0;
  pointer-events: none; z-index: 15;
  background: #050505f2; border: 1px solid var(--text-bright);
  padding: 5px 8px; max-width: 260px;
  font-size: 10px; line-height: 1.5; color: var(--text-bright);
  box-shadow: 0 6px 26px #000000cc;
}
.markchip b { color: var(--text-bright); font-weight: 500; display: block; }
.markchip i { color: #6f7671; font-style: normal; display: block; }

/* ---------- panels ---------- */
.panel {
  position: fixed; z-index: 60;
  right: 14px; bottom: calc(var(--foot-h) + 14px);
  width: 330px; max-width: calc(100vw - 28px);
  background: var(--bg-panel); border: 1px solid #262626;
  box-shadow: 0 20px 60px #000000e6;
  /* Was three discrete steps, which matched the boot sequence's register but read
     as a stutter on something that appears under the cursor several times a
     minute. Continuous now, and still short: an ease-out over 180 ms, arriving
     from 6 px below. Only opacity and transform are animated, so it runs on the
     compositor and cannot be held up by the sheet being filled behind it. */
  animation: panelIn .18s cubic-bezier(.22, .61, .36, 1);
}
@keyframes panelIn { from { opacity: 0; transform: translateY(6px); } }
.panel--about {
  right: 50%; bottom: 50%; transform: translate(50%, 50%);
  width: 420px; max-height: 74vh; overflow-y: auto;
}
.panel__close {
  position: absolute; top: 5px; right: 5px; z-index: 2;
  border: none; background: none; color: #575e59;
  font-size: 17px; line-height: 1; padding: 3px 6px; min-width: 0;
}
.panel__close:hover { color: var(--accent); border: none; }

/* The work is the largest thing on screen once chosen; the sheet sits beside it
   in the same cold register as the rest of the interface. */
.panel--detail { width: 620px; }
.panel__body { display: flex; gap: 16px; padding: 14px; }
.panel__figure {
  margin: 0; flex: 0 0 300px; width: 300px; align-self: flex-start;
  background: #141414; border: 1px solid #232323;
  display: flex; align-items: center; justify-content: center;
  min-height: 120px;
}
#detailImg { display: block; width: 100%; height: auto; }

.panel__meta { min-width: 0; flex: 1 1 auto; }
.panel__eyebrow {
  display: block; font-size: 8px; letter-spacing: .18em; color: var(--accent);
  margin-bottom: 7px;
}
.panel h2 {
  margin: 0 0 3px; font-size: 13px; font-weight: 500; line-height: 1.35;
  color: var(--text-bright); letter-spacing: .01em;
}
.panel__artist { margin: 0 0 11px; font-size: 10px; color: var(--accent); }

/* Hex values are the measurement, so they are set as text next to the colour
   rather than being left implicit in the swatch. */
.swatches { list-style: none; margin: 0 0 12px; padding: 0; display: grid; gap: 2px; }
.swatches li { display: flex; align-items: center; gap: 8px; }
.swatches i { width: 26px; height: 13px; flex: 0 0 26px; }
.swatches li.is-this i { outline: 1px solid var(--accent); outline-offset: 1px; }
.swatches code {
  font-family: var(--mono); font-size: 10px; color: var(--text);
  letter-spacing: .06em;
}
.swatches li.is-this code { color: var(--text-bright); }

/* Sits in the flow between the swatches and the catalogue rows, so the panel
   reads as measurement, then the one question you can ask of it, then
   provenance. Full width: it is the only control in this panel and there is
   nothing for it to line up beside. */
.panel__near {
  display: block; width: 100%; margin: 0 0 13px;
  font-size: 9px; letter-spacing: .14em; padding: 6px 10px;
}

.panel__rows { margin: 0 0 13px; display: grid; gap: 3px; }
.panel__rows > div { display: flex; gap: 8px; }
.panel__rows dt { font-size: 8px; letter-spacing: .14em; color: #4a504c; width: 52px; flex: 0 0 52px; padding-top: 1px; }
.panel__rows dd { margin: 0; font-size: 10px; color: var(--text); }
.panel a {
  display: inline-block; font-size: 9px; letter-spacing: .12em;
  color: var(--accent); text-decoration: none;
  border-bottom: 1px solid var(--accent-dim); padding-bottom: 1px;
}
.panel a:hover { border-bottom-color: var(--accent); }

.about__body { padding: 16px 18px 18px; }
.about__body h2 { letter-spacing: .28em; margin-bottom: 10px; }
.about__body h3 {
  font-size: 8px; letter-spacing: .18em; color: var(--accent);
  margin: 16px 0 6px; font-weight: 400;
}
.about__body p, .about__body li { font-size: 10px; line-height: 1.75; margin: 0 0 8px; }
.about__body ul { margin: 0; padding-left: 14px; }
.about__body li::marker { color: #3c423e; }

@media (max-width: 720px) {
  :root { --foot-h: 116px; --hud-h: 58px; }
  .readout > div:nth-child(n+3) { display: none; }
  .hud__sub, .stage__hint, .cursor__range { display: none; }
  /* The axes line stays: with the sub-header gone it is the only thing left that
     says what the two directions mean. */
  .stage__axes { letter-spacing: .1em; padding: 0 8px; }
  /* Room for one line of prose, not two. */
  .hud__blurb { font-size: 8px; max-width: 40ch; }
  .hud__blurb span { display: none; }
  .timeline__label { display: none; }
  .filter { margin-left: 0; }
  .panel, .panel--detail { right: 7px; left: 7px; width: auto; }
  .panel--detail { max-height: 70vh; overflow-y: auto; }
  .panel__body { flex-direction: column; }
  .panel__figure { flex: 0 0 auto; width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .panel { animation: none; }
  .boot { transition: none; }
}
