/*!
 * Nocturne UI 1.17.0 - soft dark UI kit for media/editor interfaces.
 * Single stylesheet, no build step, no dependencies.
 * Docs: AGENTS.md | Machine index: components.json
 */

/* ============================================================
   NOCTURNE UI · 01 · TOKENS
   Soft dark UI. Everything below is the ONLY source of color,
   radius, spacing, type and motion. Never hardcode a hex value
   in a component: add or reuse a token instead.
   ============================================================ */

:root,
.nc-scope {
  /* ---- surfaces (elevation goes lighter, never black) ---- */
  /* Warm neutral ramp: hue biased a hair toward yellow-green so the dark
     never reads as blue-black. Opaque surfaces are for CONTAINERS. */
  --nc-canvas:        #131312;   /* app background, behind panels */
  --nc-surface-1:     #1a1a18;   /* panels, cards, docks          */
  --nc-surface-2:     #232320;   /* menus, toasts, dialogs        */
  --nc-surface-3:     #2c2c28;   /* hover of an opaque surface    */
  --nc-inset:         #0f0f0e;   /* wells: tracks, inputs, stage  */

  /* Translucent elevation: for CONTROLS, which must adapt to whatever
     surface they land on. One value replaces a whole opaque ramp. */
  --nc-raise:         rgba(255, 255, 255, .055);
  --nc-raise-hover:   rgba(255, 255, 255, .095);
  --nc-raise-press:   rgba(255, 255, 255, .13);

  /* ---- hairlines & translucency (light comes from above) ---- */
  --nc-line:          rgba(255, 255, 255, .07);
  --nc-line-strong:   rgba(255, 255, 255, .14);
  --nc-overlay:       rgba(255, 255, 255, .06);
  --nc-overlay-2:     rgba(255, 255, 255, .11);
  --nc-scrim:         rgba(10, 10, 12, .66);
  --nc-glass:         rgba(28, 28, 33, .62);

  /* ---- text ---- */
  --nc-text:          #f3f3f1;
  --nc-text-2:        #a7a7a1;   /* labels, secondary             */
  /* text-3 is the DIMMEST value that may carry real text. It was #74746e,
     which measured 3.71:1 on --nc-surface-1 and failed AA for the 11px
     eyebrows and metadata that use it. Raised until every surface that
     hosts text clears 4.5:1 — canvas 5.57, surface-1 5.22, surface-2 4.72,
     inset 5.74. Nothing dimmer than this may be used for text; something
     that must look inactive gets opacity: .42, not a darker grey. */
  --nc-text-3:        #8d8d86;   /* metadata, ruler, eyebrows     */
  --nc-text-on-accent:#ffffff;
  --nc-thumb:         #ffffff;   /* slider/switch thumbs, radio dot */

  /* Checkerboard behind anything that can be transparent (a gradient
     preview, a colour with alpha). Two greys, never a real image. */
  --nc-checker-1:     #2a2a27;
  --nc-checker-2:     #191917;

  /* ---- accent (exactly ONE brand hue in the whole UI) ---- */
  --nc-accent:        #6152ec;
  --nc-accent-hover:  #7466f2;
  --nc-accent-press:  #5344d6;
  --nc-accent-soft:   rgba(97, 82, 236, .18);
  --nc-accent-glow:   0 6px 20px rgba(97, 82, 236, .35);
  --nc-ai-gradient:   linear-gradient(135deg, #7b64ff 0%, #6152ec 55%, #4f3fd4 100%);

  /* ---- semantic ---- */
  --nc-audio:         #2f7f68;   /* audio clip fill               */
  --nc-audio-2:       #4bb693;   /* waveform                      */
  --nc-handle:        #f2a33c;   /* trim handles + selected clip  */
  --nc-danger:        #e5484d;
  --nc-danger-soft:   rgba(229, 72, 77, .16);
  --nc-success:       #30a46c;
  --nc-warning:       #f2a33c;

  /* ---- series: categorical DATA colors, not accents ----
     For marks that stand for different THINGS shown at the same time: two
     brands on one calendar, several lines on a chart, tracks of different
     kinds. They carry meaning by identity, not by state, which is why they
     live apart from --nc-accent: the accent stays the single brand hue and
     keeps meaning "this is what you act on". Never style a button, a link or
     a selected state with a series color.
     Six is the cap on purpose. Past six categories the eye stops telling
     hues apart and a label does the job better than a color. */
  --nc-serie-1: #4c9ef0;
  --nc-serie-2: #4bb693;
  --nc-serie-3: #f2a33c;
  --nc-serie-4: #d96fb0;
  --nc-serie-5: #8a7bf0;
  --nc-serie-6: #5fc2c9;

  /* ---- image channels: luminance and R, G, B ----
     For whatever picks or draws one channel of a photo: a histogram, a
     curve, a levels row. Fixed by meaning (red is red), so they are not
     series and never stand for anything else. */
  --nc-channel-luma: #d9d6e2;
  --nc-channel-r:    #e5534b;
  --nc-channel-g:    #57c47a;
  --nc-channel-b:    #4f86f7;

  /* ---- radii (generous + consistent = the whole look) ---- */
  --nc-r-xs:  6px;
  --nc-r-sm:  8px;
  --nc-r-md: 11px;
  --nc-r-lg: 14px;
  --nc-r-xl: 18px;
  --nc-r-2xl:24px;
  --nc-r-pill: 999px;

  /* ---- spacing scale ---- */
  --nc-s-1:  4px;
  --nc-s-2:  6px;
  --nc-s-3:  8px;
  --nc-s-4: 12px;
  --nc-s-5: 16px;
  --nc-s-6: 20px;
  --nc-s-7: 24px;
  --nc-s-8: 32px;

  /* ---- control heights ---- */
  --nc-h-xs: 24px;
  --nc-h-sm: 32px;
  --nc-h-md: 40px;
  --nc-h-lg: 46px;

  /* ---- typography ---- */
  --nc-font: ui-sans-serif, -apple-system, BlinkMacSystemFont, "SF Pro Text",
             "Inter", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --nc-font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
             "Liberation Mono", monospace;
  --nc-fs-1: 11px;   /* micro: badges, ruler   */
  --nc-fs-2: 12px;   /* captions, clip labels  */
  --nc-fs-3: 13px;   /* default control text   */
  /* Controls sit at 450, an intermediate axis value on variable UI faces.
     Non-variable fallbacks round to 400 or 500; both are acceptable. */
  --nc-fw-control: 450;
  --nc-fs-4: 15px;   /* panel titles           */
  --nc-fs-5: 17px;   /* app title              */

  /* ---- depth ---- */
  --nc-shadow-1: 0 1px 0 rgba(255,255,255,.03) inset, 0 6px 18px rgba(0,0,0,.35);
  /* Edge: a bright top canto, a much fainter bottom one, and a hairline ring.
     The asymmetry between top (.16) and bottom (.04) is what the eye reads as
     a gradient; the ring is a 0.5px inset shadow, not a border, so it stays
     crisp on retina and costs nothing in layout. */
  --nc-edge: inset 0  .5px 0 rgba(255,255,255,.16),
             inset 0 -.5px 0 rgba(255,255,255,.04),
             inset 0 0 0 .5px rgba(255,255,255,.16);

  /* On a saturated fill a white bottom canto muddies the color, so it flips
     to black, and the top canto needs nearly twice the strength to register. */
  --nc-edge-accent: inset 0  .5px 0 rgba(255,255,255,.30),
                    inset 0 -.5px 0 rgba(0,0,0,.16),
                    inset 0 0 0 .5px rgba(255,255,255,.14);

  /* Short two-step lift. Two small shadows read crisper than one large one. */
  --nc-lift: 0 2px 2px -1px rgba(0,0,0,.12),
             0 4px 4px -2px rgba(0,0,0,.12);
  --nc-shadow-2: 0 1px 0 rgba(255,255,255,.04) inset, 0 12px 30px rgba(0,0,0,.45);
  --nc-shadow-3: 0 16px 44px rgba(0,0,0,.6);

  /* ---- motion ---- */
  --nc-ease: cubic-bezier(.2, .8, .2, 1);
  --nc-t-fast: 110ms var(--nc-ease);
  --nc-t:      170ms var(--nc-ease);
  --nc-t-slow: 280ms var(--nc-ease);

  /* ---- layout knobs consumed by components ---- */
  --nc-track-head-w: 116px;
  --nc-timeline-gap: 12px;
  --nc-prop-label-w: 84px;
  /* The slider is --nc-slider-hit tall so it can be grabbed, and paints a
     track only --nc-slider-track tall. Raise the hit area for touch-first
     surfaces; the track stays thin either way. */
  --nc-slider-hit: 24px;
  --nc-slider-track: 4px;

  /* ---- z-index ---- */
  --nc-z-clip: 1;
  --nc-z-overlay: 5;
  --nc-z-dock: 20;
  --nc-z-popover: 40;
  --nc-z-modal: 50;
  --nc-z-toast: 55;
  --nc-z-tooltip: 60;
}


/* ============================================================
   NOCTURNE UI · 02 · BASE
   Optional. Skip this file if you are embedding into a host app
   that already sets its own reset; keep 01-tokens.css always.
   ============================================================ */

.nc-scope *,
.nc-scope *::before,
.nc-scope *::after { box-sizing: border-box; }

.nc-body {
  margin: 0;
  background: var(--nc-canvas);
  color: var(--nc-text);
  font-family: var(--nc-font);
  font-size: var(--nc-fs-3);
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

.nc-scope ::selection { background: var(--nc-accent-soft); color: var(--nc-text); }

/* Icon sizing contract: every <svg class="nc-i"> is 16px, stroked. */
.nc-i {
  width: 16px; height: 16px; flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.nc-i--sm { width: 14px; height: 14px; }
.nc-i--lg { width: 20px; height: 20px; }
.nc-i--xl { width: 26px; height: 26px; stroke-width: 1.5; }
.nc-i--solid { fill: currentColor; stroke: none; }

/* Scrollbars: thin, invisible until the surface is hovered. */
.nc-scroll { overflow: auto; scrollbar-width: thin; scrollbar-color: transparent transparent; }
.nc-scroll:hover { scrollbar-color: var(--nc-overlay-2) transparent; }
.nc-scroll::-webkit-scrollbar { width: 10px; height: 10px; }
.nc-scroll::-webkit-scrollbar-track { background: transparent; }
.nc-scroll::-webkit-scrollbar-thumb {
  background: transparent; border: 3px solid transparent;
  border-radius: var(--nc-r-pill); background-clip: content-box;
}
.nc-scroll:hover::-webkit-scrollbar-thumb { background: var(--nc-overlay-2); background-clip: content-box; }

@media (prefers-reduced-motion: reduce) {
  .nc-scope *, .nc-scope *::before, .nc-scope *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}


/* ============================================================
   NOCTURNE UI · 03 · BUTTONS
   Base: .nc-btn  ·  Variants: --primary --ghost --danger --ai
   Sizes: --sm --lg  ·  Shapes: --pill --icon --circle --block
   ============================================================ */

.nc-btn {
  --_bg: var(--nc-raise);
  --_bg-hover: var(--nc-raise-hover);
  --_fg: var(--nc-text);
  --_edge: var(--nc-edge);
  --_lift: var(--nc-lift);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--nc-s-3);
  height: var(--nc-h-md);
  padding: 0 16px;
  border: 0;
  border-radius: var(--nc-r-md);
  background: var(--_bg);
  color: var(--_fg);
  box-shadow: var(--_edge), var(--_lift);
  font-family: var(--nc-font);
  font-size: var(--nc-fs-3);
  font-weight: var(--nc-fw-control);
  line-height: 1;
  letter-spacing: .01em;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  -webkit-appearance: none;
  appearance: none;
  text-decoration: none;
  transition: background var(--nc-t), color var(--nc-t),
              box-shadow var(--nc-t),
              transform var(--nc-t-fast), opacity var(--nc-t);
}
.nc-btn:hover { background: var(--_bg-hover); }
.nc-btn:active { transform: translateY(.5px) scale(.985); }
.nc-btn:focus-visible { outline: 2px solid var(--nc-accent); outline-offset: 2px; }
.nc-btn[disabled],
.nc-btn[aria-disabled="true"] { opacity: .42; pointer-events: none; }

/* --- variants ------------------------------------------------ */
.nc-btn--primary {
  --_bg: var(--nc-accent);
  --_bg-hover: var(--nc-accent-hover);
  --_fg: var(--nc-text-on-accent);
  --_edge: var(--nc-edge-accent);
  --_lift: var(--nc-lift), var(--nc-accent-glow);
}
.nc-btn--primary:active { --_bg: var(--nc-accent-press); }

.nc-btn--ghost {
  --_bg: transparent;
  --_bg-hover: var(--nc-raise-hover);
  --_fg: var(--nc-text-2);
  --_edge: none;
  --_lift: none;
}
.nc-btn--ghost:hover { --_fg: var(--nc-text); --_edge: var(--nc-edge); }

.nc-btn--danger {
  --_bg: transparent;
  --_bg-hover: var(--nc-danger-soft);
  --_fg: var(--nc-danger);
  --_edge: var(--nc-edge);
  --_lift: none;
}
.nc-btn--danger-solid {
  --_bg: var(--nc-danger);
  --_bg-hover: #f0585d;
  --_fg: #fff;
  --_edge: var(--nc-edge-accent);
}

/* AI / generative action. Reserve for one action per screen. */
.nc-btn--ai {
  --_fg: var(--nc-text-on-accent);
  background: var(--nc-ai-gradient);
  box-shadow: var(--nc-edge-accent), var(--nc-lift), var(--nc-accent-glow);
  position: relative;
  overflow: hidden;
}
.nc-btn--ai:hover { background: var(--nc-ai-gradient); filter: brightness(1.1); }
.nc-btn--ai::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(120deg, transparent 30%, rgba(255,255,255,.22) 50%, transparent 70%);
  transform: translateX(-120%);
  transition: transform var(--nc-t-slow);
}
.nc-btn--ai:hover::after { transform: translateX(120%); }

/* Selected / toggled-on state for any variant. */
.nc-btn.is-active,
.nc-btn[aria-pressed="true"] {
  --_bg: var(--nc-accent);
  --_bg-hover: var(--nc-accent-hover);
  --_fg: var(--nc-text-on-accent);
  --_edge: var(--nc-edge-accent);
}

/* --- sizes --------------------------------------------------- */
.nc-btn--sm { height: var(--nc-h-sm); padding: 0 12px; font-size: var(--nc-fs-2); gap: var(--nc-s-2); border-radius: var(--nc-r-sm); }
.nc-btn--lg { height: var(--nc-h-lg); padding: 0 22px; gap: 10px; font-size: var(--nc-fs-3); border-radius: var(--nc-r-lg); }

/* --- shapes -------------------------------------------------- */
.nc-btn--pill   { border-radius: var(--nc-r-pill); }
.nc-btn--icon   { width: var(--nc-h-md); padding: 0; }
.nc-btn--icon.nc-btn--sm { width: var(--nc-h-sm); }
.nc-btn--icon.nc-btn--lg { width: var(--nc-h-lg); }
.nc-btn--circle { border-radius: var(--nc-r-pill); width: var(--nc-h-md); padding: 0; }

/* --- .nc-fab: la acción principal, flotando, en un teléfono ------------------
 * Arriba del corte móvil es un botón normal dentro de su barra. Debajo, la
 * barra no tiene sitio y la acción se va a donde llega el pulgar. Se le pone
 * la clase siempre: es el ancho de pantalla el que decide, no el marcado. */
@media (max-width: 860px) {
  .nc-fab {
    position: fixed;
    right: max(var(--nc-s-5), env(safe-area-inset-right));
    bottom: max(var(--nc-s-5), env(safe-area-inset-bottom));
    z-index: var(--nc-z-dock);
    width: 56px; height: 56px; padding: 0;
    border-radius: var(--nc-r-pill);
    box-shadow: var(--nc-edge-accent), var(--nc-shadow-3), var(--nc-accent-glow);
  }
  .nc-fab .nc-i { width: 22px; height: 22px; }
}
.nc-btn--circle.nc-btn--sm { width: var(--nc-h-sm); }
.nc-btn--circle.nc-btn--lg { width: var(--nc-h-lg); }
.nc-btn--block  { display: flex; width: 100%; }

/* Trailing chevron/meta inside a button */
.nc-btn__meta { color: var(--nc-text-3); font-weight: 400; }

/* --- attached group ------------------------------------------ */
.nc-btn-group { display: inline-flex; }
.nc-btn-group > .nc-btn { border-radius: 0; margin-left: -1px; }
.nc-btn-group > .nc-btn:first-child { border-radius: var(--nc-r-md) 0 0 var(--nc-r-md); margin-left: 0; }
.nc-btn-group > .nc-btn:last-child  { border-radius: 0 var(--nc-r-md) var(--nc-r-md) 0; }
.nc-btn-group > .nc-btn:hover,
.nc-btn-group > .nc-btn:focus-visible { z-index: 1; }

/* --- rail: a cluster of circular icon buttons ----------------- */
.nc-icon-rail { display: inline-flex; align-items: center; gap: var(--nc-s-3); }
.nc-icon-rail--tight { gap: var(--nc-s-1); }
.nc-icon-rail--vertical { flex-direction: column; }


/* ============================================================
   NOCTURNE UI · 04 · CONTROLS
   Segmented control, floating dock, toolbar, select, input,
   slider, switch, menu, tooltip.
   ============================================================ */

/* --- segmented control (the Cut/Edit/Color/Sound pattern) ----- */
.nc-segmented {
  display: inline-flex;
  align-items: center;
  gap: var(--nc-s-1);
  padding: 5px;
  border: 0;
  border-radius: var(--nc-r-lg);
  background: var(--nc-raise);
  box-shadow: var(--nc-edge), var(--nc-lift);
}
.nc-segmented__item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--nc-s-3);
  height: 36px;
  padding: 0 18px;
  border: 0;
  border-radius: var(--nc-r-md);
  background: transparent;
  color: var(--nc-text-2);
  font-family: var(--nc-font);
  font-size: var(--nc-fs-3);
  font-weight: var(--nc-fw-control);
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--nc-t), color var(--nc-t);
}
.nc-segmented__item:hover { background: var(--nc-raise-hover); color: var(--nc-text); }
.nc-segmented__item:focus-visible { outline: 2px solid var(--nc-accent); outline-offset: -2px; }
.nc-segmented__item[aria-pressed="true"],
.nc-segmented__item[aria-selected="true"],
.nc-segmented__item.is-active {
  background: var(--nc-accent);
  color: var(--nc-text-on-accent);
  box-shadow: var(--nc-edge-accent), 0 2px 10px rgba(97, 82, 236, .3);
}
.nc-segmented--pill { border-radius: var(--nc-r-pill); }
.nc-segmented--pill .nc-segmented__item { border-radius: var(--nc-r-pill); }
.nc-segmented--sm .nc-segmented__item { height: 28px; padding: 0 12px; font-size: var(--nc-fs-2); }

