/* Weapon editor chrome. Layered on the shared demo-theme tokens (assets/css/
   demo-theme.css) the way assets/css/map-editor.css is, not the way
   assets/css/character-editor.css and assets/css/wall-editor.css are: those
   two hardcode a handful of panel/notice/badge colours as plain hex, which
   reads fine against the dark default but goes unreadable the moment a
   visitor flips to light (dark text implied by a literal hex background, or a
   literal dark background under text that just went dark too). Every colour
   below is either var(--bg)/var(--text) themselves, a color-mix of the two
   (so it moves with the toggle), or one of the three fixed brand/status
   colours declared here once and reused everywhere they are needed -- never a
   bare hex baked into a single rule.

   --accent, --err and --warn are fixed rather than derived: they are meant to
   read the same regardless of theme (a red is a red). --accent is the one of
   the three ever painted as a SOLID fill (button.primary's background), and a
   solid fill needs its own fixed foreground rather than var(--text) -- the one
   thing that is NOT fixed -- so it gets --accent-ink alongside it. --err and
   --warn are only ever used as text or blended a fraction into --panel/--bg
   (see #notice.bad and .field.invalid below), so each is blended toward
   var(--text) at its own use site instead of carrying a second fixed token:
   that keeps the red/amber recognisable while still gaining enough of
   whichever theme's ink is active to hold contrast against that theme's
   ground. --stage is fixed dark on purpose too: it is a raycaster viewport,
   not a page surface, and this editor's own preview (built in a later task)
   is tuned against a dark backdrop the way the game itself always draws
   against one. */
: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 three 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; }
#bar .actions { display: flex; gap: 6px; margin-left: auto; align-items: center; }
#weaponlabel { opacity: .7; font-variant-numeric: tabular-nums; }

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] { cursor: text; }
/* A fixed accent needs a fixed foreground rather than var(--text): --accent
   is the same dark blue in both themes, and in light mode var(--text) is
   dark too -- dark-on-dark-blue is the exact failure this file's header
   comment describes. --accent-ink is picked once so this stays true no
   matter which theme is active. */
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 weapon strip: eleven buttons carrying the weapons' own icon.png at twice
   its authored 16x9. It scrolls rather than wrapping, so a narrow window keeps
   the header one row tall instead of pushing the stage down. */
#weapons { display: flex; gap: 4px; align-items: center; min-width: 0; overflow-x: auto; padding: 2px; }
/* .wpick carries a fixed --stage plate rather than the panel it sits in. The
   icons are drawn light-on-dark, the same way the game draws them over its own
   raycaster viewport (this file's header explains why --stage stays fixed
   dark in both themes), and checked side by side: on the dark theme's panel
   they read crisp, but on the light theme's pale panel the same pixels read
   as washed-out grey-on-grey, close to illegible. The plate puts every icon
   back on the dark ground it was drawn for regardless of theme. */
.wpick { padding: 3px 4px; line-height: 0; background: var(--stage); }
.wpick img { display: block; width: 32px; height: 18px; image-rendering: pixelated; }
/* A weapon with no art wears its id, so the button needs its text metrics back.
   color is fixed to --accent-ink rather than left at var(--text): the button
   rule above sets color: var(--text), and var(--text) is dark in light theme --
   dark text on the fixed-dark --stage plate above is the same failure this
   file's header warns about for --accent, so the id text needs a fixed light
   foreground too. --accent-ink is already that token; reused here rather than
   adding a second one. */
.wpick.noart { line-height: 1.4; font-size: .78rem; padding: 4px 8px; color: var(--accent-ink); }
.wpick[aria-current="true"] { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 22%, var(--stage)); }

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

#notice { margin: 0; padding: 6px 12px; background: var(--panel); border-bottom: 1px solid var(--line); flex: none; font-size: .85rem; }
/* .bad is the class weapon-editor.js's notice() toggles for a rejected edit --
   the engine's own message, so it has to actually read as a problem rather
   than blend into the same panel every other status line uses. */
#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)); }

