/* pixel-native — "console OS" theme, v2.
   The app is styled as a small retro operating system: every section is a
   window with a striped title bar, controls are chunky 3D-bevelled pixel
   widgets, and the canvas sits in a HUD frame. Deep blue is the ground,
   acid green is the action colour, acid pink is the highlight colour.
   One deliberate dark theme (no light variant): the canvas checkerboard
   and palette swatches need a stable dark surround to read colours
   against. */

@font-face {
  font-family: "Press Start 2P";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("./fonts/press-start-2p-latin.woff2") format("woff2");
}

:root {
  color-scheme: dark;
  --ink: #04081f;            /* page background, deepest blue */
  --blue-deep: #060b2a;
  --panel: #0a1134;          /* window surface */
  --panel-2: #0f1a4d;        /* raised surface */
  --border: #24347a;
  --border-bright: #3d55c4;
  --text: #dfe6ff;
  --muted: #8b98d4;
  --green: #9dff00;          /* acid green — primary action */
  --green-lite: #d9ff7a;     /* bevel light edge on green */
  --green-shade: #5c9a00;    /* bevel dark edge on green */
  --green-dim: rgba(157, 255, 0, 0.14);
  --pink: #ff2ec4;           /* acid pink — highlight / secondary */
  --pink-lite: #ff8ade;
  --pink-shade: #b3128a;
  --pink-dim: rgba(255, 46, 196, 0.14);
  --danger: #ff4d6d;
  --danger-tint: rgba(255, 77, 109, 0.12);
  --warning: #ffd23e;
  --warning-tint: rgba(255, 210, 62, 0.1);
  --font-pixel: "Press Start 2P", "Courier New", monospace;
  --font-mono: "JetBrains Mono", ui-monospace, "Cascadia Mono", "Fira Code",
    Menlo, Consolas, monospace;
  /* Raised / pressed 3D bevels for blue widgets */
  --bevel-up: inset -2px -2px 0 0 rgba(2, 4, 16, 0.65),
    inset 2px 2px 0 0 rgba(130, 150, 230, 0.35);
  --bevel-down: inset 2px 2px 0 0 rgba(2, 4, 16, 0.65),
    inset -2px -2px 0 0 rgba(130, 150, 230, 0.25);
}

* { box-sizing: border-box; }

/* The `hidden` attribute must always win over any component's own
   `display` declaration, or JS-driven show/hide breaks silently. */
[hidden] { display: none !important; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 15px;
  background: var(--ink);
  color: var(--text);
  line-height: 1.5;
}

/* Background is dogfooded through pixel_native's own converter (the
   "photo" preset: k-centroid + oklab + outline=1, keep_background=True,
   since it's a full scene with no backdrop to remove) — bg-desktop.png is
   480x268 / 32 colours, bg-mobile.png 360x782 / 32 colours, swapped below
   at the same breakpoint the layout itself uses.

   This is a dedicated position:fixed layer, not background-attachment:
   fixed on body — that property is unreliable on long/tall pages (some
   engines clip it to the first viewport and show flat colour past that
   point, which is exactly the "turns black partway down" bug this
   replaced). A real fixed-position element behind everything always
   covers the full viewport at any scroll position.

   Height is pinned to 100lvh (the *large* viewport height) rather than
   inset:0, which resolves against the *dynamic* viewport. On mobile the
   address bar hides once you scroll past a threshold; that shrinks the
   dynamic viewport in a single step, and a cover/center-bottom image tied
   to it snaps to a new size and position all at once — the background
   visibly jumps at that one scroll height. Sizing to the large viewport
   locks the layer to the tallest state so browser-chrome show/hide never
   reflows it. (vh is the pre-lvh fallback for older engines.)

   Darkened and desaturated (filter, on the image layer only) plus a
   vignette (::after, a plain gradient — kept off the filtered layer so
   it doesn't get double-darkened) so it reads as quiet atmosphere behind
   the opaque panels rather than a competing, distracting photo. */
body::before {
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 100vh;
  height: 100lvh;
  z-index: -2;
  background: url("./bg-desktop.png") center bottom / cover no-repeat;
  image-rendering: pixelated;
  filter: brightness(0.4) saturate(0.65);
}
body::after {
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 100vh;
  height: 100lvh;
  z-index: -1;
  background: linear-gradient(
    180deg,
    rgba(4, 8, 31, 0.6) 0%,
    rgba(4, 8, 31, 0.32) 30%,
    rgba(4, 8, 31, 0.4) 70%,
    rgba(4, 8, 31, 0.68) 100%
  );
}

@media (max-width: 720px) {
  body::before { background-image: url("./bg-mobile.png"); }
}

/* Faint CRT scanlines over everything; pointer-events off so it is purely
   cosmetic. Kept very subtle so long editing sessions don't tire the eye. */
.crt-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  pointer-events: none;
  background: repeating-linear-gradient(
    0deg,
    rgba(4, 8, 31, 0) 0px,
    rgba(4, 8, 31, 0) 3px,
    rgba(4, 8, 31, 0.1) 4px
  );
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

:focus-visible {
  outline: 2px dashed var(--green);
  outline-offset: 2px;
}

::selection { background: var(--pink); color: var(--ink); }

