/* Prop editor chrome. Layered on the shared demo-theme tokens (assets/css/
   demo-theme.css) exactly the way assets/css/weapon-editor.css is, and on the
   same eight local tokens with the same values: this page makes no colour
   decision of its own, so a theme change moves both editors together and
   neither can drift from the other.

   The reasoning behind each token is written down once, in
   assets/css/weapon-editor.css's header, and is not repeated here. The short
   version: --accent, --err and --warn are fixed because they must read the same
   in both themes, --accent-ink exists because a solid accent fill needs a
   foreground that does NOT follow var(--text), and --stage is fixed dark
   because it is art ground rather than a page surface -- prop art is drawn
   light on dark, and a pale plate under it in light theme washes it out. */
:root {
  --panel: color-mix(in srgb, var(--text) 6%, var(--bg));
  --line: color-mix(in srgb, var(--text) 16%, transparent);
  --muted: color-mix(in srgb, var(--text) 55%, transparent);
  --accent: #3e5a8e;
  --accent-ink: #eef1f8;
  --err: #e0605f;
  --warn: #d99a2b;
  --stage: #0c0d11;
}

* { box-sizing: border-box; }
/* Matches the other four editors: an author class such as .toolset sets
   display and would otherwise outrank the UA's [hidden] { display: none }, so a
   hidden panel with a layout class of its own would still show. */
[hidden] { display: none !important; }

body {
  margin: 0; font: 14px/1.4 system-ui, sans-serif;
  background: var(--bg); color: var(--text);
  height: 100vh; display: flex; flex-direction: column;
}

#bar { display: flex; align-items: center; gap: 12px; padding: 8px 12px; background: var(--panel); border-bottom: 1px solid var(--line); flex: none; }
#bar .brand { font-weight: 600; display: flex; align-items: center; gap: 6px; flex: none; }
#bar .actions { display: flex; gap: 6px; margin-left: auto; align-items: center; flex: none; }
#proplabel { opacity: .7; flex: none; }

button, select, input {
  font: inherit; color: var(--text); background: var(--panel);
  border: 1px solid var(--line); border-radius: 6px; padding: 4px 10px;
}
button, select { cursor: pointer; }
input[type=number], input[type=text] { cursor: text; }
button.primary { background: var(--accent); color: var(--accent-ink); border-color: transparent; font-weight: 600; }
button:disabled { opacity: .4; cursor: default; }
button:focus-visible, select:focus-visible, input:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px;
}

/* The prop strip: 35 buttons, each wearing the leftmost square of that prop's
   own sheet.png (see src/tools/proped/picker.js for why a background and not an
   <img>). It scrolls rather than wrapping, so a narrow window keeps the header
   one row tall instead of pushing the stage down. */
#props { display: flex; gap: 4px; align-items: center; min-width: 0; overflow-x: auto; padding: 2px; }
/* The fixed --stage plate, not the panel the strip sits in: prop art is drawn
   light on dark, and on the light theme's pale panel the same pixels read as
   grey on grey. Same decision, same reason, as .wpick in the weapon editor. */
.ppick {
  flex: none; width: 40px; height: 40px; padding: 0; position: relative;
  background-color: var(--stage);
  background-repeat: no-repeat;
  /* auto 100% with a left origin shows the sheet's leftmost square, which for
     every shipped prop (single row, square cells) is exactly frame 0. */
  background-size: auto 100%; background-position: left center;
  image-rendering: pixelated;
}
/* The id, for a prop whose sheet.png is missing or will not decode: the button
   is then an empty dark square, and a name is better than nothing. It sits
   under the art rather than instead of it, because a background image cannot
   report that it failed the way an <img> onerror can. --accent-ink rather than
   var(--text): the plate stays dark in light theme, so its foreground has to
   stay light too. */