/* --- floating dock: an island that hovers over content -------- */
.nc-dock {
  position: absolute;
  left: 50%;
  bottom: var(--nc-s-5);
  transform: translateX(-50%);
  z-index: var(--nc-z-dock);
}
.nc-dock--top { bottom: auto; top: var(--nc-s-5); }
.nc-dock--fixed { position: fixed; }

/* --- .nc-tabbar: the app's navigation, at the bottom, on a phone ----------
 * Four or five destinations do not fit across the top of a 390px screen: the
 * last one gets clipped and nothing says the strip scrolls. Under the mobile
 * breakpoint the same buttons become an edge-to-edge bar at the bottom, icon
 * over label, where the thumb already is.
 *
 * Put it on the SAME element as the .nc-segmented it replaces: above the
 * breakpoint nothing here applies and the segmented control stands. Each item
 * needs an icon, because at this size the label alone is too small to aim at. */
@media (max-width: 860px) {
  .nc-tabbar.nc-tabbar {
    position: fixed; inset: auto 0 0 0;
    width: auto; margin: 0; max-width: none;
    z-index: var(--nc-z-dock);
    display: flex;
    gap: 0;
    padding: var(--nc-s-2) 0 max(var(--nc-s-2), env(safe-area-inset-bottom));
    border-radius: 0;
    background: var(--nc-surface-1);
    box-shadow: inset 0 1px 0 var(--nc-line), var(--nc-shadow-3);
  }
  .nc-tabbar.nc-tabbar > .nc-segmented__item,
  .nc-tabbar__item {
    position: relative;
    flex: 1 1 0; min-width: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 3px;
    height: auto; min-height: 48px; padding: var(--nc-s-2) 0;
    border-radius: var(--nc-r-md);
    font-size: var(--nc-fs-1); font-weight: 600; letter-spacing: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  /* En una barra inferior el activo se marca con color, no con relleno: el
   * relleno de píldora a este tamaño tapa el icono y pesa más que el contenido. */
  .nc-tabbar.nc-tabbar > .nc-segmented__item[aria-selected="true"],
  .nc-tabbar__item[aria-selected="true"] {
    background: none; color: var(--nc-accent); box-shadow: none;
  }
  .nc-tabbar.nc-tabbar .nc-i { width: 20px; height: 20px; }
  .nc-tabbar.nc-tabbar .nc-badge { position: absolute; top: 2px; right: 22%; }
}

/* --- toolbar: a horizontal band of controls ------------------- */
.nc-toolbar {
  display: flex;
  align-items: center;
  gap: var(--nc-s-3);
  min-width: 0;
}
.nc-toolbar__group { display: inline-flex; align-items: center; gap: var(--nc-s-3); min-width: 0; }
.nc-toolbar__spacer { flex: 1 1 auto; }
.nc-toolbar__divider { width: 1px; height: 20px; background: var(--nc-line-strong); flex: none; }

/* --- select trigger (opens a menu; renders like a ghost btn) --- */
.nc-select {
  display: inline-flex;
  align-items: center;
  gap: var(--nc-s-2);
  height: var(--nc-h-sm);
  padding: 0 10px;
  border: 1px solid transparent;
  border-radius: var(--nc-r-sm);
  background: transparent;
  color: var(--nc-text-2);
  font-family: var(--nc-font);
  font-size: var(--nc-fs-3);
  font-weight: 500;
  cursor: pointer;
  transition: background var(--nc-t), color var(--nc-t), border-color var(--nc-t);
}
.nc-select:hover { background: var(--nc-raise); color: var(--nc-text); }
.nc-select:focus-visible { outline: 2px solid var(--nc-accent); outline-offset: 2px; }
.nc-select[aria-expanded="true"] { background: var(--nc-raise-hover); color: var(--nc-text); }
.nc-select--bordered { border-color: transparent; background: var(--nc-raise); box-shadow: var(--nc-edge); }
.nc-select__caret { color: var(--nc-text-3); }

/* --- text field ---------------------------------------------- */
.nc-field { display: flex; flex-direction: column; gap: var(--nc-s-2); min-width: 0; }
.nc-label { font-size: var(--nc-fs-2); font-weight: 500; color: var(--nc-text-2); }
.nc-input {
  display: flex;
  align-items: center;
  gap: var(--nc-s-3);
  height: var(--nc-h-md);
  padding: 0 12px;
  border: 1px solid var(--nc-line);
  border-radius: var(--nc-r-md);
  background: var(--nc-inset);
  color: var(--nc-text);
  transition: border-color var(--nc-t), box-shadow var(--nc-t);
}
.nc-input > .nc-i { color: var(--nc-text-3); }
.nc-input input,
.nc-input textarea {
  all: unset;
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--nc-font);
  font-size: var(--nc-fs-3);
  color: inherit;
}
.nc-input input::placeholder, .nc-input textarea::placeholder { color: var(--nc-text-3); }
.nc-input:focus-within { border-color: var(--nc-accent); box-shadow: 0 0 0 3px var(--nc-accent-soft); }
.nc-input--sm { height: var(--nc-h-sm); border-radius: var(--nc-r-sm); }
/* Native <select> inside the same box: the caret is drawn by the wrapper. */
.nc-input select {
  all: unset; flex: 1 1 auto; min-width: 0; padding-right: 18px;
  align-self: stretch; display: flex; align-items: center;
  font-family: var(--nc-font); font-size: var(--nc-fs-3); color: inherit; cursor: pointer;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239a9ab0' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right center / 14px;
}
.nc-input select option { background: var(--nc-surface-2); color: var(--nc-text); }

/* Deshabilitado se ve deshabilitado, igual que en el resto del kit. */
.nc-input:has(input:disabled),
.nc-input:has(select:disabled),
.nc-input:has(textarea:disabled) { opacity: .42; pointer-events: none; }
/* Multi-line: the box grows with the textarea. */
.nc-input--area { height: auto; min-height: var(--nc-h-lg); padding: 10px 12px; align-items: stretch; }
.nc-input--area textarea { resize: vertical; min-height: 60px; line-height: 1.45; }
.nc-input--block { width: 100%; }

/* --- slider ---------------------------------------------------
   The box is --nc-slider-hit tall (24px) so a finger or a trackpad
   can catch it; the painted track stays --nc-slider-track (4px)
   because the background is clipped to the content box and the rest
   of the height is padding. Growing the grab area WITHOUT growing
   the track is the whole point: the old 4px box was 4px of target.
   ---------------------------------------------------------------- */
.nc-slider {
  -webkit-appearance: none; appearance: none;
  box-sizing: border-box;
  display: block;
  width: 100%;
  height: var(--nc-slider-hit, 24px);
  padding: calc((var(--nc-slider-hit, 24px) - var(--nc-slider-track, 4px)) / 2) 0;
  margin: 0;
  /* A pill radius on the 24px box made the painted 4px band end in 12x2 px
     ellipses (inner radius = outer minus padding, per axis): pointed tips,
     not round. Half the track across, half the box down leaves an exact
     pill on the band. */
  border-radius: calc(var(--nc-slider-track, 4px) / 2) / calc(var(--nc-slider-hit, 24px) / 2);
  background-color: transparent;
  /* The accent marks only what moved: from the rest value (--nc-slider-zero,
     the left end unless the app says otherwise) to the thumb. At rest the
     track carries no colour; a panel full of sliders is not a wall of accent. */
  background-image: linear-gradient(to right,
    var(--nc-overlay) 0,
    var(--nc-overlay) min(var(--nc-value, 0%), var(--nc-slider-zero, 0%)),
    var(--nc-accent)  min(var(--nc-value, 0%), var(--nc-slider-zero, 0%)),
    var(--nc-accent)  max(var(--nc-value, 0%), var(--nc-slider-zero, 0%)),
    var(--nc-overlay) max(var(--nc-value, 0%), var(--nc-slider-zero, 0%)));
  background-repeat: no-repeat;
  background-clip: content-box;
  cursor: pointer;
}
.nc-slider::-webkit-slider-runnable-track { height: var(--nc-slider-track, 4px); background: transparent; border-radius: var(--nc-r-pill); }
.nc-slider::-moz-range-track { height: var(--nc-slider-track, 4px); background: transparent; border-radius: var(--nc-r-pill); }
.nc-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 14px; height: 14px; margin-top: calc((var(--nc-slider-track, 4px) - 14px) / 2);
  border-radius: var(--nc-r-pill);
  background: var(--nc-thumb); border: 0; box-shadow: 0 2px 6px rgba(0,0,0,.55);
  cursor: grab;
  transition: box-shadow var(--nc-t-fast);
}
.nc-slider::-moz-range-thumb {
  width: 14px; height: 14px; border-radius: var(--nc-r-pill);
  background: var(--nc-thumb); border: 0; box-shadow: 0 2px 6px rgba(0,0,0,.55);
  cursor: grab;
}
.nc-slider:hover::-webkit-slider-thumb { box-shadow: 0 2px 6px rgba(0,0,0,.55), 0 0 0 4px var(--nc-accent-soft); }
.nc-slider:hover::-moz-range-thumb    { box-shadow: 0 2px 6px rgba(0,0,0,.55), 0 0 0 4px var(--nc-accent-soft); }
.nc-slider:active::-webkit-slider-thumb { cursor: grabbing; box-shadow: 0 2px 6px rgba(0,0,0,.55), 0 0 0 6px var(--nc-accent-soft); }
.nc-slider:active::-moz-range-thumb    { cursor: grabbing; box-shadow: 0 2px 6px rgba(0,0,0,.55), 0 0 0 6px var(--nc-accent-soft); }
.nc-slider:focus-visible { outline: 2px solid var(--nc-accent); outline-offset: 0; }
.nc-slider:disabled { opacity: .42; cursor: not-allowed; }

/* Bipolar: -100…+100 with the neutral at 0. The fill grows OUT OF THE
   CENTRE, so at rest the track is empty instead of looking half full,
   and a tick marks the neutral. --nc-value is still a 0–100% position:
   ((value - min) / (max - min)) * 100. Layer 1 is the centre tick (it
   clips to the border box so it pokes out of the thin track), layer 2
   is the fill, and the tick is taller than the 14px thumb on purpose so
   the neutral stays marked even when the thumb is parked on top of it. */
/* The neutral sits at --nc-slider-zero (50% by default). A control whose rest
   value is not the middle of its range (scale 30…300 at 100, brightness 0…200
   at 100 is the middle, but 0…400 at 100 is not) sets it inline, and the fill
   and the tick grow out of there. */
.nc-slider--bipolar {
  background-image:
    linear-gradient(var(--nc-text-3), var(--nc-text-3)),
    linear-gradient(to right,
      var(--nc-overlay) 0,
      var(--nc-overlay) min(var(--nc-value, 50%), var(--nc-slider-zero, 50%)),
      var(--nc-accent)  min(var(--nc-value, 50%), var(--nc-slider-zero, 50%)),
      var(--nc-accent)  max(var(--nc-value, 50%), var(--nc-slider-zero, 50%)),
      var(--nc-overlay) max(var(--nc-value, 50%), var(--nc-slider-zero, 50%)));
  background-size: 1px 18px, auto;
  background-position: var(--nc-slider-zero, 50%) center, 0 0;
  background-clip: border-box, content-box;
}

/* Marked: a plain 0…max amount whose default is not the left end (an
   intensity that starts at 75%, an opacity at 100%). The fill stays the
   classic one, left edge to thumb, so it reads as "how much"; a tick at
   --nc-slider-mark shows where the default sits, so the person sees what
   they moved away from. Never fill from the default: a bar floating in the
   middle of the track, starting at an unmarked point, does not read. */
.nc-slider--marca {
  background-image:
    linear-gradient(var(--nc-text-3), var(--nc-text-3)),
    linear-gradient(to right, var(--nc-accent) var(--nc-value, 0%), var(--nc-overlay) var(--nc-value, 0%));
  background-size: 1px 12px, auto;
  background-position: var(--nc-slider-mark, 100%) center, 0 0;
  background-clip: border-box, content-box;
}

/* --- switch --------------------------------------------------- */
.nc-switch {
  position: relative;
  display: inline-flex; align-items: center;
  width: 38px; height: 22px; flex: none;
  border-radius: var(--nc-r-pill);
  background: var(--nc-overlay-2);
  cursor: pointer;
  transition: background var(--nc-t);
}
.nc-switch input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.nc-switch__thumb {
  width: 16px; height: 16px; margin-left: 3px;
  border-radius: var(--nc-r-pill);
  background: var(--nc-thumb);
  box-shadow: 0 1px 3px rgba(0,0,0,.5);
  transition: transform var(--nc-t);
}
.nc-switch:has(input:checked) { background: var(--nc-accent); }
.nc-switch:has(input:checked) .nc-switch__thumb { transform: translateX(16px); }
.nc-switch:has(input:focus-visible) { outline: 2px solid var(--nc-accent); outline-offset: 2px; }