::-webkit-scrollbar { width: 14px; height: 14px; }
::-webkit-scrollbar-track { background: var(--blue-deep); box-shadow: var(--bevel-down); }
::-webkit-scrollbar-thumb {
  background: var(--panel-2);
  border: 2px solid var(--ink);
  box-shadow: var(--bevel-up);
}
::-webkit-scrollbar-thumb:hover { background: var(--border-bright); }

@keyframes bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-4px); }
}

/* Small pink heart in the footer */
.footer-sprite { position: relative; display: inline-block; width: 24px; height: 18px; vertical-align: -3px; margin-right: 8px; }
.footer-sprite::before {
  content: "";
  position: absolute;
  top: 0; left: 0;
  width: 3px; height: 3px;
  box-shadow:
    3px 0 #ff2ec4, 6px 0 #ff2ec4, 15px 0 #ff2ec4, 18px 0 #ff2ec4,
    0 3px #ff2ec4, 3px 3px #ff8ade, 6px 3px #ff2ec4, 9px 3px #ff2ec4, 12px 3px #ff2ec4, 15px 3px #ff2ec4, 18px 3px #ff2ec4, 21px 3px #ff2ec4,
    0 6px #ff2ec4, 3px 6px #ff2ec4, 6px 6px #ff2ec4, 9px 6px #ff2ec4, 12px 6px #ff2ec4, 15px 6px #ff2ec4, 18px 6px #ff2ec4, 21px 6px #ff2ec4,
    3px 9px #ff2ec4, 6px 9px #ff2ec4, 9px 9px #ff2ec4, 12px 9px #ff2ec4, 15px 9px #ff2ec4, 18px 9px #ff2ec4,
    6px 12px #ff2ec4, 9px 12px #ff2ec4, 12px 12px #ff2ec4, 15px 12px #ff2ec4,
    9px 15px #ff2ec4, 12px 15px #ff2ec4;
}

/* ------------------------------------------------------- resume banner */
.resume-banner {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0.6rem 1rem;
  background: var(--panel-2);
  border-bottom: 2px solid var(--pink);
  font-size: 0.85rem;
}
.resume-text { color: var(--pink-lite); }
.resume-actions { display: flex; gap: 0.5rem; }

/* -------------------------------------------------------------- header */
.site-header {
  padding: 2.2rem 2rem 0.75rem;
  max-width: 1160px;
  margin: 0 auto;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}
.brand { display: flex; align-items: center; gap: 1.1rem; }
.brand-mark { flex-shrink: 0; filter: drop-shadow(3px 3px 0 rgba(2, 4, 16, 0.7)); }
/* The logo is a two-armed pixel vortex — one green arm, one pink, built from
   2px cells (shape-rendering: crispEdges on the svg) — that ticks round in
   chunky retro steps rather than a smooth spin. */
.vortex { transform-origin: 12px 12px; animation: vortex-spin 2.4s steps(8, end) infinite; }
.vx-a { fill: var(--green); }
.vx-b { fill: var(--pink); }
@keyframes vortex-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .vortex { animation: none; } }

.brand h1 {
  margin: 0;
  font-family: var(--font-pixel);
  font-size: 1.35rem;
  letter-spacing: 0.04em;
  color: var(--text);
  /* hard drop + slight chromatic split, CRT style */
  text-shadow:
    3px 3px 0 var(--ink),
    -2px 0 0 rgba(255, 46, 196, 0.55),
    2px 0 0 rgba(157, 255, 0, 0.45);
}
.brand-accent { color: var(--green); }
.brand-nana { color: var(--pink); }
.subtitle {
  color: var(--muted);
  margin: 0.55rem 0 0;
  font-size: 0.86rem;
}
.bootline {
  margin: 0.35rem 0 0;
  font-size: 0.72rem;
  color: var(--green);
  letter-spacing: 0.08em;
}
.cursor { animation: blink 1.1s steps(1, end) infinite; }
@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }

main {
  padding: 1.4rem 2rem 3rem;
  display: flex;
  flex-direction: column;
  gap: 2rem;
  max-width: 1160px;
  margin: 0 auto;
}

.site-footer {
  max-width: 1160px;
  margin: 0 auto;
  padding: 0 2rem 2.5rem;
  color: var(--muted);
  font-size: 0.75rem;
  text-align: center;
}

/* ----------------------------------------------------- windows (panels) */
.panel {
  background: var(--panel);
  border: 2px solid var(--border-bright);
  box-shadow:
    0 0 0 2px var(--ink),
    10px 10px 0 rgba(2, 4, 16, 0.55);
  animation: rise 0.25s ease both;
}

@keyframes rise {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}

.window-bar {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  padding: 0.55rem 0.8rem;
  background: linear-gradient(180deg, #142465, #0b1440);
  border-bottom: 2px solid var(--border-bright);
}
.window-bar h2 {
  margin: 0;
  font-family: var(--font-pixel);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}
/* Classic title-bar drag stripes filling the empty middle */
.bar-stripes {
  flex: 1;
  height: 12px;
  margin: 0 0.4rem;
  background: repeating-linear-gradient(
    0deg,
    var(--border) 0 2px,
    transparent 2px 4px
  );
}
.win-dots { display: flex; gap: 5px; }
.win-dots i {
  width: 11px;
  height: 11px;
  border: 2px solid var(--ink);
  display: inline-block;
}
.win-dots .d-green { background: var(--green); }
.win-dots .d-pink { background: var(--pink); }
.win-dots .d-blue { background: var(--border-bright); }

.panel-body { padding: 1.4rem 1.6rem 1.6rem; }

.step-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-pixel);
  font-size: 0.6rem;
  width: 1.9rem;
  height: 1.9rem;
  background: var(--green);
  color: var(--ink);
  flex-shrink: 0;
  border: 2px solid var(--ink);
  box-shadow: inset -2px -2px 0 var(--green-shade), inset 2px 2px 0 var(--green-lite);
}
.step-badge-pink {
  background: var(--pink);
  box-shadow: inset -2px -2px 0 var(--pink-shade), inset 2px 2px 0 var(--pink-lite);
}