.ppick .pname {
  position: absolute; left: 0; right: 0; bottom: 0;
  font-size: .6rem; line-height: 1.2; text-align: center;
  color: var(--accent-ink); background: color-mix(in srgb, #000 55%, transparent);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ppick[aria-current="true"] { border-color: var(--accent); box-shadow: inset 0 0 0 2px var(--accent); }

#dirty { color: var(--warn); font-size: .85rem; font-weight: 600; flex: none; }

#notice { margin: 0; padding: 6px 12px; background: var(--panel); border-bottom: 1px solid var(--line); flex: none; font-size: .85rem; }
#notice.bad { background: color-mix(in srgb, var(--err) 16%, var(--bg)); border-bottom-color: color-mix(in srgb, var(--err) 45%, transparent); color: color-mix(in srgb, var(--err) 70%, var(--text)); }

/* Three columns, the same shape assets/css/weapon-editor.css uses: the rail,
   the sheet being painted, and the live stage. The stage column is fixed at the
   canvas's own 320 backing-store pixels plus its padding, so the picture is
   presented at 1:1 rather than resampled. */
#shell { display: grid; grid-template-columns: 260px 1fr 344px; flex: 1; min-height: 0; }
#rail { background: var(--panel); padding: 10px; overflow-y: auto; border-right: 1px solid var(--line); }

.lab { font-size: .72rem; text-transform: uppercase; letter-spacing: .08em; opacity: .6; margin: 12px 0 6px; }
.grp:first-child .lab { margin-top: 0; }
.grp + .grp { border-top: 1px solid var(--line); }

/* One labelled control per field, built by buildRail() in src/tools/proped/
   form.js. A pair field is two boxes sharing one row and a colour field is a
   box and a swatch, so the row has to fit three children as well as two. */
.field { display: flex; align-items: center; gap: 8px; margin: 6px 0; }
.field > span { flex: 0 0 46%; opacity: .85; font-size: .85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.field input[type=number], .field input[type=text], .field select { flex: 1 1 0; min-width: 0; padding: 3px 6px; }
.field input.half { flex: 1 1 0; min-width: 0; }
.field input[type=checkbox] { flex: none; width: 16px; height: 16px; margin: 0; accent-color: var(--accent); }
.field input.swatch { flex: 0 0 34px; padding: 1px; height: 26px; }
/* A clip field wears four controls, which do not fit beside a label in a 260px
   rail. The row wraps instead: the label and the select take the first line and
   the three buttons take the second, so the select keeps a usable width rather
   than shrinking to nothing beside them. */
.field.clip { flex-wrap: wrap; }
.field.clip select { flex: 1 1 50%; min-width: 0; }
/* 28% each: three of them and their gaps fit one line, and a single one cannot
   squeeze onto the line the label and the select already share. That is what
   puts the break where it belongs rather than wherever the text happens to
   overflow. */
.clipbtn { flex: 1 1 28%; min-width: 0; padding: 3px 6px; font-size: .8rem; }
/* The drop target reads as one: a dashed edge says "put something here" where
   a solid button says "press me", and it does both. */
.clipbtn.drop { border-style: dashed; }
.clipbtn.drop.over { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 25%, var(--panel)); }
/* A standing sentence under a group, for what its controls cannot say
   themselves. Muted and small: it is a caption, not a warning. */
.note { margin: 6px 0 2px; font-size: .74rem; color: var(--muted); }

/* The row is marked, not the input: the row is what carries the field's label,
   so reddening it puts the colour and the name in one glance. */
.field.invalid input, .field.invalid select {
  border-color: var(--err);
  background: color-mix(in srgb, var(--err) 12%, var(--panel));
}

#stage {
  position: relative; overflow: hidden; background: var(--stage);
  display: flex; flex-direction: column; align-items: stretch; gap: 8px;
  padding: 10px 10px 46px;
}

/* --- the frame strip ------------------------------------------------------
   One entry per cell of the sheet, row major. Each shows the cell's own art
   over four chips, one per state, lit when that state claims the cell. The
   strip scrolls sideways: a 13-cell directional sheet does not wrap onto a
   second row and push the canvas off the bottom. */
#frames { flex: none; display: flex; gap: 6px; overflow-x: auto; padding-bottom: 4px; }
.frame { flex: none; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 4px; border: 1px solid color-mix(in srgb, #fff 12%, transparent); border-radius: 6px; background: #05060a; }
.frame[aria-current="true"] { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.frame .pick { padding: 0; border: 0; background: none; line-height: 0; border-radius: 3px; }
.frame canvas { display: block; width: 48px; height: 48px; image-rendering: pixelated; }
.frame .no { font-size: .62rem; color: #cfd3dc; opacity: .6; font-variant-numeric: tabular-nums; }
.frame .chips { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; }
/* A chip is fixed light-on-dark for the same reason the plate is: it sits on
   art ground, which stays dark in both themes. */
.chip {
  font-size: .6rem; line-height: 1; padding: 3px 0; width: 24px; text-align: center;
  border-radius: 3px; border: 1px solid color-mix(in srgb, #fff 18%, transparent);
  background: transparent; color: #cfd3dc; opacity: .55;
}
.chip[aria-pressed="true"] { background: var(--accent); color: var(--accent-ink); border-color: transparent; opacity: 1; }

/* The scroll box around the cell being painted. min-height: 0 lets it shrink
   below its content inside the column flex; without it a zoomed-in canvas
   pushes the palette off the bottom of the stage instead of scrolling. */
#viewport { flex: 1 1 auto; min-height: 0; overflow: auto; display: flex; }
/* margin: auto rather than the parent's centring: both centre a canvas smaller
   than the box, and only this one keeps the top-left corner of a canvas LARGER
   than the box reachable, since a scroll container cannot scroll to negative
   offsets. */
#view {
  margin: auto; flex: none; display: block; image-rendering: pixelated;
  background: #05060a; border: 1px solid color-mix(in srgb, #fff 12%, transparent);
  cursor: crosshair; touch-action: none;
}

#brushbar { flex: none; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.toolset { display: flex; gap: 4px; }
.tool[aria-pressed="true"] { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
#ink { display: flex; align-items: center; gap: 6px; font-size: .8rem; color: #cfd3dc; font-variant-numeric: tabular-nums; }
#inkchip { width: 18px; height: 18px; border-radius: 4px; border: 1px solid color-mix(in srgb, #fff 25%, transparent); }

/* One row per ramp, dark to light as palette.yml orders them. */
#palette { flex: none; display: flex; flex-direction: column; gap: 3px; max-height: 22vh; overflow-y: auto; }
.ramp { display: flex; align-items: center; gap: 6px; }
.ramp .rname { flex: 0 0 68px; font-size: .68rem; color: #cfd3dc; opacity: .6; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.swatchbtn { flex: none; width: 20px; height: 18px; padding: 0; border-radius: 3px; border: 1px solid color-mix(in srgb, #000 40%, transparent); }
.swatchbtn[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--accent-ink); }

/* --- the live stage -------------------------------------------------------
   The prop as the arena shows it. The canvas is art ground like every other
   surface on this page, so it keeps the fixed --stage plate in both themes; the
   chrome around it follows the theme as the rail does. */
#live {
  background: var(--panel); border-left: 1px solid var(--line);
  padding: 10px; overflow-y: auto;
  display: flex; flex-direction: column; gap: 8px;
}
#stageview {
  flex: none; display: block; width: 320px; height: 276px;
  image-rendering: pixelated; border-radius: 6px;
  background: var(--stage); border: 1px solid color-mix(in srgb, #fff 12%, transparent);
}
#stagectl { flex: none; display: flex; flex-direction: column; gap: 6px; }
.ctl { display: flex; align-items: center; gap: 8px; }
.ctl > span:first-child { flex: 0 0 62px; opacity: .85; font-size: .85rem; }
.ctl input[type=range] { flex: 1 1 0; min-width: 0; padding: 0; accent-color: var(--accent); }
/* Tabular numerals and a fixed width: the readout sits beside a slider being
   dragged, and a proportional 1 would make the whole row twitch. */
.ctl > span:last-child { flex: 0 0 48px; text-align: right; font-size: .8rem; color: var(--muted); font-variant-numeric: tabular-nums; }
#stagehint { margin: 0; font-size: .78rem; color: var(--muted); }

/* The same bar, in the same corner, as the character, wall, map and weapon
   editors. It sits in #stage rather than in #viewport so it stays pinned while
   the canvas scrolls under it. */
#zoombar {
  position: absolute; right: 10px; bottom: 10px; display: flex; gap: 4px; align-items: center;
  background: var(--panel); border: 1px solid var(--line); border-radius: 8px; padding: 4px;
}
#zoombar #zoomlevel { min-width: 4.2em; }

/* --- the header's icon ----------------------------------------------------
   Same idiom as the other four editors' assets/css/*.css: a 12x12 crispEdges
   SVG in a data URI, masked over currentColor, so light, dark and hover all
   share one definition with no image asset. A barrel: a body in two bands with
   a hoop line between them, and a narrower rim at each end so the silhouette
   is a container rather than a stack of bars. Read at 15px, the size the
   masthead wears, in both themes. */
.ic {
  display: inline-block; width: 1.5em; height: 1.5em; flex: none;
  background: currentColor;
  -webkit-mask: var(--m) center / contain no-repeat; mask: var(--m) center / contain no-repeat;
}
.ic-prop { --m: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' shape-rendering='crispEdges'%3E%3Crect x='3' y='0' width='6' height='1'/%3E%3Crect x='2' y='2' width='8' height='3'/%3E%3Crect x='2' y='6' width='8' height='3'/%3E%3Crect x='3' y='10' width='6' height='1'/%3E%3C/svg%3E"); }