/* --- menu / popover ------------------------------------------ */
.nc-menu {
  min-width: 190px;
  padding: var(--nc-s-2);
  border: 1px solid var(--nc-line-strong);
  border-radius: var(--nc-r-lg);
  background: var(--nc-surface-2);
  box-shadow: var(--nc-shadow-3);
  z-index: var(--nc-z-popover);
}
.nc-menu__item {
  display: flex; align-items: center; gap: var(--nc-s-3);
  width: 100%; height: 32px; padding: 0 10px;
  border: 0; border-radius: var(--nc-r-sm);
  background: transparent;
  color: var(--nc-text-2);
  font-family: var(--nc-font); font-size: var(--nc-fs-3); font-weight: 500;
  text-align: left; cursor: pointer;
  transition: background var(--nc-t), color var(--nc-t);
}
.nc-menu__item:hover { background: var(--nc-raise-hover); color: var(--nc-text); }
.nc-menu__item[aria-checked="true"] { color: var(--nc-text); }
.nc-menu__item--danger { color: var(--nc-danger); }
.nc-menu__item--danger:hover { background: var(--nc-danger-soft); color: var(--nc-danger); }
.nc-menu__shortcut { margin-left: auto; color: var(--nc-text-3); font-family: var(--nc-font-mono); font-size: var(--nc-fs-1); }
.nc-menu__sep { height: 1px; margin: var(--nc-s-2) var(--nc-s-1); background: var(--nc-line); }

/* --- tooltip (attribute-driven, no JS) ------------------------ */
[data-nc-tip] { position: relative; }
[data-nc-tip]::after {
  content: attr(data-nc-tip);
  position: absolute; bottom: calc(100% + 8px); left: 50%;
  transform: translateX(-50%) translateY(4px);
  padding: 5px 9px;
  border: 1px solid var(--nc-line-strong);
  border-radius: var(--nc-r-sm);
  background: var(--nc-surface-3);
  color: var(--nc-text);
  font-size: var(--nc-fs-1); font-weight: 500; white-space: nowrap;
  box-shadow: var(--nc-shadow-2);
  opacity: 0; pointer-events: none;
  transition: opacity var(--nc-t), transform var(--nc-t);
  z-index: var(--nc-z-tooltip);
}
[data-nc-tip]:hover::after,
[data-nc-tip]:focus-visible::after { opacity: 1; transform: translateX(-50%) translateY(0); }
/* A control flush with a panel edge: the centred bubble pokes out of the
   panel, and even invisible it widens the panel's scroll area, so a stray
   trackpad swipe slides the whole panel sideways. Anchor it to that edge. */
[data-nc-tip-pos="end"]::after   { left: auto; right: 0; transform: translateY(4px); }
[data-nc-tip-pos="start"]::after { left: 0; transform: translateY(4px); }
[data-nc-tip-pos="end"]:hover::after, [data-nc-tip-pos="end"]:focus-visible::after,
[data-nc-tip-pos="start"]:hover::after, [data-nc-tip-pos="start"]:focus-visible::after { transform: translateY(0); }


/* ============================================================
   NOCTURNE UI · 05 · SURFACES
   App shell, app bar, panel, badge, chip, status dot, glass HUD.
   ============================================================ */

/* --- app shell ------------------------------------------------ */
.nc-app {
  display: flex; flex-direction: column;
  min-height: 100%;
  padding: var(--nc-s-4);
  gap: var(--nc-s-4);
  background: var(--nc-canvas);
  color: var(--nc-text);
  font-family: var(--nc-font);
}
.nc-app--framed {
  border: 1px solid var(--nc-line);
  border-radius: var(--nc-r-2xl);
  box-shadow: var(--nc-shadow-3);
  overflow: hidden;
}

/* Two-column workspace: browser + preview, then a full-width row. */
.nc-workspace {
  display: grid;
  grid-template-columns: minmax(260px, 1fr) minmax(0, 1.75fr);
  gap: var(--nc-s-4);
  min-height: 0;
}
@media (max-width: 860px) {
  .nc-workspace { grid-template-columns: minmax(0, 1fr); }
}

/* --- app bar -------------------------------------------------- */
.nc-appbar {
  display: flex; align-items: center; gap: var(--nc-s-4);
  padding: var(--nc-s-2) var(--nc-s-3);
  min-height: 52px;
}
.nc-appbar__lead,
.nc-appbar__trail { display: flex; align-items: center; gap: var(--nc-s-3); flex: 1 1 0; min-width: 0; }
.nc-appbar__trail { justify-content: flex-end; }
.nc-appbar__center { display: flex; align-items: center; gap: var(--nc-s-1); min-width: 0; }
.nc-appbar__title {
  display: inline-flex; align-items: center; gap: var(--nc-s-3);
  font-size: var(--nc-fs-5); font-weight: 600; letter-spacing: -.01em;
  color: var(--nc-text); white-space: nowrap;
}
.nc-appbar__title b { font-weight: 600; }
.nc-appbar__title span { color: var(--nc-text-2); font-weight: 500; }

/* --- panel ---------------------------------------------------- */
.nc-panel {
  display: flex; flex-direction: column; min-width: 0; min-height: 0;
  border: 0;
  border-radius: var(--nc-r-xl);
  background: var(--nc-surface-1);
  box-shadow: var(--nc-edge), var(--nc-shadow-1);
}
.nc-panel--flat { box-shadow: none; }
.nc-panel--inset { background: var(--nc-inset); }
.nc-panel__header {
  display: flex; align-items: center; gap: var(--nc-s-3);
  padding: var(--nc-s-5) var(--nc-s-5) var(--nc-s-4);
}
.nc-panel__title {
  display: inline-flex; align-items: center; gap: var(--nc-s-3);
  margin: 0;
  font-size: var(--nc-fs-4); font-weight: 600; letter-spacing: -.01em;
  color: var(--nc-text);
}
.nc-panel__actions { display: inline-flex; align-items: center; gap: var(--nc-s-1); margin-left: auto; }
.nc-panel__body { flex: 1 1 auto; min-height: 0; padding: 0 var(--nc-s-5) var(--nc-s-4); }
.nc-panel__body--flush { padding: 0; }
.nc-panel__footer {
  display: flex; align-items: center; gap: var(--nc-s-4);
  padding: var(--nc-s-3) var(--nc-s-5) var(--nc-s-5);
  color: var(--nc-text-3); font-size: var(--nc-fs-2);
}

/* --- badge (static label) ------------------------------------- */
.nc-badge {
  display: inline-flex; align-items: center; gap: 5px;
  height: 20px; padding: 0 7px;
  border-radius: var(--nc-r-xs);
  background: var(--nc-overlay-2);
  color: var(--nc-text);
  font-size: var(--nc-fs-1); font-weight: 600; line-height: 1;
}
.nc-badge--accent  { background: var(--nc-accent); color: var(--nc-text-on-accent); }
.nc-badge--muted   { background: var(--nc-overlay); color: var(--nc-text-2); }
.nc-badge--danger  { background: var(--nc-danger-soft); color: var(--nc-danger); }
.nc-badge--time    { background: var(--nc-scrim); font-family: var(--nc-font-mono); font-weight: 500; backdrop-filter: blur(6px); }
.nc-badge--pill    { border-radius: var(--nc-r-pill); padding: 0 9px; }

/* --- chip (interactive filter/tag) ---------------------------- */
.nc-chip {
  display: inline-flex; align-items: center; gap: var(--nc-s-2);
  height: 28px; padding: 0 12px;
  border: 0;
  border-radius: var(--nc-r-pill);
  background: var(--nc-raise);
  box-shadow: var(--nc-edge);
  color: var(--nc-text-2);
  font-family: var(--nc-font); font-size: var(--nc-fs-2); font-weight: 500;
  cursor: pointer;
  transition: background var(--nc-t), color var(--nc-t), border-color var(--nc-t);
}
.nc-chip:hover { background: var(--nc-raise-hover); color: var(--nc-text); }
.nc-chip[aria-pressed="true"],
.nc-chip.is-active { background: var(--nc-accent); color: var(--nc-text-on-accent); box-shadow: var(--nc-edge-accent); }

/* --- status dot ----------------------------------------------- */
.nc-dot { width: 7px; height: 7px; border-radius: var(--nc-r-pill); background: var(--nc-text-3); flex: none; }
.nc-dot--live    { background: var(--nc-success); box-shadow: 0 0 0 3px rgba(48,163,108,.18); }
.nc-dot--accent  { background: var(--nc-accent); }
.nc-dot--danger  { background: var(--nc-danger); }

/* --- glass HUD (floating pill over media) --------------------- */
.nc-hud {
  display: inline-flex; align-items: center; gap: 2px;
  padding: 5px;
  border: 1px solid var(--nc-line-strong);
  border-radius: var(--nc-r-pill);
  background: var(--nc-glass);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  backdrop-filter: blur(18px) saturate(1.4);
  box-shadow: var(--nc-shadow-2);
}
.nc-hud__item {
  display: inline-flex; align-items: center; gap: var(--nc-s-3);
  height: 32px; padding: 0 15px;
  border: 0; border-radius: var(--nc-r-pill);
  background: transparent;
  color: var(--nc-text);
  font-family: var(--nc-font); font-size: var(--nc-fs-2); font-weight: 500; line-height: 1;
  white-space: nowrap; cursor: pointer;
  transition: background var(--nc-t), color var(--nc-t);
}
.nc-hud__item:hover { background: var(--nc-raise-hover); }
.nc-hud__item--accent { background: var(--nc-accent); color: var(--nc-text-on-accent); box-shadow: var(--nc-edge-accent); }
.nc-hud__item--accent:hover { background: var(--nc-accent-hover); }
.nc-hud__item--muted { color: var(--nc-text-2); }
.nc-hud__sep { width: 1px; height: 18px; margin: 0 4px; background: var(--nc-line-strong); flex: none; }

/* Reusable glass surface for anything floating over imagery. */
.nc-glass {
  background: var(--nc-glass);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  backdrop-filter: blur(18px) saturate(1.4);
  border: 1px solid var(--nc-line-strong);
}


/* ============================================================
   NOCTURNE UI · 06 · MEDIA
   Media grid + card, thumbnails, video player.
   ============================================================ */

/* --- media grid ----------------------------------------------- */
.nc-media-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: var(--nc-s-4) var(--nc-s-4);
}
.nc-media-grid--lg { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }

/* --- media card ----------------------------------------------- */
.nc-media-card {
  display: flex; flex-direction: column; gap: var(--nc-s-3);
  min-width: 0;
  border: 0; padding: 0; background: none;
  text-align: center; cursor: pointer;
  font-family: var(--nc-font);
}
.nc-media-card__thumb {
  position: relative;
  aspect-ratio: 16 / 10;
  border: 1px solid var(--nc-line);
  border-radius: var(--nc-r-md);
  background: var(--nc-inset);
  overflow: hidden;
  transition: outline-color var(--nc-t), transform var(--nc-t-fast);
  outline: 2px solid transparent;
  outline-offset: 2px;
}
.nc-media-card__thumb > img,
.nc-media-card__thumb > video {
  display: block; width: 100%; height: 100%; object-fit: cover;
}
.nc-media-card:hover .nc-media-card__thumb { transform: translateY(-1px); outline-color: var(--nc-line-strong); }
.nc-media-card:focus-visible { outline: none; }
.nc-media-card:focus-visible .nc-media-card__thumb { outline-color: var(--nc-accent); }
.nc-media-card.is-selected .nc-media-card__thumb,
.nc-media-card[aria-selected="true"] .nc-media-card__thumb { outline-color: var(--nc-accent); }

