/* ══ SIGNAL: the shared visual language ═══════════════════════════════════════════════════
   One surface treatment, one control vocabulary, applied identically everywhere — the fix for
   "a world of bolt-ons with no cohesion". Three ideas only:
     1. A card is a soft radial GLOW that falls off to black at its own edges (never a flat
        fill with a hairline border). Its hue is the one thing that varies per tool.
     2. Everything carries the same fine GRAIN. It is what makes the surfaces feel like
        photographic material rather than flat CSS.
     3. A value is stated ONCE, large and calm, in the card — not repeated in a number box,
        a slider label, and a readout paragraph.

   This file was Studio's alone until it wasn't. It is now linked by every page in the dark
   family, AFTER /css/desktop-tokens.css, and it deliberately does not name a colour of its own.

   THE ACCENT. Every "selected / active / current value" mark reads --sig-accent, which each page
   sets once in its own :root and nothing else overrides:
       Studio   --sig-accent: var(--lime)   #E7FE55
       Mono     --sig-accent: var(--safe)   safelight amber
       Print    --sig-accent: var(--safe)   fine-print gold
       Restore  --sig-accent: var(--safe)   neural cyan
   The fallback chain ends at Studio's lime so a page that forgets still renders, but a page that
   sets its own accent never sees lime — Mono and Print keep their identity, which is the point.
   The colour discipline underneath is unchanged: the accent is the ONLY selected/active signal,
   and a card's own hue only ever tints a glow surface, never fills a control. ══════════════ */