/* --------------------------------------------------------- form fields */
.field-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 1.4rem;
  margin: 0.9rem 0;
  align-items: flex-end;
}
.field-row label {
  display: flex;
  flex-direction: column;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
  gap: 0.35rem;
  font-weight: 600;
}
label.checkbox {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.55rem;
  color: var(--text);
  font-size: 0.82rem;
  text-transform: none;
  letter-spacing: 0;
  cursor: pointer;
  user-select: none;
  padding-bottom: 0.4rem;
}

/* Custom pixel checkbox: bevelled well, acid-green pixel check when on */
label.checkbox input {
  appearance: none;
  -webkit-appearance: none;
  width: 19px;
  height: 19px;
  margin: 0;
  flex-shrink: 0;
  background: var(--blue-deep);
  border: 2px solid var(--border-bright);
  box-shadow: var(--bevel-down);
  cursor: pointer;
}
label.checkbox input:checked {
  background-color: var(--green);
  border-color: var(--ink);
  box-shadow: inset -2px -2px 0 var(--green-shade), inset 2px 2px 0 var(--green-lite);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cg fill='%2304081f'%3E%3Crect x='2' y='7' width='3' height='3'/%3E%3Crect x='5' y='10' width='3' height='3'/%3E%3Crect x='8' y='7' width='3' height='3'/%3E%3Crect x='11' y='4' width='3' height='3'/%3E%3C/g%3E%3C/svg%3E");
  background-size: 13px 13px;
  background-position: center;
  background-repeat: no-repeat;
}

input[type="number"], select {
  padding: 0.45rem 0.6rem;
  border: 2px solid var(--border);
  border-radius: 0;
  background-color: var(--blue-deep);
  color: var(--text);
  width: 9rem;
  font-family: var(--font-mono);
  font-size: 0.88rem;
  box-shadow: var(--bevel-down);
  transition: border-color 0.15s;
}
input[type="number"]:focus, select:focus {
  outline: none;
  border-color: var(--green);
}
input:disabled { opacity: 0.45; }

/* Strip the native number spinners; the theme's inputs are plain wells. */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
input[type="number"] { -moz-appearance: textfield; appearance: textfield; }

/* Custom pixel dropdown arrow */
select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 30px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cg fill='%239dff00'%3E%3Crect x='3' y='5' width='10' height='2'/%3E%3Crect x='5' y='7' width='6' height='2'/%3E%3Crect x='7' y='9' width='2' height='2'/%3E%3C/g%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
  background-size: 15px 15px;
}
select option { background: var(--blue-deep); }

/* ------------------------------------------------------------ dropzone */
/* Empty state: the dropzone fills the Start panel as a tall, centred target
   so the panel doesn't open as a thin, bare strip. Once an image is chosen
   (app.js adds .has-file) it collapses to a compact bar to make room for the
   size / style settings below. min-height and padding transition, so the
   collapse reads as a smooth shrink rather than a jump. */
.dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 1.1rem;
  min-height: clamp(16rem, 44vh, 26rem);
  border: 2px dashed var(--border-bright);
  padding: 2.4rem 1.3rem;
  cursor: pointer;
  color: var(--muted);
  background: rgba(4, 8, 31, 0.55);
  box-shadow: var(--bevel-down);
  transition: min-height 0.4s ease, padding 0.4s ease,
    border-color 0.15s, background 0.15s, color 0.15s;
}
.dropzone.has-file {
  flex-direction: row;
  justify-content: flex-start;
  text-align: left;
  gap: 1rem;
  min-height: 0;
  padding: 1.1rem 1.3rem;
}
.dropzone:hover, .dropzone.drag-over {
  border-color: var(--green);
  background: var(--green-dim);
  color: var(--green);
}
.dropzone input { display: none; }
.dropzone-copy {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
}
.dropzone.has-file .dropzone-copy {
  flex-direction: row;
  align-items: center;
}
.dz-arrow { flex-shrink: 0; animation: bob 1.2s steps(1, end) infinite; }
/* A larger arrow and heading suit the roomy empty state; they return to the
   inline size once the bar is compact. */
.dropzone:not(.has-file) .dz-arrow { width: 44px; height: 44px; }
.dropzone:not(.has-file) .dz-text strong { font-size: 0.72rem; }
.dz-text { display: flex; flex-direction: column; gap: 0.3rem; }
.dz-text strong {
  font-family: var(--font-pixel);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 400;
}
.dz-text small { font-size: 0.75rem; }
#file-thumb {
  width: 48px;
  height: 48px;
  object-fit: cover;
  border: 2px solid var(--border-bright);
  box-shadow: 3px 3px 0 rgba(2, 4, 16, 0.6);
  background: var(--blue-deep);
  image-rendering: auto;
}