.nc-media-card__type {
  position: absolute; left: 8px; bottom: 8px;
  color: #fff; opacity: .92;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.6));
}
.nc-media-card__badge { position: absolute; right: 8px; bottom: 8px; }
.nc-media-card__label {
  font-size: var(--nc-fs-2); font-weight: 500; color: var(--nc-text-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nc-media-card.is-selected .nc-media-card__label { color: var(--nc-text); }

/* Placeholder thumbnails: use when there is no real asset yet. */
.nc-thumb-ph { display: block; width: 100%; height: 100%; }
.nc-thumb-ph--1 { background: linear-gradient(135deg,#3a3f52,#1a1c26 60%,#2b2233); }
.nc-thumb-ph--2 { background: linear-gradient(135deg,#ff7a45,#c2417a 45%,#4a2b8f); }
.nc-thumb-ph--3 { background: linear-gradient(135deg,#243b55,#141e30); }
.nc-thumb-ph--4 { background: linear-gradient(135deg,#2f4f4f,#1f3a3a 55%,#16232b); }
.nc-thumb-ph--5 { background: linear-gradient(135deg,#5b4bdb,#2a2350 70%); }
.nc-thumb-ph--6 { background: linear-gradient(135deg,#7a6650,#332a22); }

/* --- player --------------------------------------------------- */
.nc-player { display: flex; flex-direction: column; gap: var(--nc-s-4); min-width: 0; }
.nc-player__stage {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--nc-r-lg);
  background: var(--nc-inset);
  overflow: hidden;
}
.nc-player__stage > img,
.nc-player__stage > video { display: block; width: 100%; height: 100%; object-fit: cover; }

.nc-player__play {
  position: absolute; inset: 0; margin: auto;
  width: 68px; height: 68px;
  display: grid; place-items: center;
  border: 1px solid rgba(255,255,255,.4);
  border-radius: var(--nc-r-pill);
  background: rgba(255,255,255,.22);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  color: #fff; cursor: pointer;
  transition: background var(--nc-t), transform var(--nc-t-fast);
}
.nc-player__play:hover { background: rgba(255,255,255,.32); transform: scale(1.04); }
.nc-player__play:active { transform: scale(.97); }
.nc-player__play:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }

/* Slot for a floating HUD anchored to the bottom of the stage. */
.nc-player__overlay {
  position: absolute; left: 50%; bottom: var(--nc-s-5);
  transform: translateX(-50%);
  z-index: var(--nc-z-overlay);
}

.nc-player__bar {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  gap: var(--nc-s-4);
}
.nc-player__time {
  font-family: var(--nc-font-mono); font-size: var(--nc-fs-2); font-weight: 500;
  color: var(--nc-text-2); font-variant-numeric: tabular-nums;
}
.nc-player__transport { display: inline-flex; align-items: center; gap: var(--nc-s-4); justify-self: center; }
.nc-player__bar > *:last-child { justify-self: end; }


/* ============================================================
   NOCTURNE UI · 07 · TIMELINE
   Grid: [track head | lane]. Rows use `display:contents` so
   heads and lanes stay aligned without fixed row heights.
   ============================================================ */

.nc-timeline {
  position: relative;
  display: grid;
  grid-template-columns: var(--nc-track-head-w) minmax(0, 1fr);
  column-gap: var(--nc-timeline-gap);
  row-gap: var(--nc-s-3);
  align-items: center;
  min-width: 0;
}
.nc-timeline__row { display: contents; }
.nc-timeline__ruler-slot { grid-column: 2; }

/* Full-height overlay pinned to the lane column: hosts the playhead.
   Absolute, not a grid item: `grid-row: 1 / -1` cannot span implicit rows. */
.nc-timeline__overlay {
  position: absolute;
  top: 0; bottom: 0; right: 0;
  left: calc(var(--nc-track-head-w) + var(--nc-timeline-gap));
  pointer-events: none;
  z-index: var(--nc-z-overlay);
}

/* --- ruler ---------------------------------------------------- */
.nc-ruler {
  position: relative;
  display: flex;
  height: 22px;
  border-bottom: 1px solid var(--nc-line);
  color: var(--nc-text-3);
  font-family: var(--nc-font-mono);
  font-size: var(--nc-fs-1);
  font-variant-numeric: tabular-nums;
  user-select: none;
}
.nc-ruler__mark {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  padding-left: 6px;
  padding-top: 2px;
  border-left: 1px solid var(--nc-line-strong);
  white-space: nowrap;
}
.nc-ruler__mark::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 5px;
  background-image: repeating-linear-gradient(90deg, var(--nc-line) 0 1px, transparent 1px var(--nc-ruler-step, 20%));
}

/* --- track head ----------------------------------------------- */
.nc-track-head {
  display: inline-flex; align-items: center; justify-content: center; gap: 2px;
  align-self: center; justify-self: start;
  height: 30px; padding: 0 4px;
  border: 0;
  border-radius: var(--nc-r-md);
  background: var(--nc-raise);
  box-shadow: var(--nc-edge);
}
.nc-track-toggle {
  display: inline-grid; place-items: center;
  width: 24px; height: 24px;
  border: 0; border-radius: var(--nc-r-xs);
  background: transparent;
  color: var(--nc-text-2);
  cursor: pointer;
  transition: background var(--nc-t), color var(--nc-t), opacity var(--nc-t);
}
.nc-track-toggle:hover { background: var(--nc-raise-hover); color: var(--nc-text); }
.nc-track-toggle:focus-visible { outline: 2px solid var(--nc-accent); outline-offset: -2px; }
.nc-track-toggle[aria-pressed="true"] { color: var(--nc-text); }
.nc-track-toggle.is-off { opacity: .4; }

/* --- track (lane) --------------------------------------------- */
.nc-track {
  --nc-track-h: 52px;
  position: relative;
  display: flex; align-items: stretch; gap: 0;
  height: var(--nc-track-h);
  border-radius: var(--nc-r-sm);
  background: var(--nc-inset);
  overflow: hidden;
  min-width: 0;
}
.nc-track--text  { --nc-track-h: 34px; }
.nc-track--video { --nc-track-h: 68px; }
.nc-track--audio { --nc-track-h: 44px; }

/* --- clip ----------------------------------------------------- */
.nc-clip {
  --nc-clip-w: 25%;
  position: relative;
  flex: 0 0 var(--nc-clip-w);
  display: flex; align-items: center; gap: var(--nc-s-2);
  min-width: 0;
  padding: 0 10px;
  border: 0;
  border-radius: var(--nc-r-sm);
  background: var(--nc-surface-2);
  color: #fff;
  font-family: var(--nc-font); font-size: var(--nc-fs-2); font-weight: var(--nc-fw-control);
  text-align: left;
  overflow: hidden;
  cursor: grab;
  outline: 2px solid transparent; outline-offset: -2px;
  transition: outline-color var(--nc-t), filter var(--nc-t);
}
.nc-clip:hover { filter: brightness(1.12); outline-color: var(--nc-line-strong); }
.nc-clip:active { cursor: grabbing; filter: brightness(1.04); }
.nc-clip:focus-visible { outline-color: #fff; }
.nc-clip.is-selected,
.nc-clip[aria-selected="true"],
.nc-clip.is-selected:hover,
.nc-clip[aria-selected="true"]:hover { outline-color: var(--nc-handle); }

.nc-clip--text  { background: var(--nc-accent); }
.nc-clip--audio { background: var(--nc-audio); }
.nc-clip--video { padding: 0; background: var(--nc-inset); }

.nc-clip__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nc-clip__strip { display: flex; width: 100%; height: 100%; }
.nc-clip__strip > * { flex: 1 1 0; min-width: 0; height: 100%; object-fit: cover; }

/* Trim handles: sit flush at the clip edges. */
.nc-clip__handle {
  position: absolute; top: 0; bottom: 0; width: 6px;
  background: var(--nc-handle);
  border-radius: 3px;
  cursor: ew-resize;
  z-index: 2;
}
.nc-clip__handle--start { left: 0; }
.nc-clip__handle--end   { right: 0; }
.nc-clip__handle { transition: width var(--nc-t-fast), filter var(--nc-t-fast); }
.nc-clip:hover > .nc-clip__handle { filter: brightness(1.15); }
.nc-clip__handle:hover { width: 9px; filter: brightness(1.3); }

/* --- waveform (pure CSS, tiles horizontally) ------------------ */
.nc-waveform {
  flex: 1 1 auto; align-self: center;
  height: 62%;
  min-width: 0;
  opacity: .85;
  background-repeat: repeat-x;
  background-position: center;
  background-size: auto 100%;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='128' height='24' viewBox='0 0 128 24'><g fill='white'><rect x='0' y='9' width='2' height='6' rx='1'/><rect x='4' y='6' width='2' height='12' rx='1'/><rect x='8' y='3' width='2' height='18' rx='1'/><rect x='12' y='7' width='2' height='10' rx='1'/><rect x='16' y='1' width='2' height='22' rx='1'/><rect x='20' y='8' width='2' height='8' rx='1'/><rect x='24' y='5' width='2' height='14' rx='1'/><rect x='28' y='2' width='2' height='20' rx='1'/><rect x='32' y='9' width='2' height='6' rx='1'/><rect x='36' y='4' width='2' height='16' rx='1'/><rect x='40' y='0' width='2' height='24' rx='1'/><rect x='44' y='7' width='2' height='10' rx='1'/><rect x='48' y='8' width='2' height='8' rx='1'/><rect x='52' y='3' width='2' height='18' rx='1'/><rect x='56' y='6' width='2' height='12' rx='1'/><rect x='60' y='1' width='2' height='22' rx='1'/><rect x='64' y='5' width='2' height='14' rx='1'/><rect x='68' y='9' width='2' height='6' rx='1'/><rect x='72' y='2' width='2' height='20' rx='1'/><rect x='76' y='7' width='2' height='10' rx='1'/><rect x='80' y='4' width='2' height='16' rx='1'/><rect x='84' y='8' width='2' height='8' rx='1'/><rect x='88' y='0' width='2' height='24' rx='1'/><rect x='92' y='6' width='2' height='12' rx='1'/><rect x='96' y='3' width='2' height='18' rx='1'/><rect x='100' y='9' width='2' height='6' rx='1'/><rect x='104' y='5' width='2' height='14' rx='1'/><rect x='108' y='1' width='2' height='22' rx='1'/><rect x='112' y='7' width='2' height='10' rx='1'/><rect x='116' y='4' width='2' height='16' rx='1'/><rect x='120' y='8' width='2' height='8' rx='1'/><rect x='124' y='2' width='2' height='20' rx='1'/></g></svg>");
}

/* --- playhead -------------------------------------------------- */
.nc-playhead {
  position: absolute; top: -6px; bottom: -6px;
  left: var(--nc-playhead, 50%);
  width: 2px; margin-left: -1px;
  background: #fff;
  border-radius: var(--nc-r-pill);
  pointer-events: auto;
  cursor: ew-resize;
}
.nc-playhead::before,
.nc-playhead::after {
  content: "";
  position: absolute; left: 50%;
  width: 9px; height: 9px; margin-left: -4.5px;
  border-radius: var(--nc-r-pill);
  background: #fff;
  box-shadow: 0 1px 4px rgba(0,0,0,.6);
}
.nc-playhead::before { top: -4px; }
.nc-playhead::after  { bottom: -4px; }

/* Empty space between clips on the same track. */
.nc-track__gap { flex: 0 0 var(--nc-clip-w, 2%); min-width: 0; }

/* ============================================================
   HOVER & POINTER AFFORDANCES
   A timeline is a direct-manipulation surface: every part that
   can be grabbed says so before it is grabbed.
   ============================================================ */

/* The lane you are over gets a hairline, so the target is obvious
   even in the empty space between clips. */
.nc-track { transition: box-shadow var(--nc-t); }
.nc-track:hover { box-shadow: inset 0 0 0 1px var(--nc-line); }

/* Track heads lift with the rest of the row. */
.nc-track-head { transition: background var(--nc-t); }
.nc-track-head:hover { background: var(--nc-raise-hover); }

/* The ruler is a seek target. */
.nc-ruler { cursor: pointer; }
.nc-ruler__mark { transition: color var(--nc-t); }
.nc-ruler:hover .nc-ruler__mark { color: var(--nc-text-2); }

/* Playhead: grows and glows when grabbable. */
.nc-playhead { transition: box-shadow var(--nc-t); }
.nc-playhead::before,
.nc-playhead::after { transition: transform var(--nc-t-fast); }
.nc-playhead:hover { box-shadow: 0 0 10px rgba(255, 255, 255, .45); }
.nc-playhead:hover::before,
.nc-playhead:hover::after { transform: scale(1.3); }

/* Ghost playhead: a faint line that follows the pointer across the
   timeline. Position it with --nc-playhead from a pointermove
   handler; hide it by removing .is-visible. */
.nc-playhead--ghost {
  width: 1px;
  background: var(--nc-line-strong);
  box-shadow: none;
  opacity: 0;
  cursor: default;
  transition: opacity var(--nc-t);
}
.nc-playhead--ghost::before,
.nc-playhead--ghost::after { display: none; }
.nc-playhead--ghost.is-visible { opacity: 1; }

/* Pointer events reach the playhead but not the overlay behind it. */
.nc-timeline__overlay > * { pointer-events: auto; }
.nc-timeline__overlay > .nc-playhead--ghost { pointer-events: none; }


/* ============================================================
   NOCTURNE UI · 08 · UTILITIES
   A deliberately tiny set. If you need more, add a component,
   not another utility.
   ============================================================ */

.nc-row  { display: flex; align-items: center; gap: var(--nc-s-3); min-width: 0; }
.nc-col  { display: flex; flex-direction: column; gap: var(--nc-s-3); min-width: 0; }
.nc-wrap { flex-wrap: wrap; }
.nc-spacer { flex: 1 1 auto; }
.nc-gap-0 { gap: 0; }
.nc-gap-1 { gap: var(--nc-s-1); }
.nc-gap-2 { gap: var(--nc-s-2); }
.nc-gap-4 { gap: var(--nc-s-4); }
.nc-gap-5 { gap: var(--nc-s-5); }
.nc-gap-6 { gap: var(--nc-s-6); }

.nc-divider { height: 1px; background: var(--nc-line); border: 0; margin: 0; }
.nc-divider--v { width: 1px; height: auto; align-self: stretch; }

.nc-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.nc-muted  { color: var(--nc-text-2); }
.nc-subtle { color: var(--nc-text-3); }
.nc-mono   { font-family: var(--nc-font-mono); font-variant-numeric: tabular-nums; }
.nc-caption{ font-size: var(--nc-fs-2); color: var(--nc-text-2); }

/* The one eyebrow: 11px, heavy, tracked out, uppercase. The kit had this
   shape open-coded in five places with three different weights; this is the
   canonical one, and it is the only 11px style allowed on --nc-text-3
   (5.22:1 on surface-1 after the token was raised — see AGENTS.md §3). */
.nc-eyebrow {
  display: block;
  color: var(--nc-text-3);
  font-size: var(--nc-fs-1); font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
}
.nc-eyebrow--accent { color: var(--nc-accent-hover); }

.nc-sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}


/* ============================================================
   NOCTURNE UI · 09 · FEEDBACK
   Dialog, toast, progress, spinner, skeleton, empty state, tabs.
   ============================================================ */

/* --- dialog --------------------------------------------------
   Built on the native <dialog>. Open with el.showModal(), which
   gives you focus trapping, Esc to close and ::backdrop for free.
   ------------------------------------------------------------- */
.nc-dialog {
  width: min(520px, calc(100vw - 32px));
  max-height: min(80vh, 720px);
  padding: 0;
  border: 0;
  border-radius: var(--nc-r-xl);
  background: var(--nc-surface-1);
  color: var(--nc-text);
  font-family: var(--nc-font);
  box-shadow: var(--nc-edge), var(--nc-shadow-3);
  overflow: hidden;
}
/* Open, the dialog is a column: header and footer stay, the body scrolls. */
.nc-dialog[open] { display: flex; flex-direction: column; }
.nc-dialog--sm { width: min(400px, calc(100vw - 32px)); }
.nc-dialog--lg { width: min(760px, calc(100vw - 32px)); }
.nc-dialog::backdrop {
  background: var(--nc-scrim);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.nc-dialog__header {
  display: flex; align-items: center; gap: var(--nc-s-4);
  padding: var(--nc-s-5) var(--nc-s-5) var(--nc-s-4);
}
.nc-dialog__title {
  margin: 0;
  font-size: var(--nc-fs-4); font-weight: 600; letter-spacing: -.01em;
  color: var(--nc-text);
}
.nc-dialog__header .nc-btn { margin-left: auto; }
.nc-dialog__body {
  flex: 1 1 auto; min-height: 0;
  padding: 0 var(--nc-s-5) var(--nc-s-5);
  overflow: auto;
  color: var(--nc-text-2); font-size: var(--nc-fs-3); line-height: 1.6;
}
.nc-dialog__footer {
  display: flex; align-items: center; justify-content: flex-end; gap: var(--nc-s-3);
  padding: var(--nc-s-4) var(--nc-s-5);
  border-top: 1px solid var(--nc-line);
  background: var(--nc-surface-1);
}
.nc-dialog__footer .nc-spacer { margin-right: auto; }

/* Scrim for frameworks that render their own overlay instead of <dialog>. */
.nc-scrim {
  position: fixed; inset: 0;
  display: grid; place-items: center;
  padding: var(--nc-s-5);
  background: var(--nc-scrim);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  z-index: var(--nc-z-modal);
}

/* --- toast ---------------------------------------------------- */
.nc-toast-region {
  position: fixed; right: var(--nc-s-5); bottom: var(--nc-s-5);
  display: flex; flex-direction: column; gap: var(--nc-s-3);
  z-index: var(--nc-z-toast);
  pointer-events: none;
}
.nc-toast-region > * { pointer-events: auto; }
.nc-toast {
  --_accent: var(--nc-text-2);
  display: flex; align-items: flex-start; gap: var(--nc-s-3);
  min-width: 280px; max-width: 380px;
  padding: var(--nc-s-4);
  border: 0;
  border-left: 3px solid var(--_accent);
  border-radius: var(--nc-r-lg);
  background: var(--nc-surface-2);
  box-shadow: var(--nc-edge), var(--nc-shadow-3);
  animation: nc-toast-in var(--nc-t-slow) both;
}
.nc-toast__icon { color: var(--_accent); margin-top: 1px; }
.nc-toast__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; }
.nc-toast__title { font-size: var(--nc-fs-3); font-weight: 600; color: var(--nc-text); }
.nc-toast__desc  { font-size: var(--nc-fs-2); color: var(--nc-text-2); }
.nc-toast__desc .nc-progress { margin-top: var(--nc-s-2); }
.nc-toast--success { --_accent: var(--nc-success); }
.nc-toast--warning { --_accent: var(--nc-warning); }
.nc-toast--danger  { --_accent: var(--nc-danger); }
.nc-toast--accent  { --_accent: var(--nc-accent); }
@keyframes nc-toast-in {
  from { opacity: 0; transform: translateY(8px) scale(.98); }
  to   { opacity: 1; transform: none; }
}

/* --- progress -------------------------------------------------- */
.nc-progress {
  position: relative;
  display: block;
  height: 6px; width: 100%;
  border-radius: var(--nc-r-pill);
  background: var(--nc-overlay);
  overflow: hidden;
}
.nc-progress--lg { height: 10px; }
.nc-progress__fill {
  display: block;
  height: 100%;
  width: var(--nc-progress, 0%);
  border-radius: inherit;
  background: var(--nc-accent);
  transition: width var(--nc-t-slow);
}
.nc-progress--success .nc-progress__fill { background: var(--nc-success); }
.nc-progress--danger  .nc-progress__fill { background: var(--nc-danger); }
.nc-progress--indeterminate .nc-progress__fill {
  width: 38%;
  animation: nc-indeterminate 1.4s var(--nc-ease) infinite;
}
@keyframes nc-indeterminate {
  from { transform: translateX(-105%); }
  to   { transform: translateX(300%); }
}

.nc-spinner {
  width: 16px; height: 16px; flex: none;
  border: 2px solid var(--nc-overlay-2);
  border-top-color: var(--nc-accent);
  border-radius: var(--nc-r-pill);
  animation: nc-spin .7s linear infinite;
}
.nc-spinner--lg { width: 24px; height: 24px; border-width: 2.5px; }
@keyframes nc-spin { to { transform: rotate(360deg); } }

/* --- skeleton -------------------------------------------------- */
.nc-skeleton {
  border-radius: var(--nc-r-sm);
  background-color: var(--nc-overlay);
  background-image: linear-gradient(90deg, transparent 20%, var(--nc-overlay-2) 50%, transparent 80%);
  background-size: 220% 100%;
  animation: nc-shimmer 1.5s ease-in-out infinite;
}
.nc-skeleton--text  { height: 10px; }
.nc-skeleton--title { height: 14px; width: 55%; }
.nc-skeleton--thumb { aspect-ratio: 16 / 10; border-radius: var(--nc-r-md); }
@keyframes nc-shimmer { from { background-position: 120% 0; } to { background-position: -120% 0; } }

/* --- empty state ----------------------------------------------- */
.nc-empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--nc-s-4);
  padding: var(--nc-s-8) var(--nc-s-6);
  border: 1px dashed var(--nc-line-strong);
  border-radius: var(--nc-r-lg);
  text-align: center;
  color: var(--nc-text-2);
}
.nc-empty__icon {
  display: grid; place-items: center;
  width: 46px; height: 46px;
  border-radius: var(--nc-r-pill);
  background: var(--nc-overlay);
  color: var(--nc-text-2);
}
.nc-empty__title { font-size: var(--nc-fs-3); font-weight: 600; color: var(--nc-text); }
.nc-empty__desc  { max-width: 38ch; font-size: var(--nc-fs-2); line-height: 1.6; }
.nc-empty--flush { border: 0; }

/* Drop target: toggle .is-dragover on dragenter / dragleave. */
.nc-empty--dropzone { transition: border-color var(--nc-t), background var(--nc-t); }
.nc-empty--dropzone:hover { border-color: var(--nc-text-3); }
.nc-empty--dropzone.is-dragover {
  border-color: var(--nc-accent);
  background: var(--nc-accent-soft);
  color: var(--nc-text);
}

/* --- tabs ------------------------------------------------------
   Tabs switch what a panel shows. A segmented control switches the
   MODE of one workspace. They are not interchangeable.
   ---------------------------------------------------------------- */
.nc-tabs {
  display: flex; align-items: center; gap: 2px;
  border-bottom: 1px solid var(--nc-line);
  min-width: 0;
}
.nc-tab {
  position: relative;
  display: inline-flex; align-items: center; gap: var(--nc-s-2);
  height: 36px; padding: 0 12px;
  border: 0; background: transparent;
  color: var(--nc-text-3);
  font-family: var(--nc-font); font-size: var(--nc-fs-3); font-weight: 500;
  white-space: nowrap; cursor: pointer;
  transition: color var(--nc-t);
}
.nc-tab:hover { color: var(--nc-text-2); }
.nc-tab:focus-visible { outline: 2px solid var(--nc-accent); outline-offset: -2px; border-radius: var(--nc-r-xs); }
.nc-tab[aria-selected="true"] { color: var(--nc-text); }
.nc-tab[aria-selected="true"]::after {
  content: "";
  position: absolute; left: 8px; right: 8px; bottom: -1px;
  height: 2px; border-radius: var(--nc-r-pill);
  background: var(--nc-accent);
}
.nc-tab__count {
  padding: 0 5px; border-radius: var(--nc-r-xs);
  background: var(--nc-overlay); color: var(--nc-text-3);
  font-size: var(--nc-fs-1); font-weight: 600;
}
.nc-tab[aria-selected="true"] .nc-tab__count { background: var(--nc-accent-soft); color: var(--nc-accent); }


/* ============================================================
   NOCTURNE UI · 10 · INSPECTOR
   The properties panel: collapsible groups, label/control rows,
   scrubbable number fields, checkbox and radio.
   ============================================================ */

.nc-inspector { display: flex; flex-direction: column; min-width: 0; }

/* --- collapsible group (native <details>, no JS) --------------- */
.nc-inspector__group { border-bottom: 1px solid var(--nc-line); }
.nc-inspector__group:last-child { border-bottom: 0; }
.nc-inspector__summary {
  display: flex; align-items: center; gap: var(--nc-s-3);
  height: 38px; padding: 0 2px;
  list-style: none; cursor: pointer;
  font-size: var(--nc-fs-1); font-weight: 650;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--nc-text-3);
  transition: color var(--nc-t);
}
.nc-inspector__summary::-webkit-details-marker { display: none; }
.nc-inspector__summary:hover { color: var(--nc-text-2); }
.nc-inspector__summary:focus-visible { outline: 2px solid var(--nc-accent); outline-offset: 2px; border-radius: var(--nc-r-xs); }
.nc-inspector__summary .nc-i { margin-left: auto; transition: transform var(--nc-t); }
.nc-inspector__group[open] > .nc-inspector__summary { color: var(--nc-text-2); }
.nc-inspector__group[open] > .nc-inspector__summary .nc-i { transform: rotate(180deg); }
.nc-inspector__content {
  display: flex; flex-direction: column; gap: var(--nc-s-3);
  padding: 2px 0 var(--nc-s-5);
}

/* --- property row ---------------------------------------------- */
.nc-prop {
  display: grid;
  grid-template-columns: var(--nc-prop-label-w) minmax(0, 1fr);
  align-items: center;
  gap: var(--nc-s-4);
  min-width: 0;
}
.nc-prop--stacked { grid-template-columns: minmax(0, 1fr); gap: var(--nc-s-2); }
.nc-prop__label {
  font-size: var(--nc-fs-2); font-weight: 500; color: var(--nc-text-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nc-prop__control { display: flex; align-items: center; gap: var(--nc-s-2); min-width: 0; }
.nc-prop__control > .nc-slider { flex: 1 1 auto; }
.nc-prop__hint { grid-column: 2; font-size: var(--nc-fs-1); color: var(--nc-text-3); }

/* Reset one property to its factory value. Render it only while the value
   differs from the default (or keep it in the DOM with [hidden]): an always-on
   reset arrow on every row is noise. Lives at the end of the control. */
.nc-prop__reset {
  flex: none;
  display: inline-grid; place-items: center;
  width: 22px; height: 22px; padding: 0;
  border: 0; border-radius: var(--nc-r-sm);
  background: transparent; color: var(--nc-text-3);
  cursor: pointer;
  transition: color var(--nc-t-fast), background var(--nc-t-fast);
}
.nc-prop__reset .nc-i { width: 14px; height: 14px; }
.nc-prop__reset:hover { color: var(--nc-text); background: var(--nc-raise); }
.nc-prop__reset:focus-visible { outline: 2px solid var(--nc-accent); outline-offset: 1px; }
.nc-prop__reset[hidden] { display: none; }

/* --- number field ---------------------------------------------
   The editor field: X, Y, W, H, angle, opacity. __key is both the
   short label and the drag handle — pointer-drag it to scrub the
   value. Native spinners are removed, so the arrows the user gets
   are the input's own ArrowUp/ArrowDown over `step`.
   The CSS supplies the affordance; wire the drag and the shift
   step with ~15 lines of JS (see AGENTS.md section 8).
   ---------------------------------------------------------------- */
.nc-num {
  display: inline-flex; align-items: center; gap: var(--nc-s-2);
  flex: 1 1 0; min-width: 0;
  height: var(--nc-num-h, 32px); padding: 0 8px;
  border: 1px solid var(--nc-line);
  border-radius: var(--nc-r-sm);
  background: var(--nc-inset);
  transition: border-color var(--nc-t), box-shadow var(--nc-t);
}
.nc-num:hover { border-color: var(--nc-line-strong); }
.nc-num:focus-within { border-color: var(--nc-accent); box-shadow: 0 0 0 3px var(--nc-accent-soft); }
.nc-num.is-scrubbing { border-color: var(--nc-accent); }
.nc-num__key {
  flex: none;
  font-size: var(--nc-fs-1); font-weight: 650; color: var(--nc-text-3);
  cursor: ew-resize; user-select: none;
  transition: color var(--nc-t);
}
.nc-num:hover .nc-num__key, .nc-num.is-scrubbing .nc-num__key { color: var(--nc-accent); }
.nc-num input {
  all: unset;
  width: 100%; min-width: 0;
  font-family: var(--nc-font-mono); font-size: var(--nc-fs-2);
  font-variant-numeric: tabular-nums;
  color: var(--nc-text);
}
.nc-num input::-webkit-outer-spin-button,
.nc-num input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.nc-num input[type="number"] { -moz-appearance: textfield; }
.nc-num__unit { flex: none; font-size: var(--nc-fs-1); color: var(--nc-text-3); }
.nc-num input:disabled { color: var(--nc-text-3); cursor: not-allowed; }
.nc-num:has(input:disabled) { opacity: .42; }
.nc-num:has(input:disabled) .nc-num__key { cursor: default; }
.nc-num:has(input[aria-invalid="true"]) { border-color: var(--nc-danger); }
.nc-num:has(input[readonly]) { background: transparent; }
/* --sm fits three of these across a 256px inspector column. */
.nc-num--sm { --nc-num-h: 26px; padding: 0 6px; gap: var(--nc-s-1); border-radius: var(--nc-r-xs); }
.nc-num--sm input { font-size: var(--nc-fs-1); }
/* --wide stops a lone field from shrinking to the width of its digits. */
.nc-num--wide { flex: 1 1 100%; }
/* Right-aligned digits, for a column of values that must line up. */
.nc-num--end input { text-align: right; }

/* --- checkbox & radio ------------------------------------------ */
.nc-check {
  display: inline-flex; align-items: center; gap: 9px;
  min-width: 0; cursor: pointer;
  font-size: var(--nc-fs-3); color: var(--nc-text-2);
  transition: color var(--nc-t);
}
.nc-check input { position: absolute; width: 1px; height: 1px; opacity: 0; margin: 0; }
.nc-check__box {
  display: grid; place-items: center;
  width: 17px; height: 17px; flex: none;
  border: 1px solid var(--nc-line-strong);
  border-radius: 5px;
  background: var(--nc-inset);
  transition: background var(--nc-t), border-color var(--nc-t), box-shadow var(--nc-t);
}
.nc-check__box .nc-i { width: 11px; height: 11px; stroke-width: 2.75; color: var(--nc-text-on-accent); opacity: 0; transition: opacity var(--nc-t); }
.nc-check:hover { color: var(--nc-text); }
.nc-check:hover .nc-check__box { border-color: var(--nc-text-3); }
.nc-check:has(input:checked) { color: var(--nc-text); }
.nc-check:has(input:checked) .nc-check__box {
  background: var(--nc-accent); border-color: transparent;
  box-shadow: var(--nc-edge-accent);
}
.nc-check:has(input:checked) .nc-check__box .nc-i { opacity: 1; }
.nc-check:has(input:focus-visible) .nc-check__box { outline: 2px solid var(--nc-accent); outline-offset: 2px; }
.nc-check:has(input:disabled) { opacity: .42; pointer-events: none; }

.nc-check--radio .nc-check__box { border-radius: var(--nc-r-pill); }
.nc-check__dot {
  width: 7px; height: 7px; border-radius: var(--nc-r-pill);
  background: var(--nc-thumb); transform: scale(0);
  transition: transform var(--nc-t);
}
.nc-check--radio:has(input:checked) .nc-check__dot { transform: scale(1); }

/* Stacked group of checkboxes or radios. */
.nc-check-group { display: flex; flex-direction: column; gap: var(--nc-s-3); }
.nc-check-group--row { flex-direction: row; flex-wrap: wrap; gap: var(--nc-s-5); }


/* ============================================================
   NOCTURNE UI · 11 · QUOTA
   Metered actions: how many of a limited operation remain.
   Discrete pips for small allowances (<= 10), .nc-progress above that.
   ============================================================ */

.nc-quota {
  --_accent: var(--nc-accent);
  display: flex; flex-direction: column; gap: var(--nc-s-3);
  min-width: 0;
}

.nc-quota__head { display: flex; align-items: baseline; gap: var(--nc-s-3); min-width: 0; }
.nc-quota__label {
  font-size: var(--nc-fs-2); font-weight: 500; color: var(--nc-text-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nc-quota__count {
  margin-left: auto; flex: none;
  font-family: var(--nc-font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--nc-fs-2); font-weight: 500; color: var(--nc-text-3);
}
.nc-quota__count b { color: var(--_accent); font-weight: 650; }

/* Pips: one per unit of allowance. Remaining are filled, spent are hollow. */
.nc-quota__pips { display: flex; gap: 4px; min-width: 0; }
.nc-quota__pip {
  flex: 1 1 0; min-width: 8px; height: 6px;
  border-radius: var(--nc-r-pill);
  background: var(--_accent);
  box-shadow: var(--nc-edge-accent);
  transition: background var(--nc-t), box-shadow var(--nc-t);
}
.nc-quota__pip.is-spent { background: var(--nc-overlay); box-shadow: none; }

.nc-quota__note {
  display: flex; align-items: center; gap: var(--nc-s-2);
  margin: 0;
  font-size: var(--nc-fs-1); color: var(--nc-text-3);
}

/* --- severity: the meter recolors as the allowance runs down ---
   Set --low at one remaining, --empty at zero. Color is never the only
   signal: the count and the spent pips carry it too. */
.nc-quota--low   { --_accent: var(--nc-warning); }
.nc-quota--empty { --_accent: var(--nc-danger); }
.nc-quota--empty .nc-quota__pip { background: var(--nc-overlay); box-shadow: none; }

/* --- inline: sits beside the metered action in a toolbar --- */
.nc-quota--inline {
  flex-direction: row; align-items: center; gap: var(--nc-s-3);
  height: var(--nc-h-sm); padding: 0 10px;
  border: 0;
  border-radius: var(--nc-r-pill);
  background: var(--nc-raise);
  box-shadow: var(--nc-edge);
}
.nc-quota--inline .nc-quota__pips { gap: 3px; }
.nc-quota--inline .nc-quota__pip { flex: none; width: 12px; height: 5px; }
.nc-quota--inline .nc-quota__count { margin-left: 0; font-size: var(--nc-fs-1); }

/* --- hero: the headline number inside a dialog --- */
.nc-quota--hero { align-items: center; text-align: center; gap: var(--nc-s-4); }
.nc-quota--hero .nc-quota__count {
  margin-left: 0;
  font-family: var(--nc-font);
  font-size: 42px; font-weight: 600; line-height: 1; letter-spacing: -.03em;
  color: var(--nc-text-3);
}
.nc-quota--hero .nc-quota__label { font-size: var(--nc-fs-3); }
.nc-quota--hero .nc-quota__pips { width: 100%; max-width: 220px; }
.nc-quota--hero .nc-quota__pip { height: 8px; }

/* --- the metered action once the allowance is gone ---------------
   Not `disabled`: a dead button tells the user nothing. This stays
   clickable so it can open the dialog that explains the limit. */
.nc-btn--locked {
  --_bg: var(--nc-raise);
  --_bg-hover: var(--nc-raise-hover);
  --_fg: var(--nc-text-3);
  --_lift: none;
}
.nc-btn--locked:hover { --_fg: var(--nc-text-2); }


/* ============================================================
   NOCTURNE UI · 12 · TABLE
   .nc-table  ·  admin lists: users, files, logs.
   Rows are opaque surface-1 on the panel; the header is an
   eyebrow, not a bar. Actions live in the last cell, right-aligned.
   ============================================================ */

.nc-table { width: 100%; overflow: auto; }
.nc-table table { width: 100%; border-collapse: collapse; font-size: var(--nc-fs-3); }
.nc-table th {
  padding: var(--nc-s-3) var(--nc-s-4);
  text-align: left;
  color: var(--nc-text-3);
  font-size: var(--nc-fs-1);
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--nc-line);
  white-space: nowrap;
}
.nc-table td {
  padding: var(--nc-s-4);
  border-bottom: 1px solid var(--nc-line);
  vertical-align: top;
  color: var(--nc-text);
}
.nc-table tbody tr { transition: background var(--nc-t-fast); }
.nc-table tbody tr:hover { background: var(--nc-raise); }
.nc-table--sticky th { position: sticky; top: 0; background: var(--nc-surface-1); z-index: 1; }
.nc-table--compact td { padding: var(--nc-s-3) var(--nc-s-4); }

/* Secondary line under a cell's main text. */
.nc-table__sub { display: block; margin-top: 2px; color: var(--nc-text-3); font-size: var(--nc-fs-2); }
/* Right-aligned action cell; buttons inside are ghost, small. */
.nc-table__actions { text-align: right; white-space: nowrap; }
.nc-table__actions .nc-btn { margin-left: var(--nc-s-2); }
/* Numeric / date cells. */
.nc-table__num { text-align: right; font-variant-numeric: tabular-nums; }
/* State via ARIA: an inactive row fades but stays readable. */
.nc-table tr[aria-disabled="true"] td,
.nc-table tr.is-inactive td { opacity: .45; }
.nc-table tr[aria-selected="true"] { background: var(--nc-accent-soft); }

/* --- La tabla en pantalla angosta -------------------------------------------
   Una tabla de cinco columnas no cabe en un teléfono: o se corta el nombre a
   tres letras o se sale por la derecha. `--stack` la convierte en una tarjeta
   por fila, con el encabezado de cada celda tomado de su data-label. La primera
   celda es el título de la tarjeta y no lleva etiqueta.
   El HTML no cambia: sigue siendo una tabla de verdad. */
@media (max-width: 700px) {
  .nc-table--stack table, .nc-table--stack tbody, .nc-table--stack tr, .nc-table--stack td { display: block; width: 100%; }
  .nc-table--stack thead { display: none; }
  .nc-table--stack tr {
    padding: var(--nc-s-4);
    border-bottom: 1px solid var(--nc-line);
  }
  .nc-table--stack tr:hover { background: none; }
  .nc-table--stack td {
    display: grid; grid-template-columns: 8.5em 1fr; gap: var(--nc-s-3);
    align-items: start; justify-items: start;   /* una etiqueta no se estira a todo el ancho */
    padding: 3px 0; border: 0;
  }
  .nc-table--stack td::before {
    content: attr(data-label);
    color: var(--nc-text-3);
    font-size: var(--nc-fs-1); font-weight: 700;
    letter-spacing: .1em; text-transform: uppercase;
  }
  .nc-table--stack td:first-child { display: block; margin-bottom: var(--nc-s-2); }
  .nc-table--stack td:first-child::before { content: none; }
  .nc-table--stack .nc-table__num { text-align: left; }
  .nc-table--stack .nc-table__actions { display: flex; flex-wrap: wrap; gap: var(--nc-s-2); margin-top: var(--nc-s-3); text-align: left; }
  .nc-table--stack .nc-table__actions::before { content: none; }
  .nc-table--stack .nc-table__actions .nc-btn { margin-left: 0; }
}
/* Varias etiquetas en una celda se acomodan solas en vez de encimarse. */
.nc-table__chips { display: flex; flex-wrap: wrap; gap: var(--nc-s-1); }


/* ============================================================
   NOCTURNE UI · 13 · BOARD (kanban)
   .nc-board  ·  columns of task cards, drag between columns.
   The CSS supplies the geometry, states and the ghost; the
   dragging itself is the host app's job (see AGENTS.md §8).
   Column = opaque surface-1 panel. Card = opaque surface-2
   container with the edge ring. Drop target = accent outline.
   ============================================================ */

.nc-board {
  display: flex; gap: var(--nc-s-4);
  align-items: flex-start;
  min-height: 0; height: 100%;
  overflow: auto;
  padding: var(--nc-s-5);
}
/* Fixed-column variant: N equal columns filling the width (ideas: 2). */
.nc-board--fixed { display: grid; grid-template-columns: repeat(var(--nc-board-cols, 2), minmax(280px, 1fr)); align-items: stretch; }

.nc-board__col {
  flex: 0 0 var(--nc-board-col-w, 280px);
  display: flex; flex-direction: column;
  min-height: 0; max-height: 100%;
  border: 0; border-radius: var(--nc-r-xl);
  background: var(--nc-surface-1);
  box-shadow: var(--nc-edge), var(--nc-shadow-1);
  transition: box-shadow var(--nc-t), background var(--nc-t);
}
.nc-board--fixed .nc-board__col { flex: initial; }
.nc-board__col[aria-dropeffect="move"],
.nc-board__col.is-target { box-shadow: 0 0 0 2px var(--nc-accent), var(--nc-shadow-1); background: var(--nc-surface-2); }
/* The "add a column" placeholder: dashed, not a real column. */
.nc-board__col--new { background: transparent; box-shadow: inset 0 0 0 1px var(--nc-line-strong); border-radius: var(--nc-r-xl); }
.nc-board__col--new .nc-board__add { margin: 0; width: 100%; padding: var(--nc-s-4); }

.nc-board__col-head {
  display: flex; align-items: center; gap: var(--nc-s-3);
  padding: var(--nc-s-4) var(--nc-s-4) var(--nc-s-3);
}
.nc-board__col-title {
  flex: 1; min-width: 0; margin: 0;
  font-size: var(--nc-fs-3); font-weight: 650;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nc-board__col-title--edit {
  flex: 1; min-width: 0; height: var(--nc-h-xs); padding: 0 var(--nc-s-2);
  border: 0; border-radius: var(--nc-r-sm); background: var(--nc-inset);
  box-shadow: 0 0 0 2px var(--nc-accent); color: var(--nc-text);
  font: inherit; font-size: var(--nc-fs-3); font-weight: 650;
}

.nc-board__cards {
  display: flex; flex-direction: column; gap: var(--nc-s-3);
  min-height: 24px; padding: 0 var(--nc-s-4);
  overflow: auto; scrollbar-width: thin;
}
.nc-board__empty {
  padding: var(--nc-s-6); text-align: center;
  color: var(--nc-text-3); font-size: var(--nc-fs-2);
  border-radius: var(--nc-r-md); box-shadow: inset 0 0 0 1px var(--nc-line);
}

/* ---- card -------------------------------------------------- */
.nc-card {
  display: block; padding: var(--nc-s-4) var(--nc-s-4);
  border: 0; border-radius: var(--nc-r-md);
  background: var(--nc-surface-2);
  box-shadow: var(--nc-edge);
  color: var(--nc-text); text-align: left; font: inherit;
  transition: box-shadow var(--nc-t), opacity var(--nc-t), transform var(--nc-t-fast);
}
.nc-card:hover { box-shadow: var(--nc-edge), var(--nc-lift); }
.nc-card:focus-visible { outline: 2px solid var(--nc-accent); outline-offset: 2px; }
/* `touch-action: none` es lo que necesita el arrastre con eventos de puntero,
   y en un teléfono es una trampa: la tarjeta se come TODOS los gestos, scroll
   incluido, y como en móvil las tarjetas cubren la pantalla entera, el tablero
   se vuelve imposible de recorrer con el dedo. Donde no hay puntero fino, la
   tarjeta devuelve el gesto al navegador y se mueve desde su ficha, no
   arrastrando. */
.nc-card--movable { cursor: grab; touch-action: none; }
@media (hover: none) and (pointer: coarse) {
  .nc-card--movable { cursor: default; touch-action: auto; }
}
.nc-card[aria-grabbed="true"],
.nc-card.is-dragging { opacity: .35; }
.nc-card__title { margin: 0; font-size: var(--nc-fs-3); line-height: 1.45; }
.nc-card__labels { display: flex; flex-wrap: wrap; gap: var(--nc-s-1); margin-bottom: var(--nc-s-2); }
.nc-card__meta { display: flex; align-items: center; gap: var(--nc-s-3); margin-top: var(--nc-s-3); color: var(--nc-text-3); font-size: var(--nc-fs-2); }
.nc-card__date { padding: 1px var(--nc-s-2); border-radius: var(--nc-r-xs); box-shadow: inset 0 0 0 1px var(--nc-line-strong); font-variant-numeric: tabular-nums; }
.nc-card__date--overdue { color: var(--nc-danger); box-shadow: inset 0 0 0 1px var(--nc-danger-soft); background: var(--nc-danger-soft); }
.nc-card__people { margin-top: var(--nc-s-2); color: var(--nc-accent-hover); font-size: var(--nc-fs-2); }
.nc-card__people::before { content: "@ "; color: var(--nc-text-3); }
.nc-card__note { margin: var(--nc-s-2) 0 0; padding-left: var(--nc-s-3); border-left: 2px solid var(--nc-line-strong); color: var(--nc-text-2); font-size: var(--nc-fs-2); line-height: 1.4; }
/* Header row inside a card: label left, quick action right. */
.nc-card__head { display: flex; align-items: center; justify-content: space-between; gap: var(--nc-s-3); margin-bottom: var(--nc-s-2); }

/* Floating clone that follows the pointer while dragging. The host app
   positions it with transform; it never receives pointer events. */
.nc-ghost {
  position: fixed; left: 0; top: 0; z-index: var(--nc-z-popover);
  pointer-events: none; opacity: .95;
  transform: translate(-9999px, -9999px);
  box-shadow: var(--nc-edge), var(--nc-shadow-3), 0 0 0 2px var(--nc-accent);
  cursor: grabbing;
}
.nc-scope.is-dragging, .nc-scope.is-dragging * { user-select: none; cursor: grabbing; }

/* ---- add card / column ------------------------------------- */
.nc-board__add {
  margin: var(--nc-s-3) var(--nc-s-4) var(--nc-s-4);
  padding: var(--nc-s-3) var(--nc-s-3);
  border: 0; border-radius: var(--nc-r-sm);
  background: transparent; color: var(--nc-text-2);
  font: inherit; font-size: var(--nc-fs-2); text-align: left; cursor: pointer;
  transition: background var(--nc-t-fast), color var(--nc-t-fast);
}
.nc-board__add:hover { background: var(--nc-raise); color: var(--nc-text); }
.nc-board__add:focus-visible { outline: 2px solid var(--nc-accent); outline-offset: 2px; }
.nc-board__add-form { display: grid; gap: var(--nc-s-3); margin: var(--nc-s-3) var(--nc-s-4) var(--nc-s-4); }
.nc-board__add-form .nc-row { gap: var(--nc-s-2); }

@media (max-width: 860px) {
  .nc-board { gap: var(--nc-s-3); padding: var(--nc-s-4); scroll-snap-type: x mandatory; }
  .nc-board__col { flex-basis: 84vw; scroll-snap-align: start; }
  .nc-board--fixed { display: flex; }
  .nc-board--fixed .nc-board__col { flex: 0 0 84vw; }
}


/* ============================================================
   NOCTURNE UI · 14 · VIEWER (lightbox)
   <dialog class="nc-viewer">  ·  one image or video, large, with
   prev/next and a side panel of metadata. Native dialog so Esc,
   focus trapping and the scrim come for free; the host app calls
   showModal()/close() and wires the arrows.
   ============================================================ */

.nc-viewer {
  position: fixed; inset: 0; width: 100vw; height: 100vh; max-width: none; max-height: none;
  margin: 0; padding: 0; border: 0;
  background: var(--nc-canvas); color: var(--nc-text);
  display: none; grid-template-columns: minmax(0, 1fr) var(--nc-viewer-side, 340px);
}
.nc-viewer[open] { display: grid; }
.nc-viewer::backdrop { background: var(--nc-scrim); }

.nc-viewer__stage {
  position: relative; display: grid; place-items: center;
  min-width: 0; min-height: 0; padding: var(--nc-s-6);
  background: var(--nc-inset);
}
.nc-viewer__stage img, .nc-viewer__stage video, .nc-viewer__stage iframe {
  max-width: 100%; max-height: 100%; width: auto; height: auto;
  border-radius: var(--nc-r-sm); box-shadow: var(--nc-shadow-3);
}
.nc-viewer__stage iframe { width: 100%; height: 100%; border: 0; }

/* Prev/next: circular glass buttons that fade in on hover of the stage. */
.nc-viewer__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: var(--nc-h-lg); height: var(--nc-h-lg);
  display: grid; place-items: center;
  border: 0; border-radius: var(--nc-r-pill);
  background: var(--nc-glass); color: var(--nc-text);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  box-shadow: var(--nc-edge);
  cursor: pointer; opacity: 0; transition: opacity var(--nc-t), background var(--nc-t-fast);
}
.nc-viewer__stage:hover .nc-viewer__nav, .nc-viewer__nav:focus-visible { opacity: 1; }
.nc-viewer__nav:hover { background: var(--nc-surface-3); }
.nc-viewer__nav:focus-visible { outline: 2px solid var(--nc-accent); outline-offset: 2px; }
.nc-viewer__nav--prev { left: var(--nc-s-5); }
.nc-viewer__nav--next { right: var(--nc-s-5); }

.nc-viewer__side {
  display: flex; flex-direction: column; gap: var(--nc-s-4);
  min-height: 0; padding: var(--nc-s-6);
  background: var(--nc-surface-1); box-shadow: inset 1px 0 0 var(--nc-line);
  overflow: auto;
}
.nc-viewer__title { margin: 0; font-size: var(--nc-fs-4); font-weight: 650; word-break: break-word; }
.nc-viewer__desc { margin: 0; color: var(--nc-text-2); font-size: var(--nc-fs-2); line-height: 1.5; }
.nc-viewer__kv { display: grid; grid-template-columns: max-content 1fr; gap: var(--nc-s-2) var(--nc-s-4); margin: 0; font-size: var(--nc-fs-2); }
.nc-viewer__kv dt { color: var(--nc-text-3); }
.nc-viewer__kv dd { margin: 0; color: var(--nc-text); word-break: break-word; }
.nc-viewer__actions { display: flex; flex-wrap: wrap; gap: var(--nc-s-2); }
.nc-viewer__siblings { display: flex; flex-wrap: wrap; gap: var(--nc-s-2); }
.nc-viewer__note { color: var(--nc-text-3); font-size: var(--nc-fs-1); line-height: 1.5; }

/* No hover means no way to discover the arrows: on touch they stay visible. */
@media (hover: none) {
  .nc-viewer__nav { opacity: 1; }
}

@media (max-width: 860px) {
  .nc-viewer[open] { grid-template-columns: minmax(0, 1fr); grid-template-rows: 1fr auto; overflow: auto; }
  .nc-viewer__stage { padding: var(--nc-s-4); }
  .nc-viewer__nav { opacity: 1; width: var(--nc-h-md); height: var(--nc-h-md); }
  .nc-viewer__nav--prev { left: var(--nc-s-3); }
  .nc-viewer__nav--next { right: var(--nc-s-3); }
  .nc-viewer__side { box-shadow: inset 0 1px 0 var(--nc-line); max-height: 45vh; padding: var(--nc-s-5); }
}


/* ============================================================
   NOCTURNE UI · 15 · SELECTION TRAY
   .nc-tray  ·  the bar that rises from the bottom when items are
   selected: count, options, bulk actions. Opaque surface-2, an
   accent hairline on top. Hidden until [open] / .is-open.
   ============================================================ */

.nc-tray[hidden] { display: none; }
.nc-tray {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--nc-z-dock);
  display: none; align-items: center; flex-wrap: wrap; gap: var(--nc-s-3);
  padding: var(--nc-s-4) var(--nc-s-6);
  background: var(--nc-surface-2);
  box-shadow: 0 -1px 0 var(--nc-accent), var(--nc-shadow-2);
  transform: translateY(100%); transition: transform var(--nc-t-slow);
}
.nc-tray[open], .nc-tray.is-open { display: flex; transform: none; }
.nc-tray__count { font-size: var(--nc-fs-3); font-weight: 650; }
.nc-tray__count b { color: var(--nc-accent-hover); }
.nc-tray__hint { color: var(--nc-text-3); font-size: var(--nc-fs-2); }
.nc-tray__spacer { flex: 1; }
/* Progress row that spans the whole tray while a bulk operation runs. */
.nc-tray__progress { flex-basis: 100%; display: flex; align-items: center; gap: var(--nc-s-4); color: var(--nc-text-2); font-size: var(--nc-fs-2); }
.nc-tray__progress .nc-progress { flex: 1; }
@media (max-width: 860px) { .nc-tray { padding: var(--nc-s-3) var(--nc-s-4); gap: var(--nc-s-2); } }


/* ============================================================
   NOCTURNE UI · 16 · FACETS & QUERY
   .nc-facets  ·  the filter rail of a search: groups of options
   with counts. .nc-query  ·  the search field with its parsed
   terms shown as tokens. Options are real checkboxes styled by
   :checked; state never lives in a class.
   ============================================================ */

.nc-facets { display: flex; flex-direction: column; gap: var(--nc-s-6); padding: var(--nc-s-5) var(--nc-s-5) var(--nc-s-8); overflow: auto; }
.nc-facets__head { display: flex; align-items: center; justify-content: space-between; min-height: var(--nc-h-md); padding: 0 var(--nc-s-5); box-shadow: inset 0 -1px 0 var(--nc-line); font-size: var(--nc-fs-3); font-weight: 650; }
.nc-facet { display: flex; flex-direction: column; gap: 2px; }
.nc-facet__title { margin: 0 0 var(--nc-s-2); color: var(--nc-accent-hover); font-size: var(--nc-fs-1); font-weight: 800; letter-spacing: .16em; text-transform: uppercase; }
.nc-facet__opt {
  display: flex; align-items: center; gap: var(--nc-s-3);
  min-height: 26px; padding: 2px var(--nc-s-2); margin: 0 calc(-1 * var(--nc-s-2));
  border-radius: var(--nc-r-xs); color: var(--nc-text-2); font-size: var(--nc-fs-2);
  cursor: pointer; transition: background var(--nc-t-fast), color var(--nc-t-fast);
}
.nc-facet__opt:hover { background: var(--nc-raise); color: var(--nc-text); }
.nc-facet__opt input { position: absolute; opacity: 0; width: 0; height: 0; }
/* The box is drawn by ::before so the input can stay invisible. */
.nc-facet__opt::before {
  content: ""; flex: none; width: 14px; height: 14px; border-radius: 4px;
  background: var(--nc-inset); box-shadow: inset 0 0 0 1px var(--nc-line-strong);
  transition: background var(--nc-t-fast), box-shadow var(--nc-t-fast);
}
.nc-facet__opt:has(input:checked) { color: var(--nc-text); }
.nc-facet__opt:has(input:checked)::before { background: var(--nc-accent); box-shadow: inset 0 0 0 1px var(--nc-accent); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath d='M3 7.5l2.5 2.5L11 4.5' fill='none' stroke='white' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
.nc-facet__opt:has(input:focus-visible)::before { outline: 2px solid var(--nc-accent); outline-offset: 2px; }
.nc-facet__name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nc-facet__count { color: var(--nc-text-3); font-size: var(--nc-fs-1); font-variant-numeric: tabular-nums; }
.nc-facet__hint { margin: var(--nc-s-2) 0 0; color: var(--nc-text-3); font-size: var(--nc-fs-1); line-height: 1.4; }

/* ---- query field ------------------------------------------- */
.nc-query { position: relative; display: flex; align-items: center; gap: var(--nc-s-3); min-width: 0; height: var(--nc-h-md); padding: 0 var(--nc-s-4); border-radius: var(--nc-r-md); background: var(--nc-inset); box-shadow: inset 0 0 0 1px var(--nc-line); transition: box-shadow var(--nc-t-fast); }
.nc-query:focus-within { box-shadow: inset 0 0 0 1px var(--nc-accent), 0 0 0 3px var(--nc-accent-soft); }
.nc-query .nc-i { color: var(--nc-text-3); }
.nc-query input { flex: 1; min-width: 0; height: 100%; border: 0; background: transparent; color: var(--nc-text); font: inherit; font-size: var(--nc-fs-3); outline: 0; }
.nc-query input::placeholder { color: var(--nc-text-3); }
.nc-kbd { padding: 1px 6px; border-radius: var(--nc-r-xs); box-shadow: inset 0 0 0 1px var(--nc-line-strong); color: var(--nc-text-3); font-family: var(--nc-font-mono); font-size: var(--nc-fs-1); }
/* The parsed terms, as read-only tokens under the field. */
.nc-query__tokens { display: flex; flex-wrap: wrap; gap: var(--nc-s-1); }
.nc-token { display: inline-flex; align-items: center; gap: 4px; height: 22px; padding: 0 var(--nc-s-2); border-radius: var(--nc-r-xs); background: var(--nc-accent-soft); color: var(--nc-text); font-size: var(--nc-fs-1); }
.nc-token__key { color: var(--nc-accent-hover); font-weight: 650; }
.nc-token--free { background: var(--nc-raise); }


/* ============================================================
   NOCTURNE UI · 17 · LINK CARD & IDENTITY
   .nc-linkcard  ·  the preview of a pasted URL: image, site, title.
   .nc-identity  ·  who: initials avatar, name, role.
   ============================================================ */

.nc-linkcard { display: block; overflow: hidden; border-radius: var(--nc-r-md); background: var(--nc-inset); box-shadow: inset 0 0 0 1px var(--nc-line); color: inherit; text-decoration: none; transition: box-shadow var(--nc-t-fast); }
a.nc-linkcard:hover { box-shadow: inset 0 0 0 1px var(--nc-line-strong); }
.nc-linkcard:focus-visible { outline: 2px solid var(--nc-accent); outline-offset: 2px; }
.nc-linkcard__img { display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover; background: var(--nc-surface-3); }
.nc-linkcard__img--tall { aspect-ratio: 4 / 5; }
.nc-linkcard__body { display: grid; gap: 2px; padding: var(--nc-s-3) var(--nc-s-4); }
.nc-linkcard__site { color: var(--nc-text-3); font-size: var(--nc-fs-1); font-weight: 700; letter-spacing: .1em; text-transform: uppercase; }
.nc-linkcard__title { color: var(--nc-text); font-size: var(--nc-fs-2); font-weight: 600; line-height: 1.35; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.nc-linkcard__desc { color: var(--nc-text-2); font-size: var(--nc-fs-1); line-height: 1.35; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
/* While the preview loads the card is just the domain as a link. */
.nc-linkcard.is-loading { padding: var(--nc-s-2) var(--nc-s-4); color: var(--nc-text-3); font-size: var(--nc-fs-1); }
.nc-linkcard__url { color: var(--nc-text-3); font-size: var(--nc-fs-1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nc-linkcard--row { display: grid; grid-template-columns: 96px 1fr; }
.nc-linkcard--row .nc-linkcard__img { aspect-ratio: 1; height: 100%; }

.nc-identity { display: inline-flex; align-items: center; gap: var(--nc-s-3); min-width: 0; color: var(--nc-text); }
.nc-identity__avatar { flex: none; width: 28px; height: 28px; display: grid; place-items: center; border-radius: var(--nc-r-pill); background: var(--nc-accent-soft); color: var(--nc-accent-hover); font-size: var(--nc-fs-1); font-weight: 800; letter-spacing: .02em; text-transform: uppercase; box-shadow: var(--nc-edge); }
.nc-identity__avatar img { width: 100%; height: 100%; border-radius: inherit; object-fit: cover; }
.nc-identity__avatar--lg { width: 40px; height: 40px; font-size: var(--nc-fs-3); }
.nc-identity__text { display: grid; min-width: 0; line-height: 1.2; }
.nc-identity__name { font-size: var(--nc-fs-2); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nc-identity__role { color: var(--nc-text-3); font-size: var(--nc-fs-1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nc-identity--admin .nc-identity__role { color: var(--nc-success); }
/* Solo el avatar. Es la identidad de una barra de aplicación: el nombre
   completo no cabe en un encabezado que ya lleva marca, navegación y acciones,
   y en cuanto la ventana se estrecha es lo primero que estorba. El nombre no
   desaparece, se muda al menú que abre el avatar, que es donde se va a buscar. */
.nc-identity--avatar { gap: 0; }
.nc-identity--avatar .nc-identity__text { display: none; }


/* ============================================================
   NOCTURNE UI · 18 · AUTH
   .nc-auth  ·  the one-card page: sign in, magic link, code.
   Fills the viewport on the canvas; the card is surface-1 with
   the deep shadow. Modes: [data-mode="sent"] shows the note.
   ============================================================ */

.nc-auth[hidden] { display: none; }
.nc-auth { position: fixed; inset: 0; z-index: var(--nc-z-modal); display: grid; place-items: center; padding: var(--nc-s-6); background: var(--nc-canvas); }
.nc-auth__card { width: min(400px, 100%); display: grid; gap: var(--nc-s-5); padding: var(--nc-s-8); border-radius: var(--nc-r-2xl); background: var(--nc-surface-1); box-shadow: var(--nc-edge), var(--nc-shadow-3); }
.nc-auth__brand { display: flex; align-items: baseline; gap: var(--nc-s-2); font-size: var(--nc-fs-5); font-weight: 800; letter-spacing: -.02em; }
.nc-auth__brand small { color: var(--nc-text-2); font-size: var(--nc-fs-3); font-weight: 400; }
.nc-auth__hint { margin: 0; color: var(--nc-text-2); font-size: var(--nc-fs-3); line-height: 1.5; }
.nc-auth__card .nc-input { height: var(--nc-h-lg); font-size: var(--nc-fs-3); }
.nc-auth__code { flex: 1; text-align: center; font-family: var(--nc-font-mono); font-size: var(--nc-fs-4); letter-spacing: .3em; }
.nc-auth__note { padding: var(--nc-s-3) var(--nc-s-4); border-radius: var(--nc-r-sm); box-shadow: inset 0 0 0 1px var(--nc-warning); color: var(--nc-warning); font-size: var(--nc-fs-2); line-height: 1.5; }
.nc-auth__note a { color: var(--nc-accent-hover); }
.nc-auth__note code { color: var(--nc-text); font-family: var(--nc-font-mono); font-size: var(--nc-fs-3); letter-spacing: .15em; }
.nc-auth__foot { color: var(--nc-text-3); font-size: var(--nc-fs-1); text-align: center; }


/* ============================================================
   NOCTURNE UI · 19 · APP LAYOUT & RESPONSIVE
   .nc-shell  ·  app bar on top, one workspace below, full height.
   .nc-split  ·  rail + main (search: facets + results).
   .nc-split__rail becomes an off-canvas drawer under 860px. For a
   drawer that covers the panel it opens over, see .nc-drawer (21).
   .nc-bar    ·  the thin toolbar at the top of a workspace.
   ============================================================ */

.nc-shell { display: grid; grid-template-rows: auto minmax(0, 1fr); height: 100vh; height: 100dvh; }
.nc-shell__ws { display: flex; flex-direction: column; min-height: 0; }
.nc-split { display: grid; grid-template-columns: var(--nc-rail-w, 246px) minmax(0, 1fr); min-height: 0; height: 100%; }
.nc-split__rail { min-height: 0; overflow: auto; background: var(--nc-surface-1); box-shadow: inset -1px 0 0 var(--nc-line); }
.nc-split__main { min-width: 0; min-height: 0; overflow: auto; }

.nc-bar { display: flex; align-items: center; gap: var(--nc-s-3); flex-wrap: wrap; padding: var(--nc-s-3) var(--nc-s-5); box-shadow: inset 0 -1px 0 var(--nc-line); background: var(--nc-surface-1); }
.nc-bar__title { font-size: var(--nc-fs-3); font-weight: 650; }
.nc-bar__title small { margin-left: var(--nc-s-2); color: var(--nc-text-3); font-size: var(--nc-fs-1); font-weight: 400; }
.nc-bar__spacer { flex: 1; }
.nc-bar__note { color: var(--nc-text-3); font-size: var(--nc-fs-2); }

/* A blocking wait: scrim + spinner + progress. Use for operations the user
   started and must wait for (downloading originals). Not for page loads. */
.nc-wait { position: fixed; inset: 0; z-index: var(--nc-z-modal); display: grid; place-items: center; background: var(--nc-scrim); -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px); }
.nc-wait__card { width: min(420px, 90vw); display: grid; gap: var(--nc-s-3); justify-items: center; padding: var(--nc-s-7) var(--nc-s-8); text-align: center; border-radius: var(--nc-r-xl); background: var(--nc-surface-2); box-shadow: var(--nc-edge), var(--nc-shadow-3); }
.nc-wait__title { font-size: var(--nc-fs-4); font-weight: 650; }
.nc-wait__text { margin: 0; min-height: 1.4em; color: var(--nc-text-2); font-size: var(--nc-fs-2); }
.nc-wait__card .nc-progress { width: 100%; }
.nc-wait__foot { color: var(--nc-text-3); font-size: var(--nc-fs-1); }

/* Inline notice inside a workspace: info by default, --danger for errors. */
.nc-notice { margin: var(--nc-s-4) var(--nc-s-5) 0; padding: var(--nc-s-3) var(--nc-s-4); border-radius: var(--nc-r-sm); background: var(--nc-surface-2); box-shadow: inset 0 0 0 1px var(--nc-line); color: var(--nc-text-2); font-size: var(--nc-fs-2); }
.nc-notice--danger { color: var(--nc-danger); box-shadow: inset 0 0 0 1px var(--nc-danger-soft); }
.nc-notice--warning { color: var(--nc-warning); box-shadow: inset 0 0 0 1px var(--nc-warning); }
.nc-notice strong { color: inherit; }
.nc-notice .nc-btn { margin-left: auto; }
.nc-notice code { color: var(--nc-text); font-family: var(--nc-font-mono); }

/* The scrim behind the rail-as-drawer. Inert above the breakpoint, so it can
   live in the markup permanently; the app toggles [open] on it and the rail
   together. Clicking it closes the drawer. */
.nc-split__scrim { display: none; }

@media (max-width: 860px) {
  .nc-split { grid-template-columns: minmax(0, 1fr); }
  /* Por encima del dock: el cajón tapa la bandeja y cualquier barra sticky del
     contenido, que es lo que significa ser un cajón. */
  .nc-split__rail { position: fixed; left: 0; top: 0; bottom: 0; z-index: var(--nc-z-popover); width: min(320px, 86vw); transform: translateX(-100%); transition: transform var(--nc-t-slow); box-shadow: var(--nc-shadow-3); }
  .nc-split__rail[open], .nc-split__rail.is-open { transform: none; }
  .nc-split__scrim { position: fixed; inset: 0; z-index: calc(var(--nc-z-popover) - 1); display: block; background: var(--nc-scrim); opacity: 0; pointer-events: none; transition: opacity var(--nc-t-slow); }
  .nc-split__scrim[open], .nc-split__scrim.is-open { opacity: 1; pointer-events: auto; }
  .nc-appbar { padding-left: var(--nc-s-4); padding-right: var(--nc-s-4); }
}


/* ============================================================
   NOCTURNE UI · 20 · COLOR & GRADIENT
   The fill editor of a graphic app, sized for a narrow inspector
   (288px panel ≈ 256px of usable width).
   .nc-swatch    ·  a real <input type="color">, restyled
   .nc-gradient  ·  read-only preview of the fill being edited
   .nc-stop      ·  one gradient stop: colour, opacity, position, remove
   ============================================================ */

/* --- colour swatch ---------------------------------------------
   A native colour input, so the OS picker, the keyboard and form
   submission all come for free. The ring is drawn on the swatch
   pseudo-element, not on the host: an inset shadow on the <input>
   would be painted under the colour and never show.
   ---------------------------------------------------------------- */
.nc-swatch {
  -webkit-appearance: none; appearance: none;
  flex: none;
  width: var(--nc-swatch-w, 26px); height: var(--nc-swatch-h, 24px);
  padding: 0;
  border: 0;
  border-radius: var(--nc-r-xs);
  background: transparent;
  cursor: pointer;
  transition: box-shadow var(--nc-t);
}
.nc-swatch::-webkit-color-swatch-wrapper { padding: 0; border-radius: inherit; }
.nc-swatch::-webkit-color-swatch { border: 0; border-radius: inherit; box-shadow: var(--nc-edge); }
.nc-swatch::-moz-color-swatch { border: 0; border-radius: inherit; box-shadow: var(--nc-edge); }
.nc-swatch:hover { box-shadow: var(--nc-lift); }
.nc-swatch:focus-visible { outline: 2px solid var(--nc-accent); outline-offset: 2px; }
.nc-swatch:disabled { opacity: .42; pointer-events: none; }

.nc-swatch--sm { --nc-swatch-w: 20px; --nc-swatch-h: 20px; }
.nc-swatch--lg { --nc-swatch-w: 32px; --nc-swatch-h: 32px; border-radius: var(--nc-r-sm); }
.nc-swatch--round { --nc-swatch-w: 24px; border-radius: var(--nc-r-pill); }

/* --- gradient / fill preview ------------------------------------
   Read-only on purpose: the stops are edited in the rows under it.
   The checkerboard shows through wherever the fill is transparent.
   ---------------------------------------------------------------- */
.nc-gradient {
  display: block;
  width: 100%;
  height: var(--nc-gradient-h, 28px);
  border-radius: var(--nc-r-sm);
  background-image:
    var(--nc-gradient, linear-gradient(90deg, var(--nc-accent), transparent)),
    repeating-conic-gradient(var(--nc-checker-1) 0% 25%, var(--nc-checker-2) 0% 50%);
  background-size: auto, 10px 10px;
  box-shadow: inset 0 0 0 1px var(--nc-line);
}
.nc-gradient--tall { --nc-gradient-h: 44px; border-radius: var(--nc-r-md); }
.nc-gradient--pill { border-radius: var(--nc-r-pill); }

/* --- fill trigger ------------------------------------------------
   The preview IS the way in: in a narrow inspector a bar plus an
   "Edit" button under it says the same thing twice. Wrap the bar in a
   button; the chevron says "this opens", the ring answers the pointer.
   It opens the fill editor (.nc-drawer); the bar itself stays read-only.
   ---------------------------------------------------------------- */
.nc-gradient-btn {
  position: relative;
  display: block; width: 100%;
  margin: 0; padding: 0;
  border: 0; border-radius: var(--nc-r-sm);
  background: none;
  cursor: pointer;
}
.nc-gradient-btn > .nc-gradient { --nc-gradient-h: 32px; transition: box-shadow var(--nc-t); }
.nc-gradient-btn:hover > .nc-gradient { box-shadow: inset 0 0 0 1px var(--nc-line-strong); }
.nc-gradient-btn:focus-visible { outline: 2px solid var(--nc-accent); outline-offset: 2px; }
.nc-gradient-btn__go {
  position: absolute; top: 50%; right: 6px;
  display: grid; place-items: center;
  width: 22px; height: 22px;
  border-radius: var(--nc-r-pill);
  background: var(--nc-glass);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  box-shadow: var(--nc-edge);
  color: var(--nc-text);
  transform: translateY(-50%);
  transition: transform var(--nc-t-fast);
  pointer-events: none;
}
.nc-gradient-btn:hover .nc-gradient-btn__go { transform: translate(2px, -50%); }
@media (prefers-reduced-motion: reduce) { .nc-gradient-btn__go, .nc-gradient-btn > .nc-gradient { transition: none; } }

/* --- gradient stop row ------------------------------------------
   Four columns that survive 256px: swatch · opacity · position ·
   remove. The two numbers share the leftover width evenly.
   ---------------------------------------------------------------- */
.nc-stop-list { display: flex; flex-direction: column; gap: var(--nc-s-1); min-width: 0; }

.nc-stop {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--nc-s-2);
  min-width: 0;
  padding: 3px;
  border-radius: var(--nc-r-sm);
  transition: background var(--nc-t-fast), box-shadow var(--nc-t-fast);
}
.nc-stop:hover { background: var(--nc-raise); }
.nc-stop:focus-within { background: var(--nc-raise); }
.nc-stop[aria-selected="true"],
.nc-stop.is-selected {
  background: var(--nc-accent-soft);
  box-shadow: inset 0 0 0 1px var(--nc-accent);
}
/* Two stops make a gradient, so the last two cannot be removed: the app
   drops the button instead of disabling it, and this keeps the columns. */
.nc-stop__spacer { width: var(--nc-h-sm); height: 1px; }


/* ============================================================
   NOCTURNE UI · 21 · OVERLAY DRAWER
   .nc-drawer  ·  a panel that slides IN OVER another panel and
   covers it whole: the gradient editor over the inspector, the
   layer list over the properties. Different job from
   .nc-split__rail, which is the left rail turning into an
   off-canvas drawer under 860px (§4.31).

   It is a native <dialog> so the phone case is a real modal:
     desktop  el.show()       — non-modal, absolute inside its host,
                                the rest of the editor stays usable
     ≤ 860px  el.showModal()  — modal, focus trapped, Esc closes,
                                and it rises from the bottom edge
   ============================================================ */

.nc-drawer-host { position: relative; overflow: hidden; }

.nc-drawer {
  position: absolute;
  inset: 0;
  display: none;
  width: 100%; max-width: none;
  height: 100%; max-height: none;
  margin: 0; padding: 0;
  border: 0; border-radius: 0;
  background: var(--nc-surface-1);
  color: var(--nc-text);
  font-family: var(--nc-font);
  font-size: var(--nc-fs-3);
  box-shadow: inset 1px 0 0 var(--nc-line), var(--nc-shadow-3);
  overflow: hidden;
  z-index: var(--nc-z-popover);
}
.nc-drawer[open],
.nc-drawer.is-open {
  display: flex;
  flex-direction: column;
  animation: nc-drawer-in var(--nc-t-slow);
}
.nc-drawer--left { box-shadow: inset -1px 0 0 var(--nc-line), var(--nc-shadow-3); }
.nc-drawer--left[open],
.nc-drawer--left.is-open { animation-name: nc-drawer-in-left; }

@keyframes nc-drawer-in      { from { transform: translateX(100%) }  to { transform: none } }
@keyframes nc-drawer-in-left { from { transform: translateX(-100%) } to { transform: none } }
@keyframes nc-drawer-in-up   { from { transform: translateY(100%) }  to { transform: none } }

/* Only paints when the dialog was opened with showModal(). */
.nc-drawer::backdrop {
  background: var(--nc-scrim);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}

.nc-drawer__header {
  display: flex; align-items: center; gap: var(--nc-s-2);
  flex: none;
  padding: var(--nc-s-3) var(--nc-s-3) var(--nc-s-3) var(--nc-s-4);
  box-shadow: inset 0 -1px 0 var(--nc-line);
}
.nc-drawer__title {
  flex: 1 1 auto; min-width: 0;
  margin: 0;
  font-size: var(--nc-fs-3); font-weight: 650; color: var(--nc-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nc-drawer__title small { margin-left: var(--nc-s-2); color: var(--nc-text-3); font-size: var(--nc-fs-1); font-weight: 400; }
.nc-drawer__body {
  flex: 1 1 auto; min-height: 0;
  padding: var(--nc-s-4);
  overflow: auto;
}
.nc-drawer__body--flush { padding: 0; }
.nc-drawer__footer {
  display: flex; align-items: center; gap: var(--nc-s-3);
  flex: none;
  padding: var(--nc-s-3) var(--nc-s-4);
  border-top: 1px solid var(--nc-line);
}
.nc-drawer__footer .nc-spacer { margin-right: auto; }

/* A MODAL drawer is a sheet, at any width: once it is in the top layer the
   browser positions it against the viewport, not against the host panel, so
   it has to stop pretending to be a panel. This is the phone case, and it is
   also what saves an app that calls showModal() on a wide screen by mistake. */
.nc-drawer:modal {
  position: fixed;
  inset: auto 0 0 0;
  width: 100%;
  height: auto; max-height: min(86dvh, 640px);
  border-radius: var(--nc-r-xl) var(--nc-r-xl) 0 0;
  box-shadow: var(--nc-shadow-3);
  animation-name: nc-drawer-in-up;
}
.nc-drawer:modal .nc-drawer__header { padding-top: var(--nc-s-4); }

/* Same geometry for a non-modal drawer that ends up on a phone anyway.
   Open it with showModal() there and you also get focus and Escape. */
@media (max-width: 860px) {
  .nc-drawer {
    position: fixed;
    inset: auto 0 0 0;
    width: 100%;
    height: auto; max-height: min(86dvh, 640px);
    border-radius: var(--nc-r-xl) var(--nc-r-xl) 0 0;
    box-shadow: var(--nc-shadow-3);
  }
  .nc-drawer[open],
  .nc-drawer.is-open,
  .nc-drawer--left[open],
  .nc-drawer--left.is-open { animation-name: nc-drawer-in-up; }
  .nc-drawer__header { padding-top: var(--nc-s-4); }
}


/* ============================================================
   NOCTURNE UI · 22 · CHANNEL PICKER
   .nc-channels  ·  pick one image channel (luminance, R, G, B)
   ------------------------------------------------------------
   Four small rings in the channel's own colour instead of a
   segmented control with "Luz | R | G | B" spelled out. The colour
   already says which channel it is, so no word is needed, and it
   fits on the header row of the histogram or curve it drives.
   The ring fills in when the channel is picked.
   Built for: Studio · Canvas histogram (Oct 2026).
   ============================================================ */
.nc-channels {
  display: inline-flex; align-items: center; gap: 2px;
  flex: none;
}
.nc-channel {
  --nc-ch: var(--nc-channel-luma);
  position: relative;
  display: inline-grid; place-items: center;
  width: 24px; height: 24px;
  padding: 0;
  border: 0;
  border-radius: var(--nc-r-pill);
  background: transparent;
  cursor: pointer;
  transition: background var(--nc-t);
}
/* the mark: a ring that becomes a disc */
.nc-channel::before {
  content: "";
  width: 12px; height: 12px;
  border-radius: var(--nc-r-pill);
  box-shadow: inset 0 0 0 1.5px var(--nc-ch);
  opacity: .78;
  transition: box-shadow var(--nc-t), opacity var(--nc-t), transform var(--nc-t);
}
.nc-channel:hover { background: var(--nc-raise-hover); }
.nc-channel:hover::before { opacity: 1; }
.nc-channel[aria-pressed="true"]::before,
.nc-channel.is-active::before {
  opacity: 1;
  box-shadow: inset 0 0 0 6px var(--nc-ch), 0 0 0 2.5px color-mix(in srgb, var(--nc-ch) 28%, transparent);
}
.nc-channel:active::before { transform: scale(.9); }
.nc-channel:focus-visible { outline: 2px solid var(--nc-accent); outline-offset: 1px; }
.nc-channel:disabled { opacity: .42; pointer-events: none; }

.nc-channel--r { --nc-ch: var(--nc-channel-r); }
.nc-channel--g { --nc-ch: var(--nc-channel-g); }
.nc-channel--b { --nc-ch: var(--nc-channel-b); }
/* luminance reads as light, not as white: a dark-to-light disc */
.nc-channel--luma::before { background: linear-gradient(135deg, transparent 50%, color-mix(in srgb, var(--nc-ch) 45%, transparent) 50%); }
.nc-channel--luma[aria-pressed="true"]::before,
.nc-channel--luma.is-active::before { background: none; }

@media (prefers-reduced-motion: reduce) {
  .nc-channel, .nc-channel::before { transition: none; }
  .nc-channel:active::before { transform: none; }
}


/* ============================================================
   NOCTURNE UI · 23 · ICON SET & ANGLE DIAL
   .nc-iconset  ·  pick one of a few icon-only options (alignment)
   .nc-angle    ·  set a direction by turning a small dial
   ------------------------------------------------------------
   Both replace a segmented control where the track added nothing:
   loose icons for a choice the icons already explain, and a dial
   for a direction, which is a turn, not a list of six arrows.
   Built for: Studio · Canvas text and gradient (Oct 2026).
   ============================================================ */

/* --- icon set ---------------------------------------------------
   No track behind the row. The picked icon gets a raised chip and
   full text colour; the others sit at --nc-text-3 until hovered.
   Not the accent: picking an alignment is a setting, not the one
   action of the screen. */
.nc-iconset {
  display: inline-flex; align-items: center; gap: 2px;
  flex: none;
}
.nc-iconset__item {
  display: inline-grid; place-items: center;
  width: 28px; height: 28px;
  padding: 0;
  border: 0;
  border-radius: var(--nc-r-xs);
  background: transparent;
  color: var(--nc-text-3);
  cursor: pointer;
  transition: background var(--nc-t), color var(--nc-t), box-shadow var(--nc-t);
}
.nc-iconset__item:hover { color: var(--nc-text); background: var(--nc-raise); }
.nc-iconset__item[aria-pressed="true"],
.nc-iconset__item.is-active { color: var(--nc-text); background: var(--nc-raise-press); box-shadow: var(--nc-edge); }
.nc-iconset__item:focus-visible { outline: 2px solid var(--nc-accent); outline-offset: 1px; }
.nc-iconset__item:disabled { opacity: .42; pointer-events: none; }
.nc-iconset__item .nc-i { width: 15px; height: 15px; }

/* --- angle dial -------------------------------------------------
   role="slider", 0–359, CSS convention (0 = up, clockwise). The
   needle points where the direction goes; eight faint ticks mark
   the 45° stops. Turning is yours (≈20 lines, see AGENTS.md): set
   --nc-angle and aria-valuenow on the host. Pair it with a
   .nc-num showing the degrees so the exact value can be typed. */
.nc-angle {
  --nc-angle: 0deg;
  --nc-angle-size: 32px;
  position: relative;
  flex: none;
  width: var(--nc-angle-size); height: var(--nc-angle-size);
  border-radius: var(--nc-r-pill);
  /* the hub is painted, not a pseudo-element: ::after stays free for [data-nc-tip] */
  background: radial-gradient(circle, var(--nc-text-3) 0 2px, transparent 2.5px), var(--nc-inset);
  box-shadow: inset 0 0 0 1px var(--nc-line);
  cursor: grab;
  touch-action: none;
  transition: box-shadow var(--nc-t);
}
.nc-angle::before {           /* ticks every 45° */
  content: "";
  position: absolute; inset: 2px;
  border-radius: inherit;
  background: repeating-conic-gradient(from -2deg, var(--nc-text-3) 0 4deg, transparent 4deg 45deg);
  -webkit-mask: radial-gradient(circle, transparent 62%, #000 63%);
          mask: radial-gradient(circle, transparent 62%, #000 63%);
  opacity: .45;
}
.nc-angle__needle {
  position: absolute; left: 50%; top: 50%;
  width: 2px; height: 42%;
  margin-left: -1px;
  border-radius: 1px;
  background: var(--nc-text);
  transform-origin: 50% 0;
  transform: rotate(calc(var(--nc-angle) + 180deg));
}
.nc-angle__needle::after {    /* the end the direction goes to */
  content: "";
  position: absolute; left: 50%; bottom: -3px;
  width: 6px; height: 6px; margin-left: -3px;
  border-radius: var(--nc-r-pill);
  background: var(--nc-text);
}
.nc-angle:hover { box-shadow: inset 0 0 0 1px var(--nc-line-strong); }
.nc-angle.is-turning { cursor: grabbing; box-shadow: inset 0 0 0 1px var(--nc-accent); }
.nc-angle:focus-visible { outline: 2px solid var(--nc-accent); outline-offset: 2px; }
.nc-angle[aria-disabled="true"] { opacity: .42; pointer-events: none; }
.nc-angle--sm { --nc-angle-size: 26px; }

@media (prefers-reduced-motion: reduce) {
  .nc-iconset__item, .nc-angle { transition: none; }
}


/* =================================================================
   Crosshair — .nc-crosshair
   A 2D point on top of an image or a video frame: the origin of a
   generated background, a light, a zoom centre. The app positions it
   (left/top in % of the frame it sits on) and drags it; the kit only
   draws it. Rule 8: never paint one the app does not move.
   ================================================================= */
.nc-crosshair {
  --nc-crosshair-size: 28px;
  position: absolute;
  width: var(--nc-crosshair-size); height: var(--nc-crosshair-size);
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 1.5px solid #fff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .55), inset 0 0 0 1px rgba(0, 0, 0, .35);
  background:
    linear-gradient(#fff, #fff) center / 1.5px 100% no-repeat,
    linear-gradient(#fff, #fff) center / 100% 1.5px no-repeat;
  cursor: move;
  touch-action: none;
  pointer-events: auto;
}
.nc-crosshair::after {
  content: ""; position: absolute; inset: -10px; border-radius: 50%;
}
.nc-crosshair:hover { box-shadow: 0 0 0 1px rgba(0, 0, 0, .55), 0 0 0 5px var(--nc-accent-soft, rgba(139, 124, 255, .3)); }
.nc-crosshair.is-dragging { cursor: grabbing; box-shadow: 0 0 0 1px rgba(0, 0, 0, .55), 0 0 0 6px var(--nc-accent-soft, rgba(139, 124, 255, .4)); }
.nc-crosshair:focus-visible { outline: 2px solid var(--nc-accent); outline-offset: 4px; }
.nc-crosshair--sm { --nc-crosshair-size: 20px; }