#shell { display: grid; grid-template-columns: 240px 1fr 220px; flex: 1; min-height: 0; }
#rail, #preview { background: var(--panel); padding: 10px; overflow-y: auto; border-right: 1px solid var(--line); }
#preview { border-right: 0; border-left: 1px solid var(--line); }

.lab { font-size: .72rem; text-transform: uppercase; letter-spacing: .08em; opacity: .6; margin: 12px 0 6px; }

/* One section per group, built by buildRail() in src/tools/weaped/form.js: a
   heading button over a body of field rows. The heading is a button because it
   is one -- it takes focus, it answers Enter and Space, and it carries
   aria-expanded, none of which a <p> with a click handler does. */
.grp { border-top: 1px solid var(--line); }
.grp:first-child { border-top: 0; }
.ghead {
  display: flex; align-items: center; gap: 6px; width: 100%;
  background: none; border: 0; border-radius: 0; padding: 9px 2px;
  text-align: left; font-size: .72rem; text-transform: uppercase;
  letter-spacing: .08em; opacity: .6;
}
.ghead:hover { opacity: 1; }
.ghead .gname { flex: 1 1 auto; }
/* 1.3em against .ic's own 1.5em: at the heading's .72rem that is about 15px,
   which is the size the glyphs were drawn and checked at -- see the glyph set's
   own header further down, which records the same figure.

   This was 1.15em, about 13px, and the two comments could not both be true. The
   set was read at 15, so the size moves to meet what was actually verified
   rather than the comment moving to meet a size nobody looked at. */
.ghead .ic { width: 1.3em; height: 1.3em; }
/* The disclosure: a triangle pointing right when shut, down when open. Built
   from borders rather than a fifth glyph, because it is chrome rather than a
   mark with a meaning to draw. */
.tri { flex: none; width: 0; height: 0; border-left: 4px solid currentColor; border-top: 3px solid transparent; border-bottom: 3px solid transparent; }
.ghead[aria-expanded="true"] .tri { transform: rotate(90deg); }
.gbody { padding-bottom: 8px; }

/* One labelled input per field, built by buildRail() in src/tools/weaped/
   form.js. A number field is two controls sharing one value (the slider and
   the box), so the row has to fit three children instead of the usual 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=range] { flex: 1 1 0; min-width: 0; margin: 0; accent-color: var(--accent); }
.field input[type=number] { flex: 0 0 64px; min-width: 0; padding: 3px 6px; }
.field input[type=text], .field select { flex: 1 1 0; min-width: 0; padding: 3px 6px; }

/* setInvalid() marks the row, not the input, because the row is what
   inputs.get(path) hands back a reference to and it is the row that already
   carries the field's label -- reddening it puts the colour and the name
   together in one glance instead of asking an eye to hop between them. */
.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: center; justify-content: center; gap: 10px;
  padding: 16px;
}
/* Fixed light-on-dark rather than var(--text): this canvas sits on --stage,
   which stays dark in both themes on purpose (see the top-of-file comment),
   so its own foreground has to stay fixed too or light mode would put dark
   page text over a backdrop that never lightened to meet it. */
#view, #strip { display: block; image-rendering: pixelated; background: #05060a; border: 1px solid color-mix(in srgb, #fff 12%, transparent); }
/* The scroll box around the framebuffer. min-height: 0 is what lets it shrink
   below its content inside the column flex -- without it a zoomed-in canvas
   pushes the timeline and the hint off the bottom of the stage instead of
   scrolling. flex: 1 1 auto so the box takes all the height the timeline and
   the hint do not: its client size is then the true space a fit has to land
   in, which is what fitStage() measures against. Sized to its content instead,
   it would report the canvas's own height and every fit would come back at
   100%. */
#viewport { align-self: stretch; flex: 1 1 auto; min-height: 0; overflow: auto; display: flex; }
/* margin: auto, not the parent's justify-content: center. Both centre a canvas
   smaller than the box; only this one keeps the top-left corner of a canvas
   LARGER than the box reachable, since a centred flex item overflows equally in
   both directions and a scroll container cannot scroll to negative offsets.
   Nothing sets max-width here on purpose: shrinking the picture to fit is what
   the zoom is for, and a silently shrunk framebuffer is no longer 1:1 at 100%. */