/* -------------------------------------------------------- preset chips */
.preset-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin: 1rem 0 0.2rem;
}
.preset-label {
  font-family: var(--font-pixel);
  font-size: 0.55rem;
  color: var(--muted);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-right: 0.2rem;
}
.chip {
  font-family: var(--font-pixel);
  font-size: 0.55rem;
  letter-spacing: 0.05em;
  /* Press Start 2P glyphs fill the em box, so wrapped labels need generous
     leading or the two lines touch. */
  line-height: 1.65;
  text-transform: uppercase;
  padding: 0.55rem 0.7rem 0.45rem;
  background: var(--panel-2);
  color: var(--muted);
  border: 2px solid var(--ink);
  box-shadow: var(--bevel-up), 2px 2px 0 rgba(2, 4, 16, 0.55);
  cursor: pointer;
  transition: color 0.12s, background 0.12s;
}
.chip:hover { color: var(--text); }
.chip:active { transform: translate(1px, 1px); }
.chip.active {
  color: var(--ink);
  background: var(--pink);
  box-shadow: inset 2px 2px 0 var(--pink-shade), inset -2px -2px 0 var(--pink-lite);
}

/* ---------------------------------------------------- start (panel 01) */
/* Two equal tabs — "Pick an image" and "Open a saved sprite" — sit at the
   top so both ways to begin are visible from the first glance. */
.start-tabs { margin-bottom: 1.1rem; }
.start-tabs .seg { flex: 1; text-align: center; }

/* Everything below the dropzone stays hidden until an image is chosen
   (progressive disclosure), then appears grouped into labelled sections. */
.settings-group {
  border: 2px solid var(--border);
  background: rgba(4, 8, 31, 0.4);
  padding: 0.3rem 1rem 1rem;
  margin-top: 1.1rem;
}
.settings-group > legend {
  font-family: var(--font-pixel);
  font-size: 0.55rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--green);
  padding: 0 0.5rem;
}
/* Keep controls bottom-aligned on the input line so the "keep square" and
   "remove background" checkboxes line up with the fields beside them; any
   helper text lives on its own line under the row, never inside a label. */
.settings-group .field-row { margin: 0.6rem 0; align-items: flex-end; }
.settings-advanced > summary { margin: 0.5rem 0; }
.field-hint {
  display: block;
  font-size: 0.72rem;
  font-weight: 400;
  color: var(--muted);
  text-transform: none;
  letter-spacing: 0;
  margin-top: 0.3rem;
}
/* The custom-size toggle sits above the (hidden-by-default) size box. */
.size-toggle { margin-top: 0.5rem; margin-bottom: 0.7rem; }
/* Appears beside the size field when several images are queued and a custom
   size is toggled on: that one size applies to every image. */
.size-shared-note {
  margin: 0.55rem 0 0;
  padding: 0.4rem 0.6rem;
  font-size: 0.72rem;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--green);
  background: var(--green-dim);
  border: 2px solid var(--green);
  box-shadow: var(--bevel-up);
}
/* The primary action is anchored to the bottom-right, at the end of the
   user's downward journey through the settings. */
.convert-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 1.3rem;
}

/* ------------------------------------------------------------- details */
details summary {
  cursor: pointer;
  color: var(--muted);
  margin: 0.8rem 0 0.6rem;
  font-family: var(--font-pixel);
  font-size: 0.58rem;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  list-style: none;
}
details summary::-webkit-details-marker { display: none; }
details summary::before { content: "▸ "; color: var(--pink); }
details[open] summary::before { content: "▾ "; }
details summary:hover, details[open] summary { color: var(--green); }

