:root {
  --ink: var(--text);
  --panel: color-mix(in srgb, var(--text) 6%, var(--bg));
  --line: color-mix(in srgb, var(--text) 16%, transparent);
  --accent: #3e5a8e;
}

* { box-sizing: border-box; }
body { margin: 0; font: 14px/1.4 system-ui, sans-serif; background: var(--bg, #14151a); color: var(--ink, #e6e8ec); }
#bar { display: flex; align-items: center; gap: 12px; padding: 8px 12px; background: var(--panel, #1c1e26); border-bottom: 1px solid var(--line, #2a2e39); }
#bar .brand { font-weight: 600; display: flex; align-items: center; gap: 6px; }
/* The masthead's mark, in the same idiom as assets/css/map-editor.css: a 12x12
   crispEdges SVG in a data URI, masked over currentColor, so one definition
   serves light and dark with no image asset. Four courses of brick in a
   running bond, kept identical to the .ic-brick in scripts/releasenotes.js so
   the landing page's Tools card and the page it opens agree. */
.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-brick { --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='0' y='0' width='5' height='2'/%3E%3Crect x='6' y='0' width='5' height='2'/%3E%3Crect x='0' y='3' width='2' height='2'/%3E%3Crect x='3' y='3' width='5' height='2'/%3E%3Crect x='9' y='3' width='3' height='2'/%3E%3Crect x='0' y='6' width='5' height='2'/%3E%3Crect x='6' y='6' width='5' height='2'/%3E%3Crect x='0' y='9' width='2' height='2'/%3E%3Crect x='3' y='9' width='5' height='2'/%3E%3Crect x='9' y='9' width='3' height='2'/%3E%3C/svg%3E"); }
#bar .actions { display: flex; gap: 6px; margin-left: auto; align-items: center; }
#matlabel { opacity: .7; font-variant-numeric: tabular-nums; }
button, select, input[type=number] { font: inherit; color: inherit; background: var(--panel, #1c1e26); border: 1px solid var(--line, #2a2e39); border-radius: 6px; padding: 4px 10px; cursor: pointer; }
input[type=number] { cursor: text; }
button.primary { background: var(--accent, #3e5a8e); border-color: transparent; }
button:disabled { opacity: .4; cursor: default; }
#dirty { color: #e0a030; font-size: .85rem; }
#notice { margin: 0; padding: 6px 12px; background: #3a2a2a; }
.framebox { display: flex; align-items: center; gap: 6px; font-size: .8rem; opacity: .85; }
.framebox input { width: 52px; padding: 4px 6px; }

#shell { display: grid; grid-template-columns: 210px 1fr 260px; height: calc(100vh - 45px); position: relative; }
#rail, #preview { background: var(--panel, #1c1e26); padding: 10px; overflow-y: auto; border-right: 1px solid var(--line, #2a2e39); }
#preview { border-right: 0; border-left: 1px solid var(--line, #2a2e39); }
.lab { font-size: .72rem; text-transform: uppercase; letter-spacing: .08em; opacity: .6; margin: 12px 0 6px; }
.lab button { float: right; font-size: .8rem; line-height: 1; padding: 1px 6px; }
.field { display: flex; align-items: center; gap: 8px; margin: 6px 0; }
.field span { flex: 0 0 62px; opacity: .8; }
.field input[type=range] { flex: 1; }
/* A text value this yaml subset cannot write back. The notice says what is
   wrong; this says which field it is about, since a bound text layer has two
   text fields and the notice names neither. */
.field input.invalid { outline: 2px solid #b04a3a; }
.field.checkbox span { flex: 1; }
.toolset { display: flex; flex-wrap: wrap; gap: 4px; }
.toolset button.active { outline: 2px solid var(--accent, #3e5a8e); }

/* Layers: an ordered list, hide toggle, label and reorder/delete buttons in a
   row (reordering is two buttons, not a drag). Selected row gets the same
   tint chared uses for a selected part, so the two editors read as one
   vocabulary. */
#layers { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 3px; }
#layers li { display: flex; align-items: center; gap: 4px; padding: 4px 6px; border-radius: 6px; cursor: pointer; }
/* .sel, not .active: that is the class name wall-editor.js's drawLayers()
   actually toggles on the selected row. */
#layers li.sel { background: rgba(255,255,255,.08); }
#layers .name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#layers li button { flex: 0 0 auto; padding: 1px 6px; font-size: .78rem; opacity: .7; }
#params { display: flex; flex-direction: column; gap: 6px; }
#ramps { display: flex; flex-direction: column; gap: 4px; }
.hint, #nearestramp { font-size: .78rem; opacity: .7; margin: 4px 0; }

/* #grid must never be stretched by CSS: its buffer is SIZE * zoom square and
   Task 17's brush maps a pointer to a tile pixel by dividing by state.zoom, so
   a width/height:100% here (the character editor's arrangement) would scale
   that square buffer into #stage's non-square box and paint somewhere other
   than the cursor. #stage centres and scrolls the canvas at its intrinsic
   size instead.
   safe center, not plain center: a centred flex item that overflows its
   container is placed at a negative offset, and scrollLeft/scrollTop cannot
   go negative, so the overflowing half is unreachable at every scroll
   position -- measured, at zoom 40 in a 1400x900 window the leftmost 815
   pixels of a 2560 pixel tile could never be brought on screen. safe falls
   back to start alignment the moment the content overflows, which keeps the
   whole tile scrollable while still centring it when it fits. A tile you
   cannot scroll to is a tile you cannot paint, the same class of bug as the
   stretch this rule replaced. */
#stage { position: relative; display: flex; justify-content: safe center; align-items: safe center; overflow: auto; background: #0c0d11; }
#grid { image-rendering: pixelated; cursor: crosshair; touch-action: none; flex: none; }
#zoombar { position: absolute; right: 10px; bottom: 10px; display: flex; gap: 4px; align-items: center; background: var(--panel,#1c1e26); border: 1px solid var(--line,#2a2e39); border-radius: 8px; padding: 4px; }
#zoomlevel, #frameat { opacity: .8; font-variant-numeric: tabular-nums; font-size: .82rem; padding: 0 2px; }
#framescrub { width: 90px; }

#pv { width: 100%; image-rendering: pixelated; background: #0c0d11; border-radius: 8px; }

/* Meters: a two-column definition list, term then value, the way the seam
   ratio and colour count are read together rather than hunted for. */
#meters { display: grid; grid-template-columns: auto auto; gap: 2px 10px; margin: 8px 0; }
#meters dt { opacity: .6; font-size: .78rem; grid-column: 1; }
#meters dd { margin: 0; grid-column: 2; text-align: right; font-variant-numeric: tabular-nums; }
#meters dd.over { color: #e0604a; font-weight: 600; }
#matmeta { font-size: .78rem; opacity: .7; }

/* A centred overlay, same footing as #shortcuts but modal-weighted: it sits
   over the whole shell rather than pinned to a corner. */
#regpanel {
  position: absolute; inset: 0; margin: auto; width: min(480px, 90%); height: fit-content;
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
  padding: 16px; box-shadow: 0 8px 32px rgba(0,0,0,.4); z-index: 10;
}
#regsteps { font-size: .85rem; opacity: .9; white-space: pre-wrap; }

#shortcuts { position: fixed; right: 16px; bottom: 16px; z-index: 5; background: var(--panel); border: 1px solid var(--line); border-radius: 8px; padding: 10px 14px; font-size: .85rem; }
#shortcuts ul { margin: 0; padding-left: 0; list-style: none; }
#shortcuts li { margin: 3px 0; opacity: .85; }
#shortcuts b { display: inline-block; min-width: 8.5em; opacity: 1; }