#view { margin: auto; flex: none; }
#timeline { display: flex; align-items: center; gap: 8px; width: 480px; max-width: 100%; }
#timeline #strip { flex: 1 1 auto; height: 28px; }
/* The drawn cell's name, beside the strip. Fixed light-on-dark like the strip
   it sits with rather than theme-following, for the reason weapon-editor.css
   states over #view: this row belongs to the stage, which is a raycaster
   viewport and dark in both themes. min-width keeps the strip from resizing
   under the playhead as the name changes length between beats. */
#timeline #drawnstate {
  flex: 0 0 auto; min-width: 9ch; font: 11px/1 ui-monospace, monospace;
  color: #cfd3dc; opacity: .85; text-align: right;
}
#stagehint { color: #cfd3dc; opacity: .7; font-size: .8rem; margin: 0; }

/* The same bar, in the same corner, as the character, wall and map editors.
   It sits in #stage rather than in #viewport so that it stays pinned while the
   framebuffer scrolls under it. #zoomlevel is a button here where the other
   editors use a span: it is the way back to real resolution, which is the one
   zoom this tool is most often asked for. Its width is fixed so the bar does
   not jiggle between "25%" and "100%". */
#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;
}
#zoombar #zoomlevel { min-width: 4.2em; }

#statemap, #schedule { margin-top: 8px; font-size: .78rem; opacity: .75; }

/* The floor pickup panel (src/tools/weaped/drop.js). The canvas keeps its 64x64
   backing store and is scaled by CSS alone, like the stage's own framebuffer:
   what is on screen is the texels the game samples, not a resample of them. */
#pickup { margin-top: 12px; font-size: .78rem; }
#pickup .lab { opacity: .75; }
#pickupcell {
  display: block; width: 128px; height: 128px;
  image-rendering: pixelated; background: var(--stage);
  border: 1px solid var(--line); border-radius: 3px;
}
#pickupinfo { display: block; margin: 6px 0; opacity: .75; line-height: 1.4; }
#pickupinfo.bad { opacity: 1; color: var(--bad, #d66); }
.pickupactions { display: flex; gap: 6px; }
.pickupactions button { flex: 1 1 auto; }

/* .statecell: one row per entry of a declared sprites.states list, built by
   buildStateMap() in src/tools/weaped/schedule.js. This lives in the 220px
   #preview column, too narrow for the rail's fixed 46% label width plus a
   32px thumbnail, so the label shrinks to whatever the number box and
   thumbnail leave it instead. */
.statecell span { flex: 1 1 auto; min-width: 0; }
.statecell input[type=number] { flex: 0 0 44px; }
.statecell .thumb {
  flex: none; width: 32px; height: 32px;
  image-rendering: pixelated; background: var(--stage);
  border: 1px solid var(--line); border-radius: 3px;
}

/* .beat: one row per shot.frames entry, built by buildSchedule(). `name`
   takes the wide box (it names a state, arbitrary text); `at` is a fraction
   of the cooldown between 0 and 1, so it gets a narrow box rather than the
   rail's slider, which is sized for far larger physics ranges. */
.beat input[type=text] { flex: 1 1 auto; min-width: 0; }
.beat input[type=number] { flex: 0 0 56px; }

#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; }

/* --- the header's icon ----------------------------------------------------
   Same idiom as the other three 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 ring with four ticks standing
   proud of it and a filled centre: a reticle, read at 15px (the size the
   masthead actually wears) rather than guessed at full scale. */