/* ------------------------------------------------------------- buttons */
.btn {
  border: 2px solid var(--ink);
  border-radius: 0;
  padding: 0.7rem 1.2rem 0.6rem;
  font-family: var(--font-pixel);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  transition: background 0.12s, transform 0.06s, color 0.12s;
}
.btn-primary {
  background: var(--green);
  color: var(--ink);
  box-shadow: inset -3px -3px 0 var(--green-shade), inset 3px 3px 0 var(--green-lite),
    4px 4px 0 rgba(2, 4, 16, 0.6);
}
.btn-primary:hover { background: #b4ff2e; }
.btn-primary:active {
  transform: translate(3px, 3px);
  box-shadow: inset 3px 3px 0 var(--green-shade), inset -3px -3px 0 var(--green-lite);
}
.btn-secondary {
  background: var(--pink);
  color: var(--ink);
  box-shadow: inset -3px -3px 0 var(--pink-shade), inset 3px 3px 0 var(--pink-lite),
    4px 4px 0 rgba(2, 4, 16, 0.6);
}
.btn-secondary:hover { background: #ff54cf; }
.btn-secondary:active {
  transform: translate(3px, 3px);
  box-shadow: inset 3px 3px 0 var(--pink-shade), inset -3px -3px 0 var(--pink-lite);
}
.btn-ghost {
  background: var(--panel-2);
  border-color: var(--ink);
  color: var(--text);
  box-shadow: var(--bevel-up), 3px 3px 0 rgba(2, 4, 16, 0.5);
}
.btn-ghost:hover { color: var(--green); }
.btn-ghost:active { transform: translate(2px, 2px); box-shadow: var(--bevel-down); }
.btn-sm { padding: 0.5rem 0.7rem 0.4rem; font-size: 0.55rem; }
.btn-cta { padding: 0.85rem 1.6rem 0.75rem; font-size: 0.7rem; }
.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}

/* Pixel spinner: a single 4px block orbiting a 2x2 course in hard steps */
.spinner { position: relative; width: 12px; height: 12px; }
.spinner::before {
  content: "";
  position: absolute;
  width: 5px;
  height: 5px;
  background: currentColor;
  animation: orbit 0.55s steps(1, end) infinite;
}
@keyframes orbit {
  0% { transform: translate(0, 0); }
  25% { transform: translate(7px, 0); }
  50% { transform: translate(7px, 7px); }
  75% { transform: translate(0, 7px); }
  100% { transform: translate(0, 0); }
}

/* ------------------------------------------------------------- banners */
.banner {
  border-radius: 0;
  border: 2px solid;
  padding: 0.6rem 0.85rem;
  font-size: 0.85rem;
  margin-top: 1rem;
  list-style: none;
}
.banner-error {
  background: var(--danger-tint);
  border-color: var(--danger);
  color: var(--danger);
  font-weight: 600;
}
.banner-warning {
  background: var(--warning-tint);
  border-color: var(--warning);
  color: var(--warning);
}
.banner-warning li { margin-left: 1rem; }

.hint {
  color: var(--muted);
  font-size: 0.8rem;
  margin-top: 0;
  background: rgba(4, 8, 31, 0.55);
  border-left: 4px solid var(--border-bright);
  padding: 0.6rem 0.85rem;
}
.hint-inline { border-left-color: var(--pink); margin-bottom: 0.4rem; }
.hint-side { margin-top: 0.8rem; font-size: 0.72rem; }

/* ---------------------------------------------------- palette library */
.palette-library-controls {
  display: flex;
  align-items: flex-end;
  gap: 1rem;
  flex-wrap: wrap;
  margin: 0.6rem 0 0.8rem;
}
.palette-library-controls label {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
  font-weight: 600;
}
.palette-library-controls select { width: 16rem; max-width: 100%; }
.palette-library-preview {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  margin-bottom: 0.9rem;
  min-height: 18px;
}
.palette-preview-chip {
  width: 18px;
  height: 18px;
  border: 2px solid var(--ink);
  box-shadow: var(--bevel-up);
}
.palette-library-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* --------------------------------------------------------- compare lab */
.lab-controls {
  display: flex;
  align-items: flex-end;
  gap: 1rem;
  flex-wrap: wrap;
  margin: 1rem 0;
}
.lab-controls label {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
  font-weight: 600;
}
.lab-controls select { width: 20rem; max-width: 100%; }

.lab-results {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(185px, 1fr));
  gap: 1.1rem;
  margin-top: 1.2rem;
}
/* Each variant is a miniature window ("cartridge") */
.lab-card {
  background: var(--blue-deep);
  border: 2px solid var(--border);
  box-shadow: 0 0 0 2px var(--ink), 5px 5px 0 rgba(2, 4, 16, 0.5);
  display: flex;
  flex-direction: column;
  transition: border-color 0.12s, transform 0.08s;
}
.lab-card:hover { border-color: var(--border-bright); transform: translateY(-2px); }
.lab-card.selected { border-color: var(--green); }
.lab-card-title {
  font-family: var(--font-pixel);
  font-size: 0.52rem;
  letter-spacing: 0.05em;
  color: var(--green);
  text-transform: uppercase;
  word-break: break-word;
  padding: 0.5rem 0.6rem 0.4rem;
  background:
    repeating-linear-gradient(0deg, rgba(36, 52, 122, 0.5) 0 2px, transparent 2px 4px),
    #0b1440;
  border-bottom: 2px solid var(--border);
  order: -1; /* title strip on top, like a window bar */
}
.lab-card.selected .lab-card-title { color: var(--ink); background: var(--green); border-bottom-color: var(--ink); }
.lab-card-preview {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0.6rem;
  background:
    linear-gradient(45deg, #101a4d 25%, transparent 25%, transparent 75%, #101a4d 75%),
    linear-gradient(45deg, #101a4d 25%, transparent 25%, transparent 75%, #101a4d 75%),
    #0a102e;
  background-size: 16px 16px;
  background-position: 0 0, 8px 8px;
  box-shadow: var(--bevel-down);
  overflow: hidden;
}
.lab-card-preview canvas {
  image-rendering: pixelated;
  max-width: 100%;
  max-height: 100%;
}
.lab-card-preview .lab-error-text {
  color: var(--danger);
  font-size: 0.72rem;
  padding: 0.5rem;
  text-align: center;
}
.lab-card-meta { font-size: 0.72rem; color: var(--muted); padding: 0 0.7rem 0.4rem; }
.lab-card .btn { justify-content: center; margin: 0 0.6rem 0.6rem; }
.lab-card-status {
  font-size: 0.72rem;
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0 0.7rem 0.6rem;
}
.lab-card-status .spinner { color: var(--pink); }

/* --------------------------------------- editor extras (compare/download)
   The old standalone "Compare" and "Download" windows now live inside the
   editor as compact, collapsible tools so they don't eat vertical space. */
.editor-extras {
  margin-top: 1.1rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.editor-tool {
  border: 2px solid var(--border);
  background: var(--blue-deep);
  box-shadow: var(--bevel-up);
}
.editor-tool > summary {
  margin: 0;
  padding: 0.6rem 0.8rem 0.5rem;
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
}
.editor-tool[open] > summary { border-bottom: 2px solid var(--border); }
.editor-tool-title { color: inherit; }
.editor-tool-hint {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0;
  text-transform: none;
  color: var(--muted);
}
.editor-tool-body { padding: 0.9rem 1rem 1.1rem; }
.editor-tool-body .hint-inline { margin-top: 0; }

/* ------------------------------------------------------- editor layout */
/* Browser-style tabs above the toolbar; one per open sprite. Hidden (via the
   [hidden] attribute in app.js) for a single-document session. */
.editor-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 0.9rem;
  padding-bottom: 0.7rem;
  border-bottom: 2px solid var(--border);
}
.editor-tab {
  display: block;
  padding: 0.3rem;
  line-height: 0;
  border: 2px solid var(--border);
  background: var(--blue-deep);
  box-shadow: var(--bevel-up);
  cursor: pointer;
}
.editor-tab.active {
  border-color: var(--green);
  box-shadow: 0 0 0 2px var(--green-dim), var(--bevel-up);
}
.editor-tab-thumb {
  width: 30px;
  height: 30px;
  object-fit: contain;
  image-rendering: pixelated;
  background: var(--ink);
  border: 1px solid var(--border);
}

.editor-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 13rem;
  grid-template-areas: "toolbar toolbar" "canvas palette";
  gap: 1rem 1.4rem;
  align-items: start;
}
.toolbar-stack {
  grid-area: toolbar;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.canvas-area { grid-area: canvas; }
.palette-panel { grid-area: palette; }

@media (max-width: 720px) {
  .editor-layout {
    grid-template-columns: 1fr;
    grid-template-areas: "toolbar" "canvas" "palette";
  }
}

.toolbar {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  flex-wrap: wrap;
  padding: 0.5rem 0.6rem;
  background: var(--blue-deep);
  border: 2px solid var(--border);
  box-shadow: var(--bevel-up);
}
.toolbar .sep {
  width: 2px;
  align-self: stretch;
  background: var(--border);
  margin: 0 0.3rem;
}

/* LCD readouts (zoom level, canvas dimensions) */
.lcd {
  font-family: var(--font-pixel);
  font-size: 0.55rem;
  color: var(--green);
  background: #020617;
  border: 2px solid var(--border);
  box-shadow: inset 2px 2px 0 rgba(0, 0, 0, 0.8);
  padding: 0.42rem 0.5rem 0.3rem;
  min-width: 3.4rem;
  text-align: center;
  letter-spacing: 0.05em;
}
.lcd-pink { color: var(--pink); }

button.tool, .btn-icon {
  background: var(--panel-2);
  color: var(--text);
  border: 2px solid var(--ink);
  border-radius: 0;
  width: 2.3rem;
  height: 2.3rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: var(--bevel-up);
  transition: background 0.12s, color 0.12s;
}
button.tool svg { width: 17px; height: 17px; }
.btn-icon svg { width: 15px; height: 15px; }
button.tool:hover, .btn-icon:hover { color: var(--green); }
button.tool:active, .btn-icon:active { box-shadow: var(--bevel-down); }
button.tool.active {
  background: var(--green);
  color: var(--ink);
  box-shadow: inset 2px 2px 0 var(--green-shade), inset -2px -2px 0 var(--green-lite);
}
.btn-icon:disabled { opacity: 0.35; cursor: not-allowed; }

/* ------------------------------------------------------- reference bar */
.reference-bar {
  border-color: var(--green);
  /* Hug the controls instead of stretching full-width like its sibling
     toolbar — .toolbar-stack's flex column otherwise stretches every
     child to the row width regardless of how little content it holds. */
  align-self: flex-start;
}
.reference-label {
  font-family: var(--font-pixel);
  font-size: 0.55rem;
  color: var(--pink);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-right: 0.3rem;
}
.segmented { display: inline-flex; gap: 4px; }
.segmented .seg {
  font-family: var(--font-pixel);
  font-size: 0.52rem;
  letter-spacing: 0.05em;
  line-height: 1.6; /* wrapped labels ("Open a saved sprite") must not collide */
  text-transform: uppercase;
  padding: 0.5rem 0.65rem 0.4rem;
  background: var(--panel-2);
  color: var(--muted);
  border: 2px solid var(--ink);
  box-shadow: var(--bevel-up);
  cursor: pointer;
  transition: color 0.12s, background 0.12s;
}
.segmented .seg:hover { color: var(--text); }
.segmented .seg.active {
  background: var(--pink);
  color: var(--ink);
  box-shadow: inset 2px 2px 0 var(--pink-shade), inset -2px -2px 0 var(--pink-lite);
}

/* ------------------------------------------------------- selection bar */
/* Appears only while a selection is live; pink frame ties it to the pink
   selection overlay drawn on the canvas. */
.selection-bar {
  border-color: var(--pink);
  align-self: flex-start;
}
.selection-label {
  font-family: var(--font-pixel);
  font-size: 0.55rem;
  color: var(--green);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-right: 0.3rem;
  white-space: nowrap;
}

.slider-wrap {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.72rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-left: 0.4rem;
}
.slider-value { min-width: 2.6rem; color: var(--text); }
input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 9rem;
  height: 8px;
  background: var(--blue-deep);
  border: 2px solid var(--border);
  box-shadow: var(--bevel-down);
  outline-offset: 4px;
  cursor: pointer;
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  background: var(--pink);
  border: 2px solid var(--ink);
  box-shadow: inset -2px -2px 0 var(--pink-shade), inset 2px 2px 0 var(--pink-lite);
}
input[type="range"]::-moz-range-thumb {
  width: 12px;
  height: 12px;
  border-radius: 0;
  background: var(--pink);
  border: 2px solid var(--ink);
}

/* --------------------------------------------------------- canvas area */
.import-row {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex-wrap: wrap;
  margin-top: 0.9rem;
}

.canvas-area {
  display: flex;
  gap: 1.2rem;
  align-items: flex-start;
  min-width: 0;
}

/* HUD frame: corner brackets around the working canvas */
.canvas-frame {
  position: relative;
  padding: 9px;
  max-width: 100%;
  min-width: 0;
}
.canvas-frame::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(var(--green), var(--green)), linear-gradient(var(--green), var(--green)),
    linear-gradient(var(--pink), var(--pink)), linear-gradient(var(--pink), var(--pink)),
    linear-gradient(var(--pink), var(--pink)), linear-gradient(var(--pink), var(--pink)),
    linear-gradient(var(--green), var(--green)), linear-gradient(var(--green), var(--green));
  background-repeat: no-repeat;
  background-size:
    16px 3px, 3px 16px,   /* top-left */
    16px 3px, 3px 16px,   /* top-right */
    16px 3px, 3px 16px,   /* bottom-left */
    16px 3px, 3px 16px;   /* bottom-right */
  background-position:
    0 0, 0 0,
    100% 0, 100% 0,
    0 100%, 0 100%,
    100% 100%, 100% 100%;
}