:root{
  --sig-r:30px;              /* squircle, not a rounded rectangle — see the reference cards */
  --sig-ease:cubic-bezier(.22,.61,.36,1);
  --sig-dur:.34s;
  --sig-accent:var(--lime,#E7FE55);

  /* ── MOTION ────────────────────────────────────────────────────────────────────────────
     One scale of durations and three curves, used everywhere, so movement reads as one hand.
     The governing idea is SETTLE: things leave quickly and arrive slowly, the way a needle
     swings to a reading and stops. Nothing in this scale is long enough to make a grader wait —
     the longest is the app's own first entrance, which happens once. */
  --sig-t-instant:90ms;      /* state that must feel like it already happened: hover, press */
  --sig-t-quick:160ms;       /* a small thing changing: a tick lighting, a chip selecting */
  --sig-t-base:240ms;        /* the default: a panel row, a swap, a fade */
  --sig-t-settle:420ms;      /* a value coming to rest, a card taking its place */
  --sig-t-enter:620ms;       /* first paint, and only first paint */
  /* fast out, long calm tail — the settle curve, and the one to reach for by default */
  --sig-e-out:cubic-bezier(.22,1,.36,1);
  --sig-e-in-out:cubic-bezier(.65,0,.35,1);
  /* a touch of overshoot, reserved for SELECTION — the only moment that should feel springy */
  --sig-e-spring:cubic-bezier(.34,1.56,.64,1);
  --sig-lift:3px;            /* how far anything ever rises toward the pointer */
  --sig-rise:10px;           /* how far anything travels on entrance */
  /* Fine monochrome grain, tiled. Two octaves of feTurbulence so it reads as film grain
     rather than a regular dot screen; very low opacity so it textures without dirtying. */
  --sig-grain:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}
/* The glow surface. --sig-hue is set per instance (a stage accent, a zone colour, whatever the
   card is actually about); everything else is identical everywhere, which is the whole point. */
.sig-glow{
  position:relative; border-radius:var(--sig-r); overflow:hidden; isolation:isolate;
  /* An INVERTED vignette, which is the actual form in the reference set: the hue is saturated
     at the RIM and falls to black in the CENTRE, and the type sits in that black core. Every
     previous attempt here had it backwards — a bright corner falling to dark edges — which is
     why the cards read as flat grey slabs no matter how the stops were tuned. Lighting a card
     from a corner makes an object; lighting it from the rim makes a WINDOW, and the number in
     the middle is then on true black with nothing competing with it.
     The hue is used at full strength, not mixed down: it only ever appears at the extreme edge,
     so even a pale accent stays a rim rather than a wash. */
  background:
    radial-gradient(115% 125% at 50% 50%,
      #000 0%,
      #000 30%,
      color-mix(in srgb, var(--sig-hue,#6b7a86) 42%, #000) 62%,
      color-mix(in srgb, var(--sig-hue,#6b7a86) 88%, #000) 88%,
      var(--sig-hue,#6b7a86) 100%),
    #000;
  box-shadow:0 18px 44px -26px rgba(0,0,0,.95);
}
/* Grain sits above the gradient, below content — one rule, every glow surface, no exceptions. */
.sig-glow::after{
  content:''; position:absolute; inset:0; z-index:0; pointer-events:none;
  background-image:var(--sig-grain); background-size:140px 140px;
  /* Heavier than before, and soft-light rather than overlay: the reference cards are visibly
     grainy across the whole colour field, which is what stops a big smooth gradient reading as
     cheap CSS. Soft-light keeps the black core black instead of lifting it to charcoal. */
  opacity:.34; mix-blend-mode:soft-light;
}
.sig-glow > *{position:relative; z-index:1}
/* The big calm value. One statement per card — never duplicated into a spinbox beside it. */
.sig-val{display:flex; align-items:baseline; gap:6px; color:#fff;
  font:300 40px/1 var(--font-sans); letter-spacing:-.02em; font-variant-numeric:tabular-nums}
.sig-val small{font:500 14px/1 var(--font-sans); opacity:.75; letter-spacing:0}
.sig-cap{font:500 13px/1.3 var(--font-sans); color:rgba(255,255,255,.92); letter-spacing:-.01em}
.sig-sub{font:500 10px/1.3 var(--font-serif); letter-spacing:.11em; text-transform:uppercase;
  color:rgba(255,255,255,.42)}
/* ── the tick scale: the one range control ───────────────────────────────────────────────
   Replaces a labelled slider + number box + reset button (three widgets for one value) with a
   single object you drag. Ticks are drawn, not an <input> track, so the pointer is the value
   and there is no second copy of the number to keep in sync. The native range input stays
   underneath (opacity 0) so keyboard, screen readers, and every existing [data-p] binding and
   drag handler keep working untouched — this is purely a new skin over the same input. */
.sig-scale{position:relative; height:46px; display:block; touch-action:none; cursor:ew-resize}
.sig-scale input[type=range]{position:absolute; inset:0; width:100%; height:100%; margin:0;
  opacity:0; cursor:ew-resize; z-index:2}
.sig-scale-ticks{position:absolute; inset:0; display:flex; align-items:center;
  justify-content:space-between; pointer-events:none}
/* Hairline ticks — the reference's scales read as a fine ruler, so a tick is 1px of ink with
   air around it, never a filled block. Taller and brighter than the first pass: at 11px and
   .22 alpha the whole ruler greyed out into a barcode instead of reading as an instrument. */
.sig-scale-ticks i{flex:0 0 1px; width:1px; height:16px; background:rgba(255,255,255,.30);
  transition:height .18s var(--sig-ease), background .18s var(--sig-ease), opacity .18s var(--sig-ease)}
.sig-scale-ticks i.on{background:rgba(255,255,255,.72); height:19px}
/* The marker is a tick with a FLAG on it, exactly as in the reference — the pennant is what
   makes the current value findable at a glance in a column of forty-nine identical hairlines.
   Drawn with a border triangle on ::before so it costs no extra element and inherits the accent. */
.sig-scale-ticks i.mark{width:2px; flex-basis:2px; height:30px; background:var(--sig-accent);
  box-shadow:0 0 14px 2px color-mix(in srgb,var(--sig-accent) 50%,transparent)}
.sig-scale-ticks i.mark::before{
  content:''; position:absolute; left:50%; margin-left:-5px; bottom:100%;
  border:5px solid transparent; border-top-width:7px; border-bottom-width:0;
  border-top-color:var(--sig-accent); filter:drop-shadow(0 0 6px color-mix(in srgb,var(--sig-accent) 55%,transparent))}
.sig-scale-ticks i{position:relative}
.sig-scale:hover .sig-scale-ticks i{height:18px}
.sig-scale:hover .sig-scale-ticks i.on{height:21px}
.sig-scale:hover .sig-scale-ticks i.mark{height:33px}

/* ══ MECHANISM + PERSISTENCE ══════════════════════════════════════════════════════════════
   al's call: combine them. They turn out to be complementary rather than overlapping —
   MECHANISM is what an instrument LOOKS like, PERSISTENCE is how it BEHAVES IN TIME. The four
   additions below are the ones every judge, on every lens, said had to survive whichever
   direction won.

   1 · THE BASELINE. A ruler has a rule. Forty-nine hairlines with nothing under them read as a
       barcode; one line beneath them and the same forty-nine read as a graduated scale. This is
       the cheapest and largest single improvement in the whole exercise.
   2 · MAJOR GRADUATIONS. Every sixth tick is taller and brighter, so the eye can count without
       counting — the difference between a scale and a texture.
   3 · TICK MAGNETISM (PERSISTENCE). While dragging, the ticks nearest the marker grow toward it,
       driven by a --near custom property with NO transition, so the ruler bends around the
       pointer at input latency rather than chasing it a frame behind.
   4 · THE VISIBLE DETENT (MECHANISM). Crossing a major graduation flashes that one tick white for
       a moment. Six or seven of those across a pull is a detent you can SEE without the value ever
       snapping to it — which would be unacceptable in a tool where the in-between values matter. */

/* 1 · the baseline. Sits under the ticks, brightens while the scale is in use. */
.sig-scale-ticks::after{content:''; position:absolute; left:0; right:0; bottom:11px; height:1px;
  background:rgba(255,255,255,.14); pointer-events:none;
  transition:background var(--sig-t-quick) var(--sig-e-out)}
.sig-scale:hover .sig-scale-ticks::after,
.sig-scale[data-live] .sig-scale-ticks::after{background:rgba(255,255,255,.3)}

/* 2 · every sixth tick is a graduation you can count on */
.sig-scale-ticks i.maj{height:23px; background:rgba(255,255,255,.46)}
.sig-scale:hover .sig-scale-ticks i.maj{height:25px}
.sig-scale-ticks i.maj.on{background:rgba(255,255,255,.82)}
/* The centre graduation, matching Chain's nth-child(25): on a symmetric slider this is the mark
   the eye checks the marker against, so it is the tallest and brightest thing on an untouched
   ruler. Without it 50 was just another major and "the middle" had to be inferred. */
.sig-scale-ticks i.mid{height:27px; background:rgba(255,255,255,.60)}
.sig-scale:hover .sig-scale-ticks i.mid{height:29px}
.sig-scale-ticks i.mid.on{background:rgba(255,255,255,.90)}
.sig-scale[data-live] .sig-scale-ticks i.mid{--sig-tick-h:27px}

/* 3 · magnetism. --near runs 1 at the marker to 0 three ticks away. Deliberately NO transition:
   the whole point is that it tracks the pointer rather than easing after it. */
.sig-scale[data-live] .sig-scale-ticks i{transition:none;
  height:calc(var(--sig-tick-h,16px) + var(--near,0) * 7px)}
.sig-scale[data-live] .sig-scale-ticks i.maj{--sig-tick-h:23px}
.sig-scale[data-live] .sig-scale-ticks i.mark{--sig-tick-h:30px}

/* 4 · the detent flash. Deliberately a DATA ATTRIBUTE, not a class: both painters rewrite every
   tick's className wholesale on each sync, and a second sync in the same tick would silently wipe
   a flash the first one had just added. dataset survives a className assignment; a class does not. */
.sig-scale-ticks i[data-detent]{background:#fff !important; opacity:1 !important;
  box-shadow:0 0 9px 1px rgba(255,255,255,.5)}

/* The marker's stem drops through the whole scale, so the value is findable even where the
   travel band is empty (a control sitting exactly at its default). */
.sig-scale-ticks i.mark::after{content:''; position:absolute; left:50%; margin-left:-.5px;
  top:100%; width:1px; height:9px; background:color-mix(in srgb,var(--sig-accent) 40%,transparent)}

/* THE DRAG LAW (PERSISTENCE). While a value is being dragged, nothing in the panel is allowed to
   animate — a photo editor may never make a grader wait for chrome. Scoped to the stack rather
   than the document so toggling it cannot force a whole-page style recalculation mid-gesture. */
body[data-dragging] .stack-col *,
body[data-dragging] .panel *{transition:none !important; animation:none !important}
/* A TINTED ruler — Kelvin, tint, hue, an HSL band. The ticks carry the scale's own colour
   (see sigRowAttr's `tint`), which is what the old solid .lr-grad-* track used to say and what
   a .sig-scale's opacity-0 input can no longer show. Only the ink changes: height still carries
   state, so these rules deliberately set NO height — the base and :hover rules above own that,
   and this block sits after them at equal specificity, so setting height here would silently
   kill the hover growth. The marker stays the accent with a dark rim so it can never disappear
   into a tick that happens to be the same colour (a lime mark on a yellow-green hue tick). */
.sig-scale-ticks.tinted i{background:var(--tc); opacity:.5}
.sig-scale-ticks.tinted i.on{background:var(--tc); opacity:1}
.sig-scale-ticks.tinted i.mark{background:var(--sig-accent); opacity:1;
  box-shadow:0 0 0 1px rgba(0,0,0,.85), 0 0 12px 1.5px color-mix(in srgb,var(--sig-accent) 55%,transparent)}
/* ── the zone ramp: eleven lit blocks, not eleven flat fills ────────────────────────────
   ONE strip, not eleven pills. A zone ramp is a continuous tonal scale, so the cells butt
   together with no gaps and no per-cell rims — rounding and shading belong to the whole strip.
   The eleven colours ARE the information, so they are never dimmed; selection is carried by a
   single accent bar on the bottom edge plus label brightness, and nothing else. */
.sig-ramp{position:relative; display:flex; height:42px; border-radius:9px; overflow:hidden;
  isolation:isolate; box-shadow:0 1px 0 rgba(255,255,255,.06), 0 10px 24px -16px rgba(0,0,0,.9)}
/* one soft top-light + one grain pass over the entire strip, not per cell */
.sig-ramp::before{content:''; position:absolute; inset:0; z-index:2; pointer-events:none;
  background:linear-gradient(180deg, rgba(255,255,255,.14) 0%, rgba(255,255,255,0) 34%, rgba(0,0,0,.3) 100%)}
.sig-ramp::after{content:''; position:absolute; inset:0; z-index:3; pointer-events:none;
  opacity:.13; mix-blend-mode:overlay;
  background-image:var(--sig-grain); background-size:140px 140px}
.sig-ramp button{position:relative; flex:1; border:none; cursor:pointer; padding:0 0 4px;
  display:flex; align-items:flex-end; justify-content:center;
  font:600 9.5px var(--font-serif); letter-spacing:.03em;
  transition:box-shadow .18s var(--sig-ease)}
.sig-ramp button > span{opacity:.5; transition:opacity .18s var(--sig-ease)}
.sig-ramp button:hover > span{opacity:.8}
.sig-ramp button.on > span{opacity:1; font-weight:700}
/* the one selection signal */
.sig-ramp button.on{box-shadow:inset 0 -3px 0 0 var(--sig-accent)}
/* edited: a small pip, top-centre of its own cell */
.sig-ramp button.edited::after{content:''; position:absolute; top:5px; left:50%;
  width:3px; height:3px; margin-left:-1.5px; border-radius:50%; background:var(--sig-accent)}
/* ── the dial: circular geometry, per the "circles for anything radial" preference ──────── */
.sig-dial{position:relative; width:64px; height:64px; flex-shrink:0}
.sig-dial svg{position:absolute; inset:0; width:100%; height:100%; transform:rotate(-90deg)}
.sig-dial-cap{position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  font:500 12px/1 var(--font-sans); color:#fff; font-variant-numeric:tabular-nums}
/* ── row: one line, one job. label left, value right, scale under. ─────────────────────── */
.sig-row{padding:2px 0 5px}
.sig-row-head{display:flex; align-items:center; justify-content:flex-end; gap:7px}
.sig-row-name{font:500 12.5px/1 var(--font-sans); color:rgba(255,255,255,.88);
  letter-spacing:-.01em; margin-right:auto}
/* The value is a real number input that LOOKS like the calm statement — type into it, or use
   the nudge pair (attached by the same shared code every .lr-row already uses). Chrome/Safari
   spinners are suppressed: the nudge buttons are the affordance, at a step the tool chose. */
.sig-row-val{font:400 13px/1 var(--font-sans); color:#fff; font-variant-numeric:tabular-nums;
  background:none; border:none; outline:none; text-align:right; width:62px; padding:3px 0;
  border-bottom:1px solid transparent; transition:border-color .15s var(--sig-ease)}
.sig-row-val::-webkit-outer-spin-button,.sig-row-val::-webkit-inner-spin-button{-webkit-appearance:none; margin:0}
.sig-row-val[type=number]{-moz-appearance:textfield; appearance:textfield}
.sig-row-val:hover{border-bottom-color:rgba(255,255,255,.18)}
.sig-row-val:focus{border-bottom-color:var(--sig-accent)}
.sig-row-val.zero{color:rgba(255,255,255,.34)}
.sig-row-unit{font:500 11px/1 var(--font-sans); color:rgba(255,255,255,.4); margin-left:-4px}
.sig-row .field-nudge{opacity:0}
.sig-row:hover .field-nudge,.sig-row .field-nudge:focus-within{opacity:1}
@media(prefers-reduced-motion:reduce){
  .sig-scale-ticks i, .sig-glow{transition:none}
}

/* ══ THE INSTRUMENTS ═══════════════════════════════════════════════════════════════════════
   Drawn by js/signal/instruments.js as inline SVG. The markup is dumb; everything about how
   they look and move lives here, so a page can retheme an instrument without touching JS.
   ────────────────────────────────────────────────────────────────────────────────────────── */

/* ── dot-matrix numerals ────────────────────────────────────────────────────────────────────
   The reference draws its hero numbers as grids of dots, and only the LIT dots are drawn — there
   is no dim background grid, which is what stops it looking like a calculator. Sized by height;
   width follows the viewBox, so a 3-digit number is simply wider than a 2-digit one. */
.sig-dm{display:inline-block; height:1em; width:auto; overflow:visible; vertical-align:-.06em}
.sig-dm circle{transform-box:fill-box; transform-origin:center}
/* The change animation. Dots do not cross-fade — each one drops in and settles, staggered
   left-to-right by its column, so a number reads as being SET rather than swapped. The class is
   added for one frame pair by setDotMatrix() and then removed, so it runs exactly once per real
   change and never on a re-render. */
.sig-dm-changing circle{animation:sigDmSet var(--sig-t-settle) var(--sig-e-out) both;
  animation-delay:calc(var(--dm-col,0) * 16ms)}
@keyframes sigDmSet{
  from{opacity:0; transform:translateY(-22%) scale(.55)}
  60%{opacity:1}
  to{opacity:1; transform:none}
}

/* ── the radial dial ────────────────────────────────────────────────────────────────────────
   A machined white disc in a ring of ticks, one tick lit in the accent. The knob is deliberately
   the brightest thing in the component: in the reference it is the physical object and everything
   else is scale around it. */
.sig-rd{display:block; width:100%; height:auto; overflow:visible}
.sig-rd-tick{stroke:rgba(255,255,255,.34); stroke-width:2.4; stroke-linecap:round;
  transition:stroke var(--sig-t-quick) var(--sig-e-out), stroke-width var(--sig-t-quick) var(--sig-e-out)}
.sig-rd-tick.on{stroke:var(--sig-accent); stroke-width:3.4;
  filter:drop-shadow(0 0 5px color-mix(in srgb,var(--sig-accent) 60%,transparent))}
.sig-rd-guides line{stroke:rgba(255,255,255,.22); stroke-width:1.2; stroke-dasharray:3 5}
.sig-rd-ring{fill:none; stroke:rgba(255,255,255,.16); stroke-width:1.4}
.sig-rd-knob{fill:#EDEBE6;
  filter:drop-shadow(0 6px 14px rgba(0,0,0,.75)) drop-shadow(0 1px 0 rgba(255,255,255,.35))}
.sig-rd-screw{fill:rgba(0,0,0,.16)}
.sig-rd-label{font:500 13px var(--font-serif,monospace); fill:rgba(20,20,20,.62);
  letter-spacing:.06em; text-transform:uppercase}

/* ── the wedge dial ─────────────────────────────────────────────────────────────────────────
   Tiny, inline, read beside a number — a proportion you glance at rather than read. */
.sig-wd{display:inline-block; width:1.05em; height:1.05em; vertical-align:-.16em}
.sig-wd-bed{fill:rgba(255,255,255,.14)}
.sig-wd path,.sig-wd circle+circle{transition:d var(--sig-t-base) var(--sig-e-out)}

/* ── the decaying wave ──────────────────────────────────────────────────────────────────────
   A signal coming to rest. It breathes very slowly at idle — the only ambient motion in the whole
   language, and it is deliberately almost too slow to notice, because a photo editor that twitches
   is a photo editor you stop trusting. */
.sig-wave{display:block; width:100%; height:auto; overflow:visible}
.sig-wave-line{fill:none; stroke:var(--sig-accent); stroke-width:1.6; stroke-linecap:round;
  filter:drop-shadow(0 0 6px color-mix(in srgb,var(--sig-accent) 45%,transparent))}
.sig-wave-echo{fill:none; stroke:var(--sig-accent); stroke-width:1.2; stroke-linecap:round; opacity:.34}
.sig-wave-rest{stroke:rgba(255,255,255,.5); stroke-width:1}
.sig-wave-mark{fill:var(--sig-accent);
  filter:drop-shadow(0 0 5px color-mix(in srgb,var(--sig-accent) 55%,transparent))}
.sig-wave-line,.sig-wave-echo{animation:sigWaveBreathe 7s var(--sig-e-in-out) infinite}
.sig-wave-echo{animation-delay:-2.4s}
@keyframes sigWaveBreathe{
  0%,100%{transform:scaleY(1)}
  50%{transform:scaleY(.9)}
}
.sig-wave-line,.sig-wave-echo{transform-box:fill-box; transform-origin:center}

/* ── entrance ───────────────────────────────────────────────────────────────────────────────
   Put .sig-enter on a container and its children arrive in sequence rather than all at once.
   --sig-i is the child's index; set it in the markup. This is the app's one first-paint moment. */
.sig-enter > *{animation:sigEnter var(--sig-t-enter) var(--sig-e-out) both;
  animation-delay:calc(var(--sig-i,0) * 60ms)}
@keyframes sigEnter{
  from{opacity:0; transform:translateY(var(--sig-rise))}
  to{opacity:1; transform:none}
}

/* Everything above is suppressed for anyone who has asked for less motion. The instruments still
   render and still read correctly — they simply arrive already at rest. */
@media(prefers-reduced-motion:reduce){
  .sig-dm-changing circle,
  .sig-wave-line,.sig-wave-echo,
  .sig-enter > *{animation:none}
  .sig-rd-tick{transition:none}
}