.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-target { --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='5' y='0' width='2' height='1'/%3E%3Crect x='5' y='11' width='2' height='1'/%3E%3Crect x='0' y='5' width='1' height='2'/%3E%3Crect x='11' y='5' width='1' height='2'/%3E%3Crect x='4' y='1' width='4' height='1'/%3E%3Crect x='2' y='2' width='2' height='1'/%3E%3Crect x='8' y='2' width='2' height='1'/%3E%3Crect x='1' y='4' width='1' height='4'/%3E%3Crect x='10' y='4' width='1' height='4'/%3E%3Crect x='2' y='9' width='2' height='1'/%3E%3Crect x='8' y='9' width='2' height='1'/%3E%3Crect x='4' y='10' width='4' height='1'/%3E%3Crect x='5' y='5' width='2' height='2'/%3E%3C/svg%3E"); }

/* --- the rail's group glyphs ----------------------------------------------
   One per entry of fields.js's GROUPS, in that order. Same idiom as the
   masthead's mark above and as the other three editors: a 12x12 crispEdges
   SVG in a data URI, masked over currentColor, so light, dark, hover and the
   open state all share one definition.

   src/tools/weaped/form.js's GROUP_ICONS is the authority for which class a
   heading wears; these rules only say what the class draws. A class defined
   here that no group names, or a group whose class nothing defines, is caught
   by tests/weaped-form.test.js rather than by a blank 1.5em box on screen.

   Every glyph here was read at 15px, which is the size a rail heading wears,
   and in both themes. A mark that only works at full scale is a mark that
   only works in this comment. */

/* A name plate: a filled bar over two thin rules, the lower one short, so the
   block reads as a heading with its lines under it rather than as a stack of
   equal bars. */
.ic-g-identity { --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='1' y='2' width='10' height='3'/%3E%3Crect x='1' y='7' width='10' height='1'/%3E%3Crect x='1' y='9' width='7' height='1'/%3E%3C/svg%3E"); }
/* A muzzle flash: a solid barrel block with a fan of three rays off its right
   edge, the outer two stepped away from horizontal rather than parallel to
   the middle one. Redrawn during Task 6's look: the first draft (three
   parallel horizontal bars of falling then rising length, next to the block)
   read, on the real rail at 15px, as a settings or levels glyph rather than a
   flash -- the exact shape a "tune" or equalizer icon uses elsewhere. Angling
   the top ray up and the bottom ray down breaks that bar-chart silhouette
   while keeping the mark asymmetric and pointing one way, which is what
   keeps it apart from the blast's four-fold X. */
.ic-g-fire { --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='4' width='6' height='4'/%3E%3Crect x='7' y='3' width='2' height='1'/%3E%3Crect x='9' y='1' width='2' height='1'/%3E%3Crect x='7' y='5' width='5' height='2'/%3E%3Crect x='7' y='8' width='2' height='1'/%3E%3Crect x='9' y='10' width='2' height='1'/%3E%3C/svg%3E"); }
/* A cracked block: a filled 10x10 square split from its top edge to its
   bottom edge by a stepped one-pixel gap. The only solid square in the set,
   so the seam does not have to carry the reading on its own at 15px. */
.ic-g-damage { --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='1' y='1' width='6' height='2'/%3E%3Crect x='1' y='3' width='5' height='2'/%3E%3Crect x='1' y='5' width='4' height='2'/%3E%3Crect x='1' y='7' width='3' height='2'/%3E%3Crect x='1' y='9' width='2' height='2'/%3E%3Crect x='8' y='1' width='3' height='2'/%3E%3Crect x='7' y='3' width='4' height='2'/%3E%3Crect x='6' y='5' width='5' height='2'/%3E%3Crect x='5' y='7' width='6' height='2'/%3E%3Crect x='4' y='9' width='7' height='2'/%3E%3C/svg%3E"); }
/* A magazine: three rounds standing in a box, walls and floor only so the
   rounds read through the opening. The motif table's rounded tops are gone:
   a round is two pixels wide here, and a bevel on two pixels is either the
   whole width or one pixel off centre. Flat tops, and the box does the work. */
.ic-g-ammo { --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='2' width='1' height='8'/%3E%3Crect x='11' y='2' width='1' height='8'/%3E%3Crect x='0' y='10' width='12' height='2'/%3E%3Crect x='2' y='4' width='2' height='6'/%3E%3Crect x='5' y='4' width='2' height='6'/%3E%3Crect x='8' y='4' width='2' height='6'/%3E%3C/svg%3E"); }
/* A ballistic curve in five steps, rising in three and falling in two, with
   the last step dropped to the floor: gravity, not a symmetric hop. Dotted
   where the throw's arc is continuous, which is the pair's whole distinction
   at 15px. */
.ic-g-physics { --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='8' width='2' height='2'/%3E%3Crect x='2' y='5' width='2' height='2'/%3E%3Crect x='5' y='3' width='2' height='2'/%3E%3Crect x='8' y='5' width='2' height='2'/%3E%3Crect x='10' y='9' width='2' height='3'/%3E%3C/svg%3E"); }
/* A burst: a filled centre with four diagonal arms running out to the
   corners. Four-fold symmetry is what says blast rather than fire, and the
   arms touch the centre so the silhouette is one X rather than nine dots. */
.ic-g-blast { --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='4' y='4' width='4' height='4'/%3E%3Crect x='2' y='2' width='2' height='2'/%3E%3Crect x='0' y='0' width='2' height='2'/%3E%3Crect x='8' y='2' width='2' height='2'/%3E%3Crect x='10' y='0' width='2' height='2'/%3E%3Crect x='2' y='8' width='2' height='2'/%3E%3Crect x='0' y='10' width='2' height='2'/%3E%3Crect x='8' y='8' width='2' height='2'/%3E%3Crect x='10' y='10' width='2' height='2'/%3E%3C/svg%3E"); }
/* An overhand arc with the object already gone from it: a two-pixel-thick
   ramp from the bottom left, and a 3x3 block clear of its end at the top
   right. The block is three wide, not two, so that at 15px it reads as a
   thing that left rather than as the arc's last step. */
.ic-g-throw { --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='8' width='2' height='3'/%3E%3Crect x='2' y='6' width='2' height='3'/%3E%3Crect x='4' y='4' width='2' height='3'/%3E%3Crect x='6' y='3' width='2' height='2'/%3E%3Crect x='9' y='0' width='3' height='3'/%3E%3C/svg%3E"); }
/* Something set down: a banded crate with a solid arrowhead descending on it.
   The motif table asks for two arrows above the box. Two of them drawn side
   by side leaves each three pixels wide, and at 15px they read as a row of
   nubs along the crate's lid rather than as arrows, so it is one solid head
   instead: the same sentence, said once and wide enough to survive. */
.ic-g-deploy { --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='2' y='0' width='8' height='1'/%3E%3Crect x='3' y='1' width='6' height='1'/%3E%3Crect x='4' y='2' width='4' height='1'/%3E%3Crect x='5' y='3' width='2' height='1'/%3E%3Crect x='1' y='5' width='10' height='1'/%3E%3Crect x='1' y='11' width='10' height='1'/%3E%3Crect x='1' y='5' width='1' height='7'/%3E%3Crect x='10' y='5' width='1' height='7'/%3E%3Crect x='1' y='8' width='10' height='1'/%3E%3C/svg%3E"); }
/* One block leading, a smaller one behind and below it: a follow relationship
   with nothing else in it. The only mark in the set that is two plain blocks,
   so the difference in size is safe to carry the whole meaning. */
.ic-g-escort { --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='6' y='2' width='5' height='5'/%3E%3Crect x='1' y='7' width='4' height='4'/%3E%3C/svg%3E"); }
/* A camera and the cone it sees through: a body at the bottom and two stepped
   edges opening upward.

   The motif table asks for an ammo belt here, and that names the wrong thing.
   This group is not a belt: its fields are fov, refresh rate, noise decay and
   death hold -- the drone's video feed, the picture the escort sends back. A
   belt would be a mark that reads cleanly and means something else, which is
   the failure the distinct-URI test exists to catch. Drawing the belt anyway
   settled it twice over: at 15px it is a comb, which is the ammo magazine's
   silhouette with the box taken off. The cone is drawn as two open edges
   rather than as a filled wedge so it does not collapse into a lump. */
.ic-g-feed { --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='1' y='0' width='1' height='2'/%3E%3Crect x='2' y='2' width='1' height='2'/%3E%3Crect x='3' y='4' width='1' height='2'/%3E%3Crect x='4' y='6' width='1' height='3'/%3E%3Crect x='10' y='0' width='1' height='2'/%3E%3Crect x='9' y='2' width='1' height='2'/%3E%3Crect x='8' y='4' width='1' height='2'/%3E%3Crect x='7' y='6' width='1' height='3'/%3E%3Crect x='4' y='9' width='4' height='3'/%3E%3C/svg%3E"); }
/* A wide plinth, a housing on a neck above it and a stub pointing right: an
   emplacement, which is what a deployed turret is. Bottom-heavy, where the
   missile below is all one horizontal band. The first draft sat the housing
   straight on the plinth and at 15px the two merged into one lump with a
   snag on its side; the one-pixel neck is what makes it a gun standing on a
   base. */
.ic-g-turret { --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='4' width='5' height='4'/%3E%3Crect x='8' y='5' width='4' height='2'/%3E%3Crect x='5' y='8' width='2' height='1'/%3E%3Crect x='1' y='9' width='10' height='3'/%3E%3C/svg%3E"); }
/* A body with a nose cone and two tail fins. Solid where the flight chevron
   below is open, which is the whole of what separates the two projectiles at
   15px: one is a thing, the other is a motion. */
.ic-g-missile { --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='1' y='4' width='6' height='4'/%3E%3Crect x='7' y='5' width='4' height='2'/%3E%3Crect x='1' y='2' width='3' height='2'/%3E%3Crect x='1' y='8' width='3' height='2'/%3E%3C/svg%3E"); }
/* Acquisition: four corner brackets standing off a centre dot. No ring, which
   is what keeps it clear of the scope and of the masthead's .ic-target, and
   the brackets sit hard in the corners so the outline is a broken square
   rather than the blast's X. */
.ic-g-lock { --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='4' height='1'/%3E%3Crect x='0' y='0' width='1' height='4'/%3E%3Crect x='8' y='0' width='4' height='1'/%3E%3Crect x='11' y='0' width='1' height='4'/%3E%3Crect x='0' y='11' width='4' height='1'/%3E%3Crect x='0' y='8' width='1' height='4'/%3E%3Crect x='8' y='11' width='4' height='1'/%3E%3Crect x='11' y='8' width='1' height='4'/%3E%3Crect x='5' y='5' width='2' height='2'/%3E%3C/svg%3E"); }
/* A swept chevron with two dashes trailing it: motion, not a projectile. The
   chevron is open at its back, so nothing about it reads as a body. */
.ic-g-flight { --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='5' width='2' height='2'/%3E%3Crect x='3' y='5' width='2' height='2'/%3E%3Crect x='9' y='5' width='2' height='2'/%3E%3Crect x='7' y='3' width='2' height='2'/%3E%3Crect x='5' y='1' width='2' height='2'/%3E%3Crect x='7' y='7' width='2' height='2'/%3E%3Crect x='5' y='9' width='2' height='2'/%3E%3C/svg%3E"); }
/* A flame: a body tapering at both ends with a notch bitten out of the top,
   so the crown is two licks of different height rather than one point. Drawn
   with a single centred point the same body read as a water drop at every
   size, which is the one thing a burn must not say. */
.ic-g-burn { --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='4' y='0' width='2' height='3'/%3E%3Crect x='7' y='2' width='2' height='1'/%3E%3Crect x='3' y='3' width='6' height='2'/%3E%3Crect x='2' y='5' width='8' height='5'/%3E%3Crect x='3' y='10' width='6' height='2'/%3E%3C/svg%3E"); }
/* A point of contact: a surface across the foot and three strokes coming down
   into it, the outer two angled inward. The surface is what makes this an
   arrival rather than one more radiating mark, and the strokes stop a row
   clear of it so they read as falling rather than as a fence standing on it. */
.ic-g-impact { --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='1' y='5' width='2' height='2'/%3E%3Crect x='3' y='7' width='2' height='2'/%3E%3Crect x='5' y='5' width='2' height='4'/%3E%3Crect x='9' y='5' width='2' height='2'/%3E%3Crect x='7' y='7' width='2' height='2'/%3E%3Crect x='0' y='10' width='12' height='2'/%3E%3C/svg%3E"); }
/* A speaker cone with two waves off it, and the one definition in this file
   that leaves crispEdges off: the waves are genuine arcs and crisp edges
   would stair-step them. The precedent is character-editor.css's .ic-eyeoff,
   for the same reason. The rects keep their hard edges anyway, being integer
   aligned. */
.ic-g-sounds { --m: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Crect x='0' y='4' width='2' height='4'/%3E%3Crect x='2' y='3' width='1' height='6'/%3E%3Crect x='3' y='2' width='1' height='8'/%3E%3Crect x='4' y='1' width='1' height='10'/%3E%3Cpath d='M6.34 3.38 A 3.2 3.2 0 0 1 6.34 8.62 L5.65 7.64 A 2 2 0 0 0 5.65 4.36 Z'/%3E%3Cpath d='M8.74 1.76 A 6 6 0 0 1 8.74 10.24 L7.89 9.39 A 4.8 4.8 0 0 0 7.89 2.61 Z'/%3E%3C/svg%3E"); }
/* Iron sights seen from behind: two tall uprights, and between them the front
   post with its bead. The first draft made the front post a plain short bar,
   and three bars of three different heights read at 15px as a bar chart --
   the same silhouette character-editor.css gives .ic-strength, and a
   quantity, not a sight. The bead is a crossbar, which no chart has. */
.ic-g-sights { --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='3' width='3' height='8'/%3E%3Crect x='9' y='3' width='3' height='8'/%3E%3Crect x='4' y='5' width='4' height='2'/%3E%3Crect x='5' y='7' width='2' height='4'/%3E%3C/svg%3E"); }
/* A ring with a crosshair drawn through it. The masthead's .ic-target is the
   same ring with its ticks OUTSIDE and a filled centre; here the cross is
   inside and the centre is where the two lines meet, so the two never read as
   one another even though both are rings. The cross is one pixel wide, thin
   as anything in this file gets: at two pixels the four arms and the ring
   close up on each other and at 15px the whole mark is a filled disc with
   four bites out of it. */
.ic-g-scope { --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='4' y='1' width='4' height='1'/%3E%3Crect x='2' y='2' width='2' height='1'/%3E%3Crect x='8' y='2' width='2' height='1'/%3E%3Crect x='2' y='3' width='1' height='1'/%3E%3Crect x='9' y='3' width='1' height='1'/%3E%3Crect x='1' y='4' width='1' height='4'/%3E%3Crect x='10' y='4' width='1' height='4'/%3E%3Crect x='2' y='8' width='1' height='1'/%3E%3Crect x='9' y='8' width='1' height='1'/%3E%3Crect x='2' y='9' width='2' height='1'/%3E%3Crect x='8' y='9' width='2' height='1'/%3E%3Crect x='4' y='10' width='4' height='1'/%3E%3Crect x='2' y='5' width='8' height='1'/%3E%3Crect x='5' y='2' width='1' height='8'/%3E%3C/svg%3E"); }
/* A shell leaving a port: a slot in the left wall and two cases climbing away
   from it. The slot is a gap between two blocks rather than an outlined
   bracket, which at 15px is the difference between reading as a port and
   reading as a smudge. */
.ic-g-eject { --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='4' width='5' height='3'/%3E%3Crect x='0' y='9' width='5' height='3'/%3E%3Crect x='6' y='6' width='2' height='2'/%3E%3Crect x='9' y='3' width='2' height='2'/%3E%3C/svg%3E"); }
/* The sprite sheet: a framed grid of cells. character-editor.css's .ic-sheet
   geometry verbatim -- it means the same thing in both editors and it was
   already read at 15px there, so redrawing it could only make it differ. */
.ic-g-sheet { --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='1' width='12' height='1'/%3E%3Crect x='0' y='10' width='12' height='1'/%3E%3Crect x='0' y='1' width='1' height='10'/%3E%3Crect x='11' y='1' width='1' height='10'/%3E%3Crect x='4' y='1' width='1' height='10'/%3E%3Crect x='8' y='1' width='1' height='10'/%3E%3Crect x='0' y='5' width='12' height='1'/%3E%3C/svg%3E"); }