.canvas-scroll {
  overflow: auto;
  max-width: 100%;
  max-height: 70vh;
  /* Don't let edge-of-canvas scrolls chain into rubber-banding the page
     while editing on touch devices. */
  overscroll-behavior: contain;
  border: 2px solid var(--border);
  background:
    linear-gradient(45deg, #0d1540 25%, transparent 25%, transparent 75%, #0d1540 75%),
    linear-gradient(45deg, #0d1540 25%, transparent 25%, transparent 75%, #0d1540 75%),
    #090f30;
  background-size: 16px 16px;
  background-position: 0 0, 8px 8px;
}

.canvas-stack {
  position: relative;
  display: inline-block;
}
.canvas-stack canvas {
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  image-rendering: pixelated;
}
#reference-canvas {
  pointer-events: none;
  image-rendering: auto;
}
#sprite-canvas {
  position: absolute;
  top: 0;
  left: 0;
  cursor: crosshair;
  /* One-finger touch must paint, not scroll/zoom the page — without this
     mobile browsers hijack the stroke as a pan gesture. Pan the canvas
     viewport from outside the grid (or zoom out) instead. */
  touch-action: none;
}
#sprite-canvas:focus-visible { outline-offset: -2px; }

/* Side-by-side reference: a second canvas frame that mirrors the editor's
   exactly — same size, same zoom — so a pixel here maps to the pixel there.
   In this mode the whole layout goes single-column and the palette drops
   below both frames (see .editor-layout.side-mode) to make room. */
.editor-layout.side-mode {
  grid-template-columns: 1fr;
  grid-template-areas: "toolbar" "canvas" "palette";
}
.editor-layout.side-mode .canvas-frame {
  flex: 1 1 0;
  min-width: 0;
}
.reference-frame { position: relative; }
#reference-side-canvas {
  display: block;
  /* One image-pixel per grid cell, upscaled crisp so each cell is a block
     that lines up 1:1 with the sprite pixel beside it. */
  image-rendering: pixelated;
}
.reference-frame-tag {
  display: block;
  margin-top: 0.4rem;
  font-family: var(--font-pixel);
  font-size: 0.5rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--pink);
  text-align: center;
}

/* -------------------------------------------------------- palette panel */
.palette-panel h3 {
  margin: 0 0 0.6rem;
  font-family: var(--font-pixel);
  font-size: 0.58rem;
  letter-spacing: 0.07em;
  color: var(--muted);
  text-transform: uppercase;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}
.palette-count { color: var(--green); }
#palette-swatches {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(1.9rem, 1fr));
  gap: 0.4rem;
  margin-bottom: 0.7rem;
  max-height: 40vh;
  overflow-y: auto;
  padding: 3px;
}
.swatch {
  aspect-ratio: 1;
  width: 100%;
  border-radius: 0;
  border: 2px solid var(--ink);
  box-shadow: inset -2px -2px 0 rgba(0, 0, 0, 0.35), inset 2px 2px 0 rgba(255, 255, 255, 0.25);
  cursor: pointer;
  padding: 0;
  transition: transform 0.05s;
}
.swatch:hover { transform: translateY(-2px); }
.swatch.active {
  outline: 2px solid var(--green);
  outline-offset: 1px;
  box-shadow: inset 2px 2px 0 rgba(0, 0, 0, 0.35), inset -2px -2px 0 rgba(255, 255, 255, 0.25);
}
.swatch.transparent {
  background:
    linear-gradient(45deg, #26307055 25%, transparent 25%, transparent 75%, #26307055 75%),
    linear-gradient(45deg, #26307055 25%, transparent 25%, transparent 75%, #26307055 75%),
    var(--blue-deep);
  background-size: 8px 8px;
  background-position: 0 0, 4px 4px;
}

#add-colour-button { width: 100%; justify-content: center; }

/* ------------------------------------------------------- palette lock */
.palette-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.6rem;
}
/* The base .palette-panel h3 is a space-between flex row (count pushed to the
   far edge); inside .palette-head the lock button owns the right edge, so the
   heading reverts to inline flow — "Palette 16/255" keeps its single space. */
.palette-head h3 { margin: 0; display: block; }

.lock-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-family: var(--font-pixel);
  font-size: 0.5rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0.42rem 0.5rem 0.32rem;
  background: var(--panel-2);
  color: var(--muted);
  border: 2px solid var(--ink);
  box-shadow: var(--bevel-up);
  cursor: pointer;
  transition: color 0.12s, background 0.12s;
}
.lock-toggle:hover { color: var(--text); }
.lock-toggle .lock-ico { width: 11px; height: 11px; }
.lock-toggle .lock-closed { display: none; }
.lock-toggle.active {
  background: var(--pink);
  color: var(--ink);
  box-shadow: inset 2px 2px 0 var(--pink-shade), inset -2px -2px 0 var(--pink-lite);
}
.lock-toggle.active .lock-open { display: none; }
.lock-toggle.active .lock-closed { display: inline-block; }

/* Locked: a neon frame round the swatches so the frozen state reads at a
   glance, and the swatches stop offering their hover/edit affordances. */
.palette-panel.is-locked #palette-swatches {
  outline: 2px solid var(--pink);
  outline-offset: 3px;
}
.palette-panel.is-locked .swatch { cursor: default; }
.palette-panel.is-locked .swatch:hover { transform: none; }

/* Start-panel notice: conversions are pinned to the locked palette. */
.lock-note {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0.9rem;
  padding: 0.55rem 0.7rem;
  font-size: 0.78rem;
  color: var(--pink-lite);
  background: var(--pink-dim);
  border: 2px solid var(--pink);
  box-shadow: var(--bevel-up);
}
.lock-note .lock-ico { width: 13px; height: 13px; flex: none; color: var(--pink); }
.lock-note b { color: var(--text); }
.lock-note-unlock {
  margin-left: auto;
  flex: none;
  font-family: var(--font-pixel);
  font-size: 0.5rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0.4rem 0.5rem 0.32rem;
  background: var(--panel-2);
  color: var(--text);
  border: 2px solid var(--ink);
  box-shadow: var(--bevel-up);
  cursor: pointer;
}
.lock-note-unlock:hover { color: var(--pink); }

.kbd-grid {
  margin: 0;
  display: grid;
  gap: 0.85rem;
}
.kbd-group dt {
  margin: 0 0 0.4rem;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}
.kbd-group dd {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem 0.9rem;
}
.kbd-group dd span {
  font-size: 0.78rem;
  color: var(--muted);
  white-space: nowrap;
}
kbd {
  background: var(--blue-deep);
  border: 2px solid var(--border);
  border-bottom-width: 4px;
  border-radius: 0;
  padding: 0.1rem 0.4rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--green);
}

/* -------------------------------------------------------------- export */
.export-formats {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  margin-bottom: 1.1rem;
}
.export-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
}

/* -------------------------------------------------------------- toasts */
.toast-stack {
  position: fixed;
  bottom: 1.25rem;
  right: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  z-index: 110;
}
.toast {
  background: var(--panel-2);
  border: 2px solid var(--border-bright);
  border-left-width: 6px;
  box-shadow: 0 0 0 2px var(--ink), 4px 4px 0 rgba(2, 4, 16, 0.6);
  padding: 0.6rem 0.9rem;
  font-size: 0.82rem;
  animation: toast-in 0.2s ease both;
  max-width: 20rem;
}
.toast.toast-error { border-color: var(--danger); }
.toast.toast-success { border-color: var(--green); }
.toast.toast-out { animation: toast-out 0.2s ease both; }
@keyframes toast-in { from { opacity: 0; transform: translateY(6px); } }
@keyframes toast-out { to { opacity: 0; transform: translateY(6px); } }

@media (max-width: 720px) {
  .site-header, main { padding-left: 1rem; padding-right: 1rem; }
  .header-side { align-items: flex-start; }
  .field-row { gap: 0.7rem; }
  input[type="number"], select { width: 100%; }
  .lab-controls select { width: 100%; }
  .palette-library-controls select { width: 100%; }
  .canvas-area { flex-direction: column; }
  .editor-layout.side-mode .canvas-frame { flex: 1 1 auto; width: 100%; }
  /* Finger-sized tap targets for the editor controls. */
  button.tool, .btn-icon { width: 2.8rem; height: 2.8rem; }
  button.tool svg { width: 20px; height: 20px; }
  .chip { padding: 0.7rem 0.8rem 0.6rem; }
  .checkbox { padding: 0.25rem 0; }
}

/* Respect users who ask for less motion: freeze the decorative loops. */
@media (prefers-reduced-motion: reduce) {
  .dz-arrow, .mk-a, .mk-b, .cursor { animation: none; }
}
