/* =====================================================================
 * DEDEN — SHARED UI
 * =====================================================================
 * One visual language for every panel in the game, taken from the LOGIN
 * SCREEN rather than invented: dark, near-black panels, oxidised-red trim,
 * warm sand text, Bebas Neue for anything that acts like a label and Share
 * Tech Mono for anything that holds data.
 *
 * Two halves:
 *
 *   1. TOKENS + `ded-*` COMPONENTS — what new UI should be built from.
 *   2. A COMPATIBILITY LAYER at the bottom that restyles the class names
 *      the three existing editors already use (`ge-*` animation,
 *      `pfx-*` particle FX, `ed-*` level). ~300KB of editor JS does not
 *      need rewriting for the game to look like one product, and a
 *      half-converted UI looks worse than an unconverted one.
 *
 * Loaded once from index.html, before the editor scripts.
 * ===================================================================== */

:root {
  /* Surfaces — flat, near-black, very slightly warm. */
  --ded-bg:          #100807;   /* the panel itself (login container); a touch up from #0a0505 so drop shadows + dark scrollbars read */
  --ded-bg-raised:   #140a09;   /* headers, toolbars, anything on top   */
  --ded-bg-sunken:   #070303;   /* lists, wells, canvas surrounds       */
  --ded-field:       #140808;   /* inputs (login input background)      */

  /* Trim. The login border is rgba(120,30,20,.5) over black. */
  --ded-line:        #3a1611;   /* ordinary dividers                    */
  --ded-line-strong: #781e14;   /* the structural red trim              */
  --ded-line-soft:   #241110;   /* hairlines inside a panel             */

  /* Text. */
  --ded-text:        #dcb4a0;   /* body / field text                    */
  --ded-text-dim:    #96604a;   /* labels, secondary                    */
  --ded-text-faint:  #6b4034;   /* hints, placeholders                  */
  --ded-accent:      #c85032;   /* headings, active state               */
  --ded-accent-hot:  #ff6a3c;   /* hover / focus                        */

  /* Buttons (login's primary + secondary). */
  --ded-btn:         #1e0c0a;
  --ded-btn-hover:   #2d1210;
  --ded-btn-pri:     #781910;
  --ded-btn-pri-hot: #a02314;

  /* Alternate button colours — the same button in orange and green, for
     marking things apart at a glance (warn / go). Each set mirrors the red
     one: trim, hover trim, plain fill, hover fill, filled, filled hover,
     filled text. */
  --ded-org-line:    #8a4a12;
  --ded-org-accent:  #e08a2c;
  --ded-org-btn:     #1f1308;
  --ded-org-hover:   #2e1c0c;
  --ded-org-pri:     #7a4210;
  --ded-org-pri-hot: #a0581a;
  --ded-org-text:    #f6d0a0;
  --ded-org-glow:    0 0 18px rgba(224,138,44,0.25);
  --ded-grn-line:    #2f5e1c;
  --ded-grn-accent:  #6cbf45;
  --ded-grn-btn:     #0c1609;
  --ded-grn-hover:   #14240e;
  --ded-grn-pri:     #2c5c17;
  --ded-grn-pri-hot: #3b7a20;
  --ded-grn-text:    #cdf2b4;
  --ded-grn-glow:    0 0 18px rgba(108,191,69,0.22);

  /* Depth: windows sit over the game, buttons sit over the window. A hard
     little shadow 2px below and 3px to the right (pressed buttons drop to
     1px/2px, so they read as pushed in). */
  --ded-drop:        3px 2px 2px rgba(0,0,0,0.6);
  --ded-drop-press:  2px 1px 1px rgba(0,0,0,0.6);

  /* Native control internals — checkbox wells, slider tracks, the
     stepper key. Tokens rather than inline values so the whole set stays
     adjustable from one place, same as everything else. */
  --ded-well:        #0b0505;   /* unchecked checkbox interior          */
  --ded-well-on:     #1a0806;   /* checked checkbox interior            */
  --ded-track:       #2b2426;   /* slider's UNFILLED portion            */
  --ded-track-edge:  #181314;   /* its hairline                         */
  --ded-thumb:       #5e1108;   /* slider handle core                   */
  --ded-ink:         #140606;   /* marks cut out of a red key           */

  /* Feedback. */
  --ded-good:        #4ade80;
  --ded-bad:         #e86a5a;

  /* Metrics — a single radius everywhere, as asked. */
  --ded-r:           4px;
  --ded-r-sm:        3px;
  --ded-glow:        0 0 18px rgba(200,40,20,0.22);

  --ded-font-ui:     'Special Elite', 'Segoe UI', serif;
  --ded-font-data:   'Special Elite', 'Segoe UI', serif;

  /* Tells the browser to render native widgets (spinners, dropdown
     arrows, scrollbars) dark instead of white. Cheaper and more reliable
     than fighting each one individually. */
  color-scheme: dark;
}

/* =====================================================================
 * NATIVE CONTROLS
 * =====================================================================
 * Checkboxes, number spinners and range sliders ship with light-mode
 * chrome that no amount of background/border styling reaches — a white
 * checkbox interior, white spinner buttons, a white slider track. They
 * are rebuilt here once, for every panel in the game, rather than per
 * editor.
 * ===================================================================== */

/* ── Focus ──
   The browser's default focus ring is a blue/white outline that has
   nothing to do with this theme; it was showing as a bright halo round
   the script box. Replaced with the same red glow used elsewhere. */
.ded-panel :focus, .ded-panel :focus-visible,
#gani-ed :focus, #gani-dlg :focus, #pfx-editor :focus, #level-editor :focus {
  outline: none !important;
}
.ded-panel input:focus, .ded-panel select:focus, .ded-panel textarea:focus,
#gani-ed input:focus, #gani-ed select:focus, #gani-ed textarea:focus,
#gani-dlg input:focus, #gani-dlg select:focus, #gani-dlg textarea:focus,
#pfx-editor input:focus, #pfx-editor select:focus, #pfx-editor textarea:focus,
#level-editor input:focus, #level-editor select:focus, #level-editor textarea:focus {
  border-color: var(--ded-accent) !important;
  box-shadow: var(--ded-glow) !important;
}

/* ── Checkboxes ──
   Default is a black outline with a WHITE interior, which on a near-black
   panel reads as a bright dot and pulls the eye to every UNCHECKED box —
   the opposite of what a checkbox should do. Red outline, black interior,
   red tick.

   The size MUST be !important. ganiEditor.js injects its <style> at
   runtime, i.e. after this file, and carries
   `#gani-ed .ge-chk input { width: auto }` at the same specificity (1-1-0)
   — so it won. With `appearance: none` there is no intrinsic size to fall
   back on, and `width: auto` collapsed the box to a sliver: the label text
   ran over the top of it and there was almost nothing left to click. Both
   symptoms, one cause. The right margin keeps the label clear of the box
   for the markup that has no flex `gap` of its own. */
.ded-panel input[type=checkbox], .ded-panel input[type=radio],
#gani-ed input[type=checkbox], #gani-dlg input[type=checkbox],
#pfx-editor input[type=checkbox], #level-editor input[type=checkbox] {
  appearance: none !important; -webkit-appearance: none !important;
  width: 13px !important; height: 13px !important;
  min-width: 13px !important; min-height: 13px !important;
  margin: 0 5px 0 0 !important;
  padding: 0 !important;
  vertical-align: middle;
  background: var(--ded-well);
  border: 1px solid var(--ded-line-strong);
  border-radius: var(--ded-r-sm);
  cursor: pointer;
  position: relative;
  flex: 0 0 auto !important;
  transition: border-color .15s, background .15s;
}
/* The label is a sibling, not a wrapper, in some of this markup — give it
   the same clearance so nothing sits on the box. */
#gani-ed input[type=checkbox] + label, #gani-dlg input[type=checkbox] + label,
#pfx-editor input[type=checkbox] + label, #level-editor input[type=checkbox] + label,
.ded-panel input[type=checkbox] + label {
  margin-left: 0;
  cursor: pointer;
  vertical-align: middle;
}
.ded-panel input[type=radio], #gani-ed input[type=radio] { border-radius: 50%; }
.ded-panel input[type=checkbox]:hover, #gani-ed input[type=checkbox]:hover,
#gani-dlg input[type=checkbox]:hover, #pfx-editor input[type=checkbox]:hover,
#level-editor input[type=checkbox]:hover { border-color: var(--ded-accent); }
.ded-panel input[type=checkbox]:checked, #gani-ed input[type=checkbox]:checked,
#gani-dlg input[type=checkbox]:checked, #pfx-editor input[type=checkbox]:checked,
#level-editor input[type=checkbox]:checked {
  border-color: var(--ded-accent);
  background: var(--ded-well-on);
}
.ded-panel input[type=checkbox]:checked::after,
#gani-ed input[type=checkbox]:checked::after,
#gani-dlg input[type=checkbox]:checked::after,
#pfx-editor input[type=checkbox]:checked::after,
#level-editor input[type=checkbox]:checked::after {
  content: "";
  position: absolute; left: 3px; top: 0px;
  width: 4px; height: 8px;
  border: solid var(--ded-accent-hot);
  border-width: 0 2px 2px 0;
  transform: rotate(42deg);
}

/* ── Number spinners ──
   The arrows stayed grey because of a contradiction: `-webkit-appearance:
   none` is the trick that REMOVES the native stepper, and the previous
   rule set it and then tried to paint a background on the thing it had
   just removed. You cannot style that widget; Chrome paints it natively.
   What was left on screen was the UA's own dark-mode stepper.

   So the key is painted on the INPUT, as a background image at its right
   edge, and the native stepper is made invisible with `opacity: 0` rather
   than removed — opacity does not affect hit-testing, so the arrows still
   step the value, they just no longer draw themselves. The visible key is
   entirely ours: a red field with the arrows cut out of it in black.

   The SVG's colours cannot be var()s — a data: URI is an opaque string to
   the CSS parser — so they are written out and noted here instead:
   %23781910 = --ded-btn-pri, %23140606 = --ded-ink. */
/* UNSCOPED, on purpose. Scoping this to the four editor roots left the
   native stepper alive anywhere else a number input exists — the particle
   editor's colour dialog, for one, is appended to <body> and is inside
   none of them. Every number input in this app is editor chrome; the game
   itself is canvas. One rule, no gaps.

   `appearance: textfield` is a SECOND, independent suppression. Telling
   the input to render as a plain text field removes the spin buttons in
   Chrome as well as Firefox, without depending on a shadow pseudo-element
   honouring anything. The pseudo rule below stays as a third belt: the
   previous attempt scoped the `outer` pseudo to `.ded-panel` only, so for
   the editors just the `inner` one was hidden — and what survived drew
   itself at the right edge of the CONTENT box, i.e. 19px to the LEFT of
   the painted key, on top of the digits it was meant to change. */
input[type=number] {
  appearance: textfield !important; -webkit-appearance: textfield !important;
  -moz-appearance: textfield !important;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='15' height='24'><rect x='0' y='0' width='15' height='24' fill='%23781910'/><path d='M7.5 5 L11 10 L4 10 Z' fill='%23140606'/><path d='M7.5 19 L4 14 L11 14 Z' fill='%23140606'/></svg>") !important;
  background-repeat: no-repeat !important;
  background-position: right center !important;
  background-size: 15px 100% !important;
  padding-right: 19px !important;
}
/* The native widget is REMOVED, not hidden. `opacity: 0` was an earlier
   attempt — an invisible widget would still hit-test, so stepping would
   survive — but Chrome honours only a short list of properties on this
   shadow pseudo and opacity is not among them, so the UA stepper kept
   painting. BOTH pseudos, unscoped, since which one a given Chrome build
   renders is not something to bet on.
   Stepping is given back in ui.js instead (`_numberKeyStep`), which reads
   the click position against the key's width and calls stepUp/stepDown —
   so the behaviour is ours too, and it works identically in Firefox. */
input[type=number]::-webkit-outer-spin-button,
input[type=number]::-webkit-inner-spin-button {
  appearance: none !important; -webkit-appearance: none !important;
  display: none !important;
  width: 0 !important;
  margin: 0 !important;
}
/* The key's clickable width, shared with ui.js so the hit area and the
   painted graphic cannot drift apart. */
:root { --ded-step-w: 19px; }

/* ── Sliders ──
   Default track is white past the fill and the handle is a white ring
   over grey. Track goes dark, handle becomes a bright red rim over a
   deeper red core. Both engines are covered: Firefox fills via
   ::-moz-range-progress, WebKit has no progress pseudo so the filled
   portion there comes from accent-color on the unstyled parts. */
.ded-panel input[type=range],
#gani-ed input[type=range], #gani-dlg input[type=range],
#pfx-editor input[type=range], #level-editor input[type=range] {
  appearance: none; -webkit-appearance: none;
  background: transparent;
  height: 14px;
  cursor: pointer;
}
.ded-panel input[type=range]::-webkit-slider-runnable-track,
#gani-ed input[type=range]::-webkit-slider-runnable-track,
#gani-dlg input[type=range]::-webkit-slider-runnable-track,
#pfx-editor input[type=range]::-webkit-slider-runnable-track,
#level-editor input[type=range]::-webkit-slider-runnable-track {
  height: 4px; border-radius: 2px;
  background: var(--ded-track);                 /* the UNFILLED portion: dark grey */
  border: 1px solid var(--ded-track-edge);
}
.ded-panel input[type=range]::-moz-range-track,
#gani-ed input[type=range]::-moz-range-track,
#gani-dlg input[type=range]::-moz-range-track,
#pfx-editor input[type=range]::-moz-range-track,
#level-editor input[type=range]::-moz-range-track {
  height: 4px; border-radius: 2px;
  background: var(--ded-track);
  border: 1px solid var(--ded-track-edge);
}
.ded-panel input[type=range]::-moz-range-progress,
#gani-ed input[type=range]::-moz-range-progress,
#gani-dlg input[type=range]::-moz-range-progress,
#pfx-editor input[type=range]::-moz-range-progress,
#level-editor input[type=range]::-moz-range-progress {
  height: 4px; border-radius: 2px;
  background: var(--ded-btn-pri-hot);  /* the FILLED portion: red */
}
.ded-panel input[type=range]::-webkit-slider-thumb,
#gani-ed input[type=range]::-webkit-slider-thumb,
#gani-dlg input[type=range]::-webkit-slider-thumb,
#pfx-editor input[type=range]::-webkit-slider-thumb,
#level-editor input[type=range]::-webkit-slider-thumb {
  appearance: none; -webkit-appearance: none;
  width: 12px; height: 12px; margin-top: -5px;
  border-radius: 50%;
  background: var(--ded-thumb);                 /* darker red core */
  border: 2px solid var(--ded-accent-hot);   /* vibrant red rim */
  box-shadow: 0 0 6px rgba(200,40,20,.45);
}
.ded-panel input[type=range]::-moz-range-thumb,
#gani-ed input[type=range]::-moz-range-thumb,
#gani-dlg input[type=range]::-moz-range-thumb,
#pfx-editor input[type=range]::-moz-range-thumb,
#level-editor input[type=range]::-moz-range-thumb {
  width: 12px; height: 12px;
  border-radius: 50%;
  background: var(--ded-thumb);
  border: 2px solid var(--ded-accent-hot);
  box-shadow: 0 0 6px rgba(200,40,20,.45);
}

/* ── Panels ────────────────────────────────────────────────────────── */
.ded-panel {
  background: var(--ded-bg);
  border: 1px solid var(--ded-line-strong);
  border-radius: var(--ded-r);
  color: var(--ded-text);
  font-family: var(--ded-font-data);
  font-size: 11px;
  box-shadow: var(--ded-drop), 0 0 40px rgba(0,0,0,0.8), inset 0 0 60px rgba(80,10,5,0.10);
}

/* A panel's title strip. Matches the login heading: wide-tracked Bebas. */
.ded-head {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 12px;
  background: linear-gradient(180deg, #1a0d0b, #120706);
  border-bottom: 1px solid var(--ded-line-strong);
  border-radius: var(--ded-r) var(--ded-r) 0 0;
  font-family: var(--ded-font-ui);
  font-size: 15px; letter-spacing: 2px; text-transform: uppercase;
  color: var(--ded-accent);
  text-shadow: 0 0 15px rgba(200,40,20,0.3);
}
.ded-head .ded-x { margin-left: auto; cursor: pointer; color: var(--ded-text-faint); }
.ded-head .ded-x:hover { color: var(--ded-accent-hot); }

/* A section divider inside a panel. */
.ded-section {
  padding: 6px 10px;
  background: var(--ded-bg-raised);
  border-top: 1px solid var(--ded-line-soft);
  border-bottom: 1px solid var(--ded-line-soft);
  font-family: var(--ded-font-ui);
  font-size: 13px; letter-spacing: 1px; text-transform: uppercase;
  color: var(--ded-accent);
}

/* ── Buttons ───────────────────────────────────────────────────────── */
.ded-btn {
  font-family: var(--ded-font-ui);
  font-size: 13px; letter-spacing: 1px; text-transform: uppercase;
  /* Bebas Neue has a tall cap-height and almost no descender, so it sits
     visually high in a box with symmetric padding. FOUR extra pixels on
     top optically centres it — two was still reading high. The total is
     unchanged, so no button changes height. Same correction wherever
     Bebas is used; keep these four rules in step. */
  padding: 6px 12px;
  line-height: 1;
  background: var(--ded-btn);
  color: var(--ded-text-dim);
  border: 1px solid var(--ded-line-strong);
  border-radius: var(--ded-r-sm);
  cursor: pointer;
  transition: background .18s, color .18s, box-shadow .18s, border-color .18s;
  box-shadow: var(--ded-drop);
}
.ded-btn:hover { background: var(--ded-btn-hover); color: var(--ded-text); border-color: var(--ded-accent); }
.ded-btn:active { transform: translateY(1px); box-shadow: var(--ded-drop-press); }
.ded-btn:disabled { opacity: .35; cursor: default; transform: none; }
.ded-btn:disabled:hover { background: var(--ded-btn); color: var(--ded-text-dim); border-color: var(--ded-line-strong); }
.ded-btn.pri, .ded-btn.on {
  background: var(--ded-btn-pri);
  color: #ffc8a0;
}
.ded-btn.pri:hover, .ded-btn.on:hover { background: var(--ded-btn-pri-hot); box-shadow: var(--ded-drop), var(--ded-glow); }
.ded-btn.tiny { font-size: 11px; letter-spacing: 1px; padding: 2px 7px; }

/* Orange / green: add .orange or .green to any .ded-btn (with .pri for
   the filled version). Same shape, hover and press as the red one.
   --ded-worn-trim recolours the worn trim overlay to match. */
.ded-btn.orange { background: var(--ded-org-btn); border-color: var(--ded-org-line); color: var(--ded-org-accent); --ded-worn-trim: var(--ded-org-line); }
.ded-btn.orange:hover { background: var(--ded-org-hover); border-color: var(--ded-org-accent); color: var(--ded-org-text); --ded-worn-trim: var(--ded-org-accent); }
.ded-btn.orange.pri, .ded-btn.orange.on { background: var(--ded-org-pri); color: var(--ded-org-text); }
.ded-btn.orange.pri:hover, .ded-btn.orange.on:hover { background: var(--ded-org-pri-hot); box-shadow: var(--ded-drop), var(--ded-org-glow); }
.ded-btn.green { background: var(--ded-grn-btn); border-color: var(--ded-grn-line); color: var(--ded-grn-accent); --ded-worn-trim: var(--ded-grn-line); }
.ded-btn.green:hover { background: var(--ded-grn-hover); border-color: var(--ded-grn-accent); color: var(--ded-grn-text); --ded-worn-trim: var(--ded-grn-accent); }
.ded-btn.green.pri, .ded-btn.green.on { background: var(--ded-grn-pri); color: var(--ded-grn-text); }
.ded-btn.green.pri:hover, .ded-btn.green.on:hover { background: var(--ded-grn-pri-hot); box-shadow: var(--ded-drop), var(--ded-grn-glow); }
.ded-btn.orange:disabled:hover { background: var(--ded-org-btn); border-color: var(--ded-org-line); color: var(--ded-org-accent); }
.ded-btn.green:disabled:hover { background: var(--ded-grn-btn); border-color: var(--ded-grn-line); color: var(--ded-grn-accent); }


/* ── Fields ────────────────────────────────────────────────────────── */
.ded-input, .ded-panel input[type=text], .ded-panel input[type=number],
.ded-panel input[type=password], .ded-panel select, .ded-panel textarea {
  background-color: var(--ded-field);
  border: 1px solid var(--ded-line);
  border-radius: var(--ded-r-sm);
  color: var(--ded-text);
  font-family: var(--ded-font-data);
  font-size: 11px;
  padding: 4px 7px;
  outline: none;
  box-sizing: border-box;
  transition: border-color .25s, box-shadow .25s;
}
.ded-panel input:focus, .ded-panel select:focus, .ded-panel textarea:focus,
.ded-input:focus { border-color: var(--ded-accent); box-shadow: var(--ded-glow); }
.ded-panel ::placeholder { color: var(--ded-text-faint); }
.ded-panel input[type=checkbox], .ded-panel input[type=radio] { accent-color: var(--ded-btn-pri); }
.ded-panel input[type=range] { accent-color: var(--ded-accent); }

.ded-label { color: var(--ded-text-dim); font-size: 11px; font-family: var(--ded-font-data); }
.ded-hint  { color: var(--ded-text-faint); font-size: 10px; line-height: 1.5; }

/* ── Lists ─────────────────────────────────────────────────────────── */
.ded-list {
  background: var(--ded-bg-sunken);
  border: 1px solid var(--ded-line-soft);
  border-radius: var(--ded-r-sm);
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--ded-line-strong) var(--ded-bg-sunken);
}
.ded-list > div {
  padding: 3px 8px; cursor: pointer; display: flex; gap: 6px;
  color: var(--ded-text); white-space: nowrap; user-select: none;
  border-bottom: 1px solid var(--ded-line-soft);
}
.ded-list > div:hover { background: #240f0c; color: var(--ded-accent-hot); }
.ded-list > div.on { background: var(--ded-btn-pri); color: #ffc8a0; }

/* ── Scrollbars, everywhere in a panel ─────────────────────────────── */
/* Scrollbars for `.ded-panel` are handled once, with everything else, in
   the blanket block of the compatibility layer below — restating them
   here only created a second place for the thumb's colour to come from. */

/* =====================================================================
 * COMPATIBILITY LAYER
 * =====================================================================
 * The three editors were written before this existed and between them use
 * several hundred class references. Rather than rewrite that JS — and risk
 * regressions in tools that work — their existing class names are mapped
 * onto the tokens above. The result is one consistent product today; new
 * UI should use the `ded-*` classes directly.
 *
 * These are deliberately last so they win on equal specificity, and each
 * rule is scoped to its editor's root so nothing leaks into the game.
 * ===================================================================== */

/* ── Scrollbars, everywhere ────────────────────────────────────────
   Blanket rules on the editor ROOTS rather than per class. Listing
   classes meant the panels I happened to think of went red while the
   others stayed grey — the Sprites list and the Pieces list sat side by
   side with different bars. Anything that scrolls inside an editor is
   covered by this, including panels added later.
   TWO RENDERING PATHS, and they are mutually exclusive. Since Chrome 121
   the standard `scrollbar-color`/`scrollbar-width` properties work — but
   the moment either is set to anything other than `auto`, Chrome switches
   to the standard scrollbar and IGNORES every `::-webkit-scrollbar-*`
   rule. That is a dead end for us: the standard properties can set two
   flat colours and nothing else, so a textured (worn) thumb is impossible
   through them.

   So Chrome is deliberately forced back onto the ::-webkit- path by
   setting the standard properties to `auto`, which also neutralises the
   editors' own `scrollbar-color: #3a3a46` declarations — those are what
   kept the Sprites list grey. Firefox, which has no ::-webkit- pseudos,
   keeps the standard path in the @supports block below.

   !important throughout, because the editors set `scrollbar-color` on
   their own classes (e.g. `#gani-ed .ge-scroll`, specificity 1-1-0) which
   outranks `#gani-ed *` (1-0-0) and is injected later besides. */
#gani-ed, #gani-dlg, #pfx-editor, #level-editor, .ded-panel,
#gani-ed *, #gani-dlg *, #pfx-editor *, #level-editor *, .ded-panel * {
  scrollbar-width: auto !important;
  scrollbar-color: #781e14 #070303 !important;
}
/* 10/16 REVERSAL of the above: forcing Chrome onto the ::-webkit- path
   left EVERY bar default grey in James's browser — the webkit rules below
   are simply not applied there. The only bars that were ever red were the
   ones carrying a real `scrollbar-color` (the Pieces list, .pfx-body). So
   every editor bar now takes the standard path, in literal hex (thumb =
   --ded-line-strong, track = --ded-bg-sunken). Cost: flat red thumbs, no
   worn texture. The ::-webkit- rules are left in place; with
   scrollbar-color set, Chrome ignores them. */
#gani-ed ::-webkit-scrollbar, #gani-dlg ::-webkit-scrollbar,
#pfx-editor ::-webkit-scrollbar, #level-editor ::-webkit-scrollbar,
.ded-panel ::-webkit-scrollbar,
#gani-ed::-webkit-scrollbar, #pfx-editor::-webkit-scrollbar,
#level-editor::-webkit-scrollbar {
  width: 10px !important; height: 10px !important;
  background: #070303 !important;
}
/* The colours in this block are written out LITERALLY (#070303 =
   --ded-bg-sunken, #241110 = --ded-line-soft, #781e14 = --ded-line-strong,
   #c85032 = --ded-accent). Custom properties do not reliably reach WebKit's
   scrollbar pseudo-elements: the var() resolved to nothing, the declaration
   went invalid, and every bar on this path drew in default GREY. The only
   red bars were the few elements with their own `scrollbar-color`, which
   puts them on the standard path instead (the Pieces list, .pfx-body). */
#gani-ed ::-webkit-scrollbar-track, #gani-dlg ::-webkit-scrollbar-track,
#pfx-editor ::-webkit-scrollbar-track, #level-editor ::-webkit-scrollbar-track,
.ded-panel ::-webkit-scrollbar-track,
#gani-ed::-webkit-scrollbar-track, #pfx-editor::-webkit-scrollbar-track,
#level-editor::-webkit-scrollbar-track {
  background: #070303 !important;
  border-left: 1px solid #241110 !important;
}
#gani-ed ::-webkit-scrollbar-thumb, #gani-dlg ::-webkit-scrollbar-thumb,
#pfx-editor ::-webkit-scrollbar-thumb, #level-editor ::-webkit-scrollbar-thumb,
.ded-panel ::-webkit-scrollbar-thumb,
#gani-ed::-webkit-scrollbar-thumb, #pfx-editor::-webkit-scrollbar-thumb,
#level-editor::-webkit-scrollbar-thumb {
  background-color: #781e14 !important;
  border-radius: 5px !important;
}
#gani-ed ::-webkit-scrollbar-thumb:hover, #gani-dlg ::-webkit-scrollbar-thumb:hover,
#pfx-editor ::-webkit-scrollbar-thumb:hover, #level-editor ::-webkit-scrollbar-thumb:hover,
.ded-panel ::-webkit-scrollbar-thumb:hover,
#gani-ed::-webkit-scrollbar-thumb:hover, #pfx-editor::-webkit-scrollbar-thumb:hover,
#level-editor::-webkit-scrollbar-thumb:hover {
  background-color: #c85032 !important;
}
#gani-ed ::-webkit-scrollbar-corner, #gani-dlg ::-webkit-scrollbar-corner,
#pfx-editor ::-webkit-scrollbar-corner, #level-editor ::-webkit-scrollbar-corner,
.ded-panel ::-webkit-scrollbar-corner { background: #070303 !important; }
/* Forcing Chrome onto this path also brings back the Windows-style arrow
   BUTTONS at the ends of a bar, which are drawn in default grey and were
   never part of this design. Chrome only shows them once the bar is
   styled, so they have to be turned off explicitly here. */
#gani-ed ::-webkit-scrollbar-button, #gani-dlg ::-webkit-scrollbar-button,
#pfx-editor ::-webkit-scrollbar-button, #level-editor ::-webkit-scrollbar-button,
.ded-panel ::-webkit-scrollbar-button,
#gani-ed::-webkit-scrollbar-button, #pfx-editor::-webkit-scrollbar-button,
#level-editor::-webkit-scrollbar-button {
  display: none !important;
  width: 0 !important; height: 0 !important;
}

/* ── Animation editor (ge-*) ───────────────────────────────────────── */
#gani-ed, #gani-dlg, #gani-ctx, #gani-testbar {
  --ed-bg: var(--ded-bg);
}
#gani-ed { background: var(--ded-bg) !important; color: var(--ded-text) !important; }
#gani-ed .ge-top {
  background: linear-gradient(180deg, #1a0d0b, #120706) !important;
  border-bottom: 2px solid var(--ded-line-strong) !important;
}
#gani-ed .ge-col { background: var(--ded-bg-raised) !important; }
#gani-ed .ge-col.l { border-right: 1px solid var(--ded-line-soft) !important; }
#gani-ed .ge-col.r { border-left: 1px solid var(--ded-line-soft) !important; }
/* The right column is a flex column whose last child, the Info panel, has
   no flex basis — so once the sections above it grew past the column
   height, Info was simply pushed off the bottom with no way to reach it.
   Making the column itself scroll fixes that for good, and leaves room for
   sections added later rather than re-breaking at the next one.
   `min-height: 0` is what lets a flex item actually shrink below its
   content size; without it `overflow` on the column does nothing. */
#gani-ed .ge-col.r {
  overflow-y: auto !important;
  overflow-x: hidden !important;
  min-height: 0 !important;
}
/* Info keeps a usable height instead of collapsing, and no longer needs a
   scrollbar of its own now that its parent has one. */
#gani-ed #ge-info {
  flex: 0 0 auto !important;
  min-height: 70px;
  overflow: visible !important;
}
#gani-ed .ge-sec {
  background: var(--ded-bg-raised) !important;
  color: var(--ded-accent) !important;
  border-top: 1px solid var(--ded-line-soft) !important;
  border-bottom: 1px solid var(--ded-line-soft) !important;
  font-family: var(--ded-font-ui); font-size: 13px; letter-spacing: 1px;
  text-transform: uppercase;
}
#gani-ed .ge-canvaswrap { background: #0b0404 !important; }
#gani-ed .ge-bot { background: var(--ded-bg-raised) !important; border-top: 1px solid var(--ded-line-strong) !important; }
#gani-ed .ge-zoom { background: rgba(16,8,7,.94) !important; border: 1px solid var(--ded-line-strong) !important; }

#gani-ed .ge-btn, #gani-dlg .ge-btn, #gani-testbar .ge-btn {
  font-family: var(--ded-font-ui) !important;
  font-size: 13px !important; letter-spacing: 1px !important; text-transform: uppercase;
  background: var(--ded-btn) !important;
  color: var(--ded-text-dim) !important;
  border: 1px solid var(--ded-line-strong) !important;
  border-radius: var(--ded-r-sm) !important;
  /* Bebas sits high — see .ded-btn. */
  padding: 5px 11px !important;
  line-height: 1 !important;
  transition: background .18s, color .18s, box-shadow .18s, border-color .18s;
}
#gani-ed .ge-btn:hover, #gani-dlg .ge-btn:hover, #gani-testbar .ge-btn:hover {
  background: var(--ded-btn-hover) !important; color: var(--ded-text) !important;
  border-color: var(--ded-accent) !important;
}
#gani-ed .ge-btn.pri, #gani-dlg .ge-btn.pri, #gani-testbar .ge-btn.pri,
#gani-ed .ge-btn.on,  #gani-dlg .ge-btn.on, #gani-testbar .ge-btn.on {
  background: var(--ded-btn-pri) !important; color: #ffc8a0 !important;
}
#gani-ed .ge-btn.pri:hover, #gani-dlg .ge-btn.pri:hover, #gani-testbar .ge-btn.pri:hover,
#gani-ed .ge-btn.on:hover,  #gani-dlg .ge-btn.on:hover, #gani-testbar .ge-btn.on:hover {
  background: var(--ded-btn-pri-hot) !important; box-shadow: var(--ded-drop), var(--ded-glow) !important;
}
#gani-ed .ge-btn.tiny, #gani-dlg .ge-btn.tiny, #gani-testbar .ge-btn.tiny {
  font-size: 11px !important; letter-spacing: 1px !important; padding: 2px 7px !important;
}

#gani-ed input[type=text], #gani-ed input[type=number], #gani-ed select,
#gani-dlg input[type=text], #gani-dlg input[type=number], #gani-dlg select,
#gani-dlg textarea {
  background-color: var(--ded-field) !important;
  border: 1px solid var(--ded-line) !important;
  border-radius: var(--ded-r-sm) !important;
  color: var(--ded-text) !important;
}
#gani-ed input:focus, #gani-ed select:focus,
#gani-dlg input:focus, #gani-dlg select:focus, #gani-dlg textarea:focus {
  border-color: var(--ded-accent) !important; box-shadow: var(--ded-glow) !important;
}
#gani-ed ::placeholder, #gani-dlg ::placeholder { color: var(--ded-text-faint) !important; }
#gani-ed input[type=checkbox], #gani-dlg input[type=checkbox] { accent-color: var(--ded-btn-pri) !important; }

#gani-ed .ge-pl, #gani-dlg .ge-pl {
  background: var(--ded-bg-sunken) !important;
  border: 1px solid var(--ded-line-soft) !important;
  border-radius: var(--ded-r-sm) !important;
  color: var(--ded-text) !important;
}
#gani-ed .ge-pl div:hover, #gani-dlg .ge-pl div:hover { background: #240f0c !important; color: var(--ded-accent-hot) !important; }
#gani-ed .ge-pl div.on, #gani-dlg .ge-pl div.on { background: var(--ded-btn-pri) !important; color: #ffc8a0 !important; }
#gani-ed .ge-pl div.msel { background: #2a1210 !important; color: var(--ded-text) !important; }
#gani-ed .ge-pl div span.t, #gani-dlg .ge-pl div span.t { color: var(--ded-accent) !important; }
#gani-ed .ge-pl div button:hover, #gani-dlg .ge-pl div button:hover { color: var(--ded-accent-hot) !important; }

#gani-ed .ge-sp { background: var(--ded-bg-sunken) !important; border: 1px solid var(--ded-line-soft) !important; border-radius: var(--ded-r-sm) !important; }
#gani-ed .ge-sp:hover { border-color: var(--ded-accent) !important; }
#gani-ed .ge-sp.on { border-color: var(--ded-accent) !important; background: #2a1210 !important; }
#gani-ed .ge-sp span { color: var(--ded-text-dim) !important; }

#gani-ed .ge-fr { background: var(--ded-bg-raised) !important; border: 1px solid var(--ded-line-soft) !important; border-radius: var(--ded-r-sm) !important; }
#gani-ed .ge-fr:hover { border-color: var(--ded-accent) !important; }
#gani-ed .ge-fr.on { background: var(--ded-btn-pri) !important; border-color: var(--ded-accent) !important; }
#gani-ed .ge-fr b { color: var(--ded-text-dim) !important; }
#gani-ed .ge-fr.on b { color: #ffc8a0 !important; }
#gani-ed .ge-fr i { color: var(--ded-text-faint) !important; }
#gani-ed .ge-fr.dropL { box-shadow: inset 3px 0 0 var(--ded-accent) !important; }
#gani-ed .ge-fr.dropR { box-shadow: inset -3px 0 0 var(--ded-accent) !important; }

#gani-ed .ge-keys span { background: var(--ded-bg-raised) !important; border: 1px solid var(--ded-line) !important; color: var(--ded-text-dim) !important; border-radius: var(--ded-r-sm) !important; }
#gani-ed .ge-keys span:hover { border-color: var(--ded-accent) !important; color: var(--ded-accent-hot) !important; }
#gani-ed .ge-keys span.cur { background: var(--ded-btn-pri) !important; border-color: var(--ded-accent) !important; color: #ffc8a0 !important; }

#gani-dlg .box { background: var(--ded-bg) !important; border: 1px solid var(--ded-line-strong) !important; border-radius: var(--ded-r) !important; }
#gani-dlg .hd {
  background: linear-gradient(180deg, #1a0d0b, #120706) !important;
  border-bottom: 1px solid var(--ded-line-strong) !important;
  color: var(--ded-accent) !important;
  font-family: var(--ded-font-ui); font-size: 15px; letter-spacing: 2px; text-transform: uppercase;
  border-radius: var(--ded-r) var(--ded-r) 0 0 !important;
}
#gani-dlg .ft { background: var(--ded-bg) !important; border-top: 1px solid var(--ded-line-soft) !important; border-radius: 0 0 var(--ded-r) var(--ded-r) !important; }
#gani-dlg .sect { color: var(--ded-accent) !important; border-top: 1px solid var(--ded-line-soft) !important; }
#gani-dlg .note { color: var(--ded-text-faint) !important; }
#gani-dlg .ask .opt { background: var(--ded-bg-raised) !important; border: 1px solid var(--ded-line) !important; border-radius: var(--ded-r-sm) !important; }
#gani-dlg .ask .opt:hover { background: #240f0c !important; border-color: var(--ded-accent) !important; }
#gani-dlg .ask .opt b { color: var(--ded-accent) !important; font-family: var(--ded-font-ui); font-size: 14px; letter-spacing: 1px; }
#gani-dlg .ask .sugg span { background: var(--ded-bg-raised) !important; border: 1px solid var(--ded-line) !important; color: var(--ded-text-dim) !important; border-radius: var(--ded-r-sm) !important; }
#gani-dlg .ask .sugg span:hover { border-color: var(--ded-accent) !important; color: var(--ded-accent-hot) !important; }

#gani-ctx { background: var(--ded-bg-raised) !important; border: 1px solid var(--ded-line-strong) !important; border-radius: var(--ded-r-sm) !important; }
#gani-ctx div { color: var(--ded-text) !important; font-family: var(--ded-font-data) !important; }
#gani-ctx div:hover { background: var(--ded-btn-pri) !important; color: #ffc8a0 !important; }
#gani-ctx .sep { background: var(--ded-line) !important; }

#gani-testbar {
  background: linear-gradient(180deg, #1a0d0b, #120706) !important;
  border: 1px solid var(--ded-line-strong) !important;
  border-radius: var(--ded-r) !important;
  color: var(--ded-text) !important;
}
#gani-testbar .t { color: var(--ded-text-dim) !important; }
#gani-testbar .t b { color: var(--ded-accent) !important; }
#gani-testbar .h { color: var(--ded-text-faint) !important; }

#gani-flash {
  background: var(--ded-bg) !important;
  border: 1px solid var(--ded-line-strong) !important;
  border-radius: var(--ded-r-sm) !important;
  color: var(--ded-accent) !important;
  font-family: var(--ded-font-data) !important;
}

/* ── Particle FX editor (pfx-*) ────────────────────────────────────── */
#pfx-editor { background: var(--ded-bg) !important; color: var(--ded-text) !important; }
#pfx-editor .pfx-grp { border-bottom: 1px solid var(--ded-line-soft) !important; }
#pfx-editor .pfx-grp-h {
  background: var(--ded-bg-raised) !important; color: var(--ded-accent) !important;
  font-family: var(--ded-font-ui); font-size: 13px; letter-spacing: 1px; text-transform: uppercase;
}
#pfx-editor .pfx-grp-h:hover { background: #240f0c !important; }
#pfx-editor .pfx-row label { color: var(--ded-text-dim) !important; }
#pfx-editor .pfx-row input[type=range] { accent-color: var(--ded-accent) !important; }
#pfx-editor .pfx-row input[type=number],
#pfx-editor .pfx-row select, #pfx-editor .pfx-row input[type=text] {
  background-color: var(--ded-field) !important; border: 1px solid var(--ded-line) !important;
  color: var(--ded-text) !important; border-radius: var(--ded-r-sm) !important;
}
/* `.pfx-body`'s own scrollbar rules used to be restated here. Removed: at
   1-1-0 they outranked the blanket rules above and their `background`
   shorthand wiped the thumb's texture back to a flat colour. The blanket
   rules cover this element already. */
#pfx-editor .pfx-btn {
  font-family: var(--ded-font-ui) !important; font-size: 13px !important;
  letter-spacing: 1px !important; text-transform: uppercase;
  background: var(--ded-btn) !important; color: var(--ded-text-dim) !important;
  border: 1px solid var(--ded-line-strong) !important; border-radius: var(--ded-r-sm) !important;
  /* Bebas sits high — see .ded-btn. Totals match the original 4px/9px. */
  padding: 4px 9px !important; line-height: 1 !important;
}
#pfx-editor .pfx-btn:hover { background: var(--ded-btn-hover) !important; color: var(--ded-text) !important; border-color: var(--ded-accent) !important; }
#pfx-editor .pfx-btn.pri { background: var(--ded-btn-pri) !important; color: #ffc8a0 !important; }
#pfx-editor .pfx-btn.pri:hover { background: var(--ded-btn-pri-hot) !important; box-shadow: var(--ded-drop), var(--ded-glow) !important; }
#pfx-editor .pfx-tab {
  background: var(--ded-bg-raised) !important; border: 1px solid var(--ded-line) !important;
  color: var(--ded-text-dim) !important;
  font-family: var(--ded-font-ui); letter-spacing: 1px; text-transform: uppercase;
  border-radius: var(--ded-r-sm) var(--ded-r-sm) 0 0 !important;
  padding: 3px 8px !important; line-height: 1 !important;
}
#pfx-editor .pfx-tab.on { background: var(--ded-btn-pri) !important; border-color: var(--ded-accent) !important; color: #ffc8a0 !important; }
#pfx-editor .pfx-maintab {
  font-family: var(--ded-font-ui) !important; letter-spacing: 1px !important;
  text-transform: uppercase;
  padding: 5px 16px !important; line-height: 1 !important;
}
#pfx-editor .pfx-maintab.on { background: var(--ded-btn-pri) !important; border-color: var(--ded-accent) !important; }
#pfx-flash {
  background: var(--ded-bg) !important; border: 1px solid var(--ded-line-strong) !important;
  color: var(--ded-accent) !important; border-radius: var(--ded-r-sm) !important;
}
.pfx-cd .pfx-cd-title { color: var(--ded-accent) !important; font-family: var(--ded-font-ui); letter-spacing: 1px; }
.pfx-cd .pfx-cd-lbl { color: var(--ded-text-dim) !important; }
.pfx-cd .pfx-cd-sw { border: 1px solid var(--ded-line) !important; }
.pfx-cd .pfx-cd-sw:hover { border-color: var(--ded-accent) !important; }
.pfx-cd .pfx-cd-prev { border: 1px solid var(--ded-line) !important; border-radius: var(--ded-r-sm) !important; }

/* ── Level editor (#level-editor) ──────────────────────────────────── */
/* Its panels are built with inline `style.cssText`, so these need
   !important to win — an important declaration in a stylesheet does beat
   a plain inline style. Its own injected #ed-styles block is overridden
   the same way. The old palette here was the orange/brown set
   (#e8943a, #ff8a40, #8b4513, #ffcc40) left over from before this system
   existed; every one of them is remapped. */
#level-editor,
#level-editor > div,
#level-editor [id^="ed-"] {
  background-color: var(--ded-bg) !important;
  color: var(--ded-text) !important;
  border-color: var(--ded-line-strong) !important;
}
#level-editor { border-radius: var(--ded-r) !important; font-family: var(--ded-font-data) !important; }
#level-editor [id^="ed-"] { border-radius: var(--ded-r-sm) !important; }

.ed-btn {
  font-family: var(--ded-font-ui) !important; font-size: 13px !important;
  letter-spacing: 1px !important; text-transform: uppercase;
  background: var(--ded-btn) !important; color: var(--ded-text-dim) !important;
  border: 1px solid var(--ded-line-strong) !important;
  border-radius: var(--ded-r-sm) !important;
  padding: 5px 10px !important; line-height: 1 !important;
  cursor: pointer;
  transition: background .18s, color .18s, box-shadow .18s, border-color .18s;
}
.ed-btn:hover {
  background: var(--ded-btn-hover) !important; color: var(--ded-text) !important;
  border-color: var(--ded-accent) !important;
}
/* The level editor marks a held tool with `.active`, not `.on`. */
.ed-btn.active, .ed-btn.on, .ed-btn.pri {
  background: var(--ded-btn-pri) !important;
  border-color: var(--ded-accent) !important;
  color: #ffc8a0 !important;
}
.ed-btn.active:hover, .ed-btn.on:hover, .ed-btn.pri:hover {
  background: var(--ded-btn-pri-hot) !important; box-shadow: var(--ded-drop), var(--ded-glow) !important;
}
/* Grouped buttons keep their seam, but round the ends to match. */
.ed-btn-group .ed-btn { border-radius: 0 !important; }
.ed-btn-group .ed-btn:first-child { border-radius: var(--ded-r-sm) 0 0 var(--ded-r-sm) !important; }
.ed-btn-group .ed-btn:last-child  { border-radius: 0 var(--ded-r-sm) var(--ded-r-sm) 0 !important; }

/* ── The three editors' buttons + windows: drop shadow and the orange /
   green variants, exactly as .ded-btn (see there). Kept for the few that
   matter — Save = green, Undo/Redo = orange — not every button.
   #level-editor's ed-btn rules carry the id because its [id^="ed-"] rule
   (1-1-0) recolours every ed-* element, buttons included. */
#gani-ed .ge-btn, #gani-dlg .ge-btn, #gani-testbar .ge-btn, #pfx-editor .pfx-btn, .ed-btn { box-shadow: var(--ded-drop); }
#gani-ed .ge-btn:active, #gani-dlg .ge-btn:active, #gani-testbar .ge-btn:active,
#pfx-editor .pfx-btn:active, .ed-btn:active { box-shadow: var(--ded-drop-press) !important; transform: translateY(1px); }
#gani-ed .ge-btn:disabled, #gani-dlg .ge-btn:disabled { transform: none; }
#gani-dlg .box, #pfx-editor, #level-editor { box-shadow: var(--ded-drop), 0 0 40px rgba(0,0,0,0.8) !important; }
#gani-ed .ge-btn.orange, #gani-dlg .ge-btn.orange, #pfx-editor .pfx-btn.orange, #level-editor .ed-btn.orange {
  background: var(--ded-org-btn) !important; border-color: var(--ded-org-line) !important; color: var(--ded-org-accent) !important;
}
#gani-ed .ge-btn.orange:hover, #gani-dlg .ge-btn.orange:hover, #pfx-editor .pfx-btn.orange:hover, #level-editor .ed-btn.orange:hover {
  background: var(--ded-org-hover) !important; border-color: var(--ded-org-accent) !important; color: var(--ded-org-text) !important;
}
#gani-ed .ge-btn.orange.pri, #gani-dlg .ge-btn.orange.pri, #pfx-editor .pfx-btn.orange.pri, #level-editor .ed-btn.orange.pri {
  background: var(--ded-org-pri) !important; color: var(--ded-org-text) !important;
}
#gani-ed .ge-btn.orange.pri:hover, #gani-dlg .ge-btn.orange.pri:hover, #pfx-editor .pfx-btn.orange.pri:hover, #level-editor .ed-btn.orange.pri:hover {
  background: var(--ded-org-pri-hot) !important; box-shadow: var(--ded-drop), var(--ded-org-glow) !important;
}
#gani-ed .ge-btn.green, #gani-dlg .ge-btn.green, #pfx-editor .pfx-btn.green, #level-editor .ed-btn.green {
  background: var(--ded-grn-btn) !important; border-color: var(--ded-grn-line) !important; color: var(--ded-grn-accent) !important;
}
#gani-ed .ge-btn.green:hover, #gani-dlg .ge-btn.green:hover, #pfx-editor .pfx-btn.green:hover, #level-editor .ed-btn.green:hover {
  background: var(--ded-grn-hover) !important; border-color: var(--ded-grn-accent) !important; color: var(--ded-grn-text) !important;
}
#gani-ed .ge-btn.green.pri, #gani-dlg .ge-btn.green.pri, #pfx-editor .pfx-btn.green.pri, #level-editor .ed-btn.green.pri {
  background: var(--ded-grn-pri) !important; color: var(--ded-grn-text) !important;
}
#gani-ed .ge-btn.green.pri:hover, #gani-dlg .ge-btn.green.pri:hover, #pfx-editor .pfx-btn.green.pri:hover, #level-editor .ed-btn.green.pri:hover {
  background: var(--ded-grn-pri-hot) !important; box-shadow: var(--ded-drop), var(--ded-grn-glow) !important;
}
/* A disabled coloured button stays its colour (dimmed by the editor's own opacity rule). */
#gani-ed .ge-btn.green:disabled:hover { background: var(--ded-grn-btn) !important; border-color: var(--ded-grn-line) !important; color: var(--ded-grn-accent) !important; }
#gani-ed .ge-btn.green.pri:disabled:hover { background: var(--ded-grn-pri) !important; color: var(--ded-grn-text) !important; box-shadow: var(--ded-drop) !important; }
#gani-ed .ge-btn.orange:disabled:hover { background: var(--ded-org-btn) !important; border-color: var(--ded-org-line) !important; color: var(--ded-org-accent) !important; }
/* The floppy-disk Save buttons (level + particle editors). The emoji glyph
   is taller than a 13px Bebas line box (line-height 1), so it stuck up past
   the top edge. Text 3px smaller, and the disk nudged down 3px inside. A
   transform, not position:relative — see the worn-trim restore-list trap. */
.ed-btn#ed-save, #pfx-editor #pfx-save, #pfx-editor #pfx-x-gani { font-size: 10px !important; }
.ded-save-ico { display: inline-block; transform: translateY(3px); }

#level-editor input[type=text], #level-editor input[type=number], #level-editor select {
  background-color: var(--ded-field) !important;
  border: 1px solid var(--ded-line) !important;
  border-radius: var(--ded-r-sm) !important;
  color: var(--ded-text) !important;
  font-family: var(--ded-font-data) !important;
}
#level-editor ::placeholder { color: var(--ded-text-faint) !important; }
/* Level-editor scrollbars: see the blanket block above. */
#ed-npc-tooltip {
  background: var(--ded-bg) !important; border: 1px solid var(--ded-line-strong) !important;
  border-radius: var(--ded-r-sm) !important; color: var(--ded-text) !important;
}

/* =====================================================================
 * LOGIN SCREEN
 * =====================================================================
 * The system was derived FROM here, so it already matches — but the
 * rounded corners asked for had never been applied to the original.
 * ===================================================================== */
#login-container { border-radius: var(--ded-r); }
#login-container input { border-radius: var(--ded-r-sm); }
.login-buttons button { border-radius: var(--ded-r-sm); }
/* Bebas sits high here too. */
.login-buttons button { padding: 10px; line-height: 1; }

/* The frame around the game itself was still grey #333 — the one piece of
   chrome the player looks at the whole time. Colour only; the width stays
   2px so nothing about the canvas geometry moves. It is NOT worn: see the
   note in the worn-trim section. */
#game-wrapper { border-color: var(--ded-line-strong) !important; }


/* =====================================================================
 * WORN TRIM  —  "randomly broken and faded away"
 * =====================================================================
 * Opt-in: `<html class="ded-worn wear-med">`. Off by default at the
 * stylesheet level, so nothing here can affect a build that never
 * switches it on.  Dial: UI.setWear("off"|"light"|"med"|"heavy").
 *
 * TWO MECHANISMS, because a trim and a fill cannot be worn the same way.
 *
 * LINE — the element's own border drops to a near-black GHOST, and the
 *   bright oxidised red is re-painted on a ::before overlay which is then
 *   MASKED BY NOISE. Where the mask is solid you get full-strength trim;
 *   where it thins, the ghost shows through. Nothing is ever removed, so
 *   a panel still reads as a panel — the paint has just come off it.
 *
 * FILL — a ::after layer carrying a noise BLOTCH as a background image,
 *   sitting above the element's background and below its content
 *   (`isolation: isolate` + `z-index: -1`). This is what reaches hover
 *   highlights and selected states, and it is the ONLY way to reach a
 *   scrollbar thumb, which cannot have a pseudo-element.
 *
 * Both are the same noise: feTurbulence, with feColorMatrix rebuilding
 * ALPHA from the red channel as  a = k*r + c.  fractalNoise centres on
 * 0.5, so for the LINE texture the trim is solid where r >= (1-c)/k and
 * eaten through where r <= -c/k, and k/c are the entire intensity dial.
 *
 * Three things make this read as decay rather than as a dashed border:
 *
 *   1. The mask is CLOUDY, not a dash pattern, so the line fades out and
 *      back instead of stopping dead.
 *   2. The four CORNERS are added back unconditionally. Corners are what
 *      the eye uses to read a rectangle at all; eroding them makes a
 *      panel look broken rather than old.
 *   3. Each element samples the texture at its OWN offset, so two
 *      buttons side by side never wear the same way.
 *
 * Cost: two textures, each rasterised once at a fixed 160px and shared by
 * every element. Static, editor chrome only, never touches the game
 * canvas or the frame loop.
 *
 * NOT worn, deliberately:
 *   - form controls. A 13px checkbox or a text field with a broken
 *     outline reads as a bug, not as age.
 *   - `#game-wrapper`. Its border is 2px of theme colour now, but a
 *     masked overlay there would be a full-viewport composited layer
 *     sitting on the game canvas, and this engine's frame budget is not
 *     worth a 2px frame.
 *   - `.ge-fr.node`, which marks a frame as holding a named node with an
 *     `outline`. Outlines cannot be masked; it stays crisp, which is
 *     useful anyway since it is an indicator rather than decoration.
 * ===================================================================== */

:root {
  /* What is left of the trim where the paint has gone. Not transparent:
     a frame that vanishes in patches reads as a rendering fault, a frame
     that DARKENS in patches reads as age. */
  --ded-line-ghost: #1d0a08;
}

/* Intensities. `med` is the default and is what used to ship as `heavy`;
   every level is about 50% more eaten than the previous set. */
html.ded-worn {
  --ded-wear-line:   url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='w' filterUnits='userSpaceOnUse' x='0' y='0' width='160' height='160' color-interpolation-filters='sRGB'><feTurbulence type='fractalNoise' baseFrequency='0.055' numOctaves='3' seed='17' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 4.5 0 0 0 -1.14'/></filter><rect width='160' height='160' filter='url(%23w)'/></svg>");
  --ded-wear-blotch: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='b' filterUnits='userSpaceOnUse' x='0' y='0' width='160' height='160' color-interpolation-filters='sRGB'><feTurbulence type='fractalNoise' baseFrequency='0.07' numOctaves='3' seed='23' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 0.07 0 0 0 0 0.025 0 0 0 0 0.02 1.7 0 0 0 -0.855'/></filter><rect width='160' height='160' filter='url(%23b)'/></svg>");
}
html.ded-worn.wear-light {
  --ded-wear-line:   url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='w' filterUnits='userSpaceOnUse' x='0' y='0' width='160' height='160' color-interpolation-filters='sRGB'><feTurbulence type='fractalNoise' baseFrequency='0.05' numOctaves='3' seed='5' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 4 0 0 0 -0.56'/></filter><rect width='160' height='160' filter='url(%23w)'/></svg>");
  --ded-wear-blotch: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='b' filterUnits='userSpaceOnUse' x='0' y='0' width='160' height='160' color-interpolation-filters='sRGB'><feTurbulence type='fractalNoise' baseFrequency='0.065' numOctaves='3' seed='11' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 0.07 0 0 0 0 0.025 0 0 0 0 0.02 1.4 0 0 0 -0.705'/></filter><rect width='160' height='160' filter='url(%23b)'/></svg>");
}
html.ded-worn.wear-med {
  --ded-wear-line:   url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='w' filterUnits='userSpaceOnUse' x='0' y='0' width='160' height='160' color-interpolation-filters='sRGB'><feTurbulence type='fractalNoise' baseFrequency='0.055' numOctaves='3' seed='17' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 4.5 0 0 0 -1.14'/></filter><rect width='160' height='160' filter='url(%23w)'/></svg>");
  --ded-wear-blotch: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='b' filterUnits='userSpaceOnUse' x='0' y='0' width='160' height='160' color-interpolation-filters='sRGB'><feTurbulence type='fractalNoise' baseFrequency='0.07' numOctaves='3' seed='23' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 0.07 0 0 0 0 0.025 0 0 0 0 0.02 1.7 0 0 0 -0.855'/></filter><rect width='160' height='160' filter='url(%23b)'/></svg>");
}
html.ded-worn.wear-heavy {
  --ded-wear-line:   url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='w' filterUnits='userSpaceOnUse' x='0' y='0' width='160' height='160' color-interpolation-filters='sRGB'><feTurbulence type='fractalNoise' baseFrequency='0.065' numOctaves='3' seed='29' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 4.76 0 0 0 -1.57'/></filter><rect width='160' height='160' filter='url(%23w)'/></svg>");
  --ded-wear-blotch: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='b' filterUnits='userSpaceOnUse' x='0' y='0' width='160' height='160' color-interpolation-filters='sRGB'><feTurbulence type='fractalNoise' baseFrequency='0.08' numOctaves='3' seed='31' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 0.07 0 0 0 0 0.025 0 0 0 0 0.02 2 0 0 0 -1.005'/></filter><rect width='160' height='160' filter='url(%23b)'/></svg>");
}

/* ── The ghost ──
   `position: relative` deliberately carries NO !important: anything
   already fixed or absolutely positioned (the editor roots, the test bar,
   the zoom readout) keeps its own positioning and still serves as the
   containing block, while ordinary static boxes get promoted so the
   overlay has something to anchor to. */
html.ded-worn .ded-panel,
html.ded-worn .ded-btn,
html.ded-worn .ded-list,
html.ded-worn #gui-container .ded-skin-window,
html.ded-worn #gui-container .ded-skin-scroll,
html.ded-worn #gani-ed .ge-btn,
html.ded-worn #gani-dlg .ge-btn, html.ded-worn #gani-testbar .ge-btn,
html.ded-worn #gani-ed .ge-keys span,
html.ded-worn #gani-ed .ge-sp,
html.ded-worn #gani-ed .ge-fr,
html.ded-worn #gani-ed .ge-pl,
html.ded-worn #gani-dlg .ge-pl,
html.ded-worn #gani-ed .ge-zoom,
html.ded-worn #gani-ctx,
html.ded-worn #gani-dlg .box,
html.ded-worn #gani-dlg .prev,
html.ded-worn #gani-dlg .ask .opt,
html.ded-worn #gani-dlg .ask .sugg span,
html.ded-worn #gani-testbar,
html.ded-worn #gani-flash,
html.ded-worn #pfx-editor .pfx-btn,
html.ded-worn #pfx-editor .pfx-tab,
html.ded-worn #pfx-editor .pfx-maintab,
html.ded-worn #pfx-editor .pfx-swatch,
html.ded-worn .ded-sb-thumb,
html.ded-worn .ded-sb-btn,
html.ded-worn #pfx-flash,
html.ded-worn .pfx-cd .pfx-cd-sw,
html.ded-worn .pfx-cd .pfx-cd-prev,
html.ded-worn #level-editor,
html.ded-worn #level-editor [id^="ed-"],
html.ded-worn .ed-btn,
html.ded-worn #ed-npc-tooltip,
html.ded-worn #login-container,
html.ded-worn .login-buttons button,
html.ded-worn .ded-head,
html.ded-worn .ded-list > div,
html.ded-worn #gani-dlg .hd,
html.ded-worn #pfx-editor .pfx-grp,
html.ded-worn #gani-ed .ge-pl div,
html.ded-worn #gani-dlg .ge-pl div,
html.ded-worn #gani-ed .ge-bot,
html.ded-worn #gani-dlg .ft,
html.ded-worn #gani-dlg .sect,
html.ded-worn .ded-section,
html.ded-worn #gani-ed .ge-sec,
html.ded-worn #pfx-editor,
html.ded-worn #gani-testbar .s,
html.ded-worn #gani-ed .ge-top {
  position: relative;
  border-color: var(--ded-line-ghost) !important;
}

/* ...except where it is wrong. `html.ded-worn #gani-ed .ge-zoom` is
   specificity 1-2-1 and the editor's own `#gani-ed .ge-zoom` is 1-1-0, so
   the rule above WON and turned five absolutely/fixed-positioned elements
   static: the zoom readout dropped into the middle of the canvas, and the
   context menu, the test bar and both toasts went with it. Their real
   positioning is restored here. Anything added to the groups above that
   is not statically positioned belongs in this list too. */
html.ded-worn #gani-ed .ge-zoom { position: absolute !important; }
/* In-game GUI controls (gui.js) are all absolutely positioned by script;
   the skinned ones carry .ded-btn / .ded-skin-* from the groups above. */
html.ded-worn #gui-container .gui-control { position: absolute; }
html.ded-worn #gani-ctx, html.ded-worn #gani-testbar, html.ded-worn #gani-flash, html.ded-worn #pfx-flash {
  position: fixed !important;
}

/* ── The masked overlay ──
   One rule, so the mask is defined exactly once for every group. */
html.ded-worn .ded-panel::before,
html.ded-worn .ded-btn::before,
html.ded-worn .ded-list::before,
html.ded-worn #gui-container .ded-skin-window::before,
html.ded-worn #gui-container .ded-skin-scroll::before,
html.ded-worn #gani-ed .ge-btn::before,
html.ded-worn #gani-dlg .ge-btn::before, html.ded-worn #gani-testbar .ge-btn::before,
html.ded-worn #gani-ed .ge-keys span::before,
html.ded-worn #gani-ed .ge-sp::before,
html.ded-worn #gani-ed .ge-fr::before,
html.ded-worn #gani-ed .ge-pl::before,
html.ded-worn #gani-dlg .ge-pl::before,
html.ded-worn #gani-ed .ge-zoom::before,
html.ded-worn #gani-ctx::before,
html.ded-worn #gani-dlg .box::before,
html.ded-worn #gani-dlg .prev::before,
html.ded-worn #gani-dlg .ask .opt::before,
html.ded-worn #gani-dlg .ask .sugg span::before,
html.ded-worn #gani-testbar::before,
html.ded-worn #gani-flash::before,
html.ded-worn #pfx-editor .pfx-btn::before,
html.ded-worn #pfx-editor .pfx-tab::before,
html.ded-worn #pfx-editor .pfx-maintab::before,
html.ded-worn #pfx-editor .pfx-swatch::before,
html.ded-worn .ded-sb-thumb::before,
html.ded-worn .ded-sb-btn::before,
html.ded-worn #pfx-flash::before,
html.ded-worn .pfx-cd .pfx-cd-sw::before,
html.ded-worn .pfx-cd .pfx-cd-prev::before,
html.ded-worn #level-editor::before,
html.ded-worn #level-editor [id^="ed-"]::before,
html.ded-worn .ed-btn::before,
html.ded-worn #ed-npc-tooltip::before,
html.ded-worn #login-container::before,
html.ded-worn .login-buttons button::before,
html.ded-worn .ded-head::before,
html.ded-worn .ded-list > div::before,
html.ded-worn #gani-dlg .hd::before,
html.ded-worn #pfx-editor .pfx-grp::before,
html.ded-worn #gani-ed .ge-pl div::before,
html.ded-worn #gani-dlg .ge-pl div::before,
html.ded-worn #gani-ed .ge-bot::before,
html.ded-worn #gani-dlg .ft::before,
html.ded-worn #gani-dlg .sect::before,
html.ded-worn .ded-section::before,
html.ded-worn #gani-ed .ge-sec::before,
html.ded-worn #pfx-editor::before,
html.ded-worn #gani-testbar .s::before,
html.ded-worn #gani-ed .ge-top::before,
html.ded-worn .ded-section::after,
html.ded-worn #gani-ed .ge-sec::after {
  content: "";
  position: absolute;
  pointer-events: none;
  -webkit-mask-image:
      radial-gradient(circle var(--ded-worn-corner, 12px) at 0% 0%, #000 0 55%, transparent 100%),
      radial-gradient(circle var(--ded-worn-corner, 12px) at 100% 0%, #000 0 55%, transparent 100%),
      radial-gradient(circle var(--ded-worn-corner, 12px) at 0% 100%, #000 0 55%, transparent 100%),
      radial-gradient(circle var(--ded-worn-corner, 12px) at 100% 100%, #000 0 55%, transparent 100%),
      var(--ded-wear-line);
  -webkit-mask-repeat: no-repeat, no-repeat, no-repeat, no-repeat, repeat;
  -webkit-mask-size: 100% 100%, 100% 100%, 100% 100%, 100% 100%, 160px 160px;
  -webkit-mask-position: 0 0, 0 0, 0 0, 0 0,
      var(--ded-wx, 0px) var(--ded-wy, 0px);
  mask-image:
      radial-gradient(circle var(--ded-worn-corner, 12px) at 0% 0%, #000 0 55%, transparent 100%),
      radial-gradient(circle var(--ded-worn-corner, 12px) at 100% 0%, #000 0 55%, transparent 100%),
      radial-gradient(circle var(--ded-worn-corner, 12px) at 0% 100%, #000 0 55%, transparent 100%),
      radial-gradient(circle var(--ded-worn-corner, 12px) at 100% 100%, #000 0 55%, transparent 100%),
      var(--ded-wear-line);
  mask-repeat: no-repeat, no-repeat, no-repeat, no-repeat, repeat;
  mask-size: 100% 100%, 100% 100%, 100% 100%, 100% 100%, 160px 160px;
  mask-position: 0 0, 0 0, 0 0, 0 0,
      var(--ded-wx, 0px) var(--ded-wy, 0px);
}

/* A worn box that SCROLLS (ui.js adopts it as .ded-cs) would carry its
   overlay along with the content; ui.js keeps --ded-sx/--ded-sy at the
   scroll offset so the trim stays on the box's edge. */
html.ded-worn .ded-cs::before { transform: translate(var(--ded-sx, 0px), var(--ded-sy, 0px)); }

/* Ring geometry: a 1px border on a box the size of the padding area, so
   the worn line lands on the host's own border run. */
html.ded-worn .ded-panel::before,
html.ded-worn .ded-btn::before,
html.ded-worn .ded-list::before,
html.ded-worn #gui-container .ded-skin-window::before,
html.ded-worn #gui-container .ded-skin-scroll::before,
html.ded-worn #gani-ed .ge-btn::before,
html.ded-worn #gani-dlg .ge-btn::before, html.ded-worn #gani-testbar .ge-btn::before,
html.ded-worn #gani-ed .ge-keys span::before,
html.ded-worn #gani-ed .ge-sp::before,
html.ded-worn #gani-ed .ge-fr::before,
html.ded-worn #gani-ed .ge-pl::before,
html.ded-worn #gani-dlg .ge-pl::before,
html.ded-worn #gani-ed .ge-zoom::before,
html.ded-worn #gani-ctx::before,
html.ded-worn #gani-dlg .box::before,
html.ded-worn #gani-dlg .prev::before,
html.ded-worn #gani-dlg .ask .opt::before,
html.ded-worn #gani-dlg .ask .sugg span::before,
html.ded-worn #gani-testbar::before,
html.ded-worn #gani-flash::before,
html.ded-worn #pfx-editor .pfx-btn::before,
html.ded-worn #pfx-editor .pfx-tab::before,
html.ded-worn #pfx-editor .pfx-maintab::before,
html.ded-worn #pfx-editor .pfx-swatch::before,
html.ded-worn .ded-sb-thumb::before,
html.ded-worn .ded-sb-btn::before,
html.ded-worn #pfx-flash::before,
html.ded-worn .pfx-cd .pfx-cd-sw::before,
html.ded-worn .pfx-cd .pfx-cd-prev::before,
html.ded-worn #level-editor::before,
html.ded-worn #level-editor [id^="ed-"]::before,
html.ded-worn .ed-btn::before,
html.ded-worn #ed-npc-tooltip::before,
html.ded-worn #login-container::before,
html.ded-worn .login-buttons button::before {
  inset: 0;
  border: 1px solid var(--ded-worn-trim, var(--ded-line-strong));
  border-radius: inherit;
  background: none;
}

/* Edge geometry. A single rule's worth of trim, so these use a filled
   strip rather than a border. */
html.ded-worn .ded-head::before,
html.ded-worn .ded-list > div::before,
html.ded-worn #gani-dlg .hd::before,
html.ded-worn #pfx-editor .pfx-grp::before,
html.ded-worn #gani-ed .ge-pl div::before,
html.ded-worn #gani-dlg .ge-pl div::before,
html.ded-worn .ded-section::after,
html.ded-worn #gani-ed .ge-sec::after {
  left: 0; right: 0; bottom: -1px; height: 1px;
  background: var(--ded-worn-trim, var(--ded-line-soft));
}
html.ded-worn #gani-ed .ge-bot::before,
html.ded-worn #gani-dlg .ft::before,
html.ded-worn #gani-dlg .sect::before,
html.ded-worn .ded-section::before,
html.ded-worn #gani-ed .ge-sec::before {
  left: 0; right: 0; top: -1px; height: 1px;
  background: var(--ded-worn-trim, var(--ded-line-soft));
}
html.ded-worn #gani-ed .ge-top::before {
  left: 0; right: 0; bottom: -2px; height: 2px;
  background: var(--ded-worn-trim, var(--ded-line-strong));
}

html.ded-worn #pfx-editor::before {
  top: 0; bottom: 0; left: -2px; width: 2px;
  background: var(--ded-worn-trim, var(--ded-line-strong));
}

html.ded-worn #gani-testbar .s::before {
  top: 0; bottom: 0; left: -1px; width: 1px;
  background: var(--ded-worn-trim, var(--ded-line-soft));
}



/* A straight edge has no corners to protect, and keeping them would
   leave a 330px rail intact at both ends. */
html.ded-worn .ded-head::before,
html.ded-worn .ded-list > div::before,
html.ded-worn #gani-dlg .hd::before,
html.ded-worn #pfx-editor .pfx-grp::before,
html.ded-worn #gani-ed .ge-pl div::before,
html.ded-worn #gani-dlg .ge-pl div::before,
html.ded-worn #gani-ed .ge-bot::before,
html.ded-worn #gani-dlg .ft::before,
html.ded-worn #gani-dlg .sect::before,
html.ded-worn .ded-section::before,
html.ded-worn #gani-ed .ge-sec::before,
html.ded-worn #pfx-editor::before,
html.ded-worn #gani-testbar .s::before,
html.ded-worn #gani-ed .ge-top::before,
html.ded-worn .ded-section::after,
html.ded-worn #gani-ed .ge-sec::after {
  --ded-worn-corner: 0px;
}
html.ded-worn .ded-btn::before,
html.ded-worn .ded-list > div::before,
html.ded-worn #gani-ed .ge-btn::before,
html.ded-worn #gani-dlg .ge-btn::before, html.ded-worn #gani-testbar .ge-btn::before,
html.ded-worn #gani-ed .ge-keys span::before,
html.ded-worn #gani-dlg .ask .sugg span::before,
html.ded-worn #pfx-editor .pfx-btn::before,
html.ded-worn #pfx-editor .pfx-tab::before,
html.ded-worn #pfx-editor .pfx-maintab::before,
html.ded-worn #pfx-editor .pfx-swatch::before,
html.ded-worn .ded-sb-thumb::before,
html.ded-worn .ded-sb-btn::before,
html.ded-worn .pfx-cd .pfx-cd-sw::before,
html.ded-worn .ed-btn::before,
html.ded-worn .login-buttons button::before {
  --ded-worn-corner: 5px;
}
html.ded-worn #gani-ed .ge-sp::before,
html.ded-worn #gani-ed .ge-fr::before,
html.ded-worn #gani-ctx::before,
html.ded-worn #gani-dlg .ask .opt::before,
html.ded-worn .pfx-cd .pfx-cd-prev::before,
html.ded-worn #gani-flash::before,
html.ded-worn #pfx-flash::before {
  --ded-worn-corner: 7px;
}

/* ── The blotch (fills) ──
   `isolation: isolate` makes the host a stacking context so that
   `z-index: -1` lands the blotch ABOVE the host's own background but
   BELOW its text and canvases. Without the isolation, -1 would drop it
   behind the background and nothing would show. */
html.ded-worn .ded-btn,
html.ded-worn .ded-list > div,
html.ded-worn .ded-head,
html.ded-worn #gani-ed .ge-btn,
html.ded-worn #gani-dlg .ge-btn, html.ded-worn #gani-testbar .ge-btn,
html.ded-worn #gani-ed .ge-keys span,
html.ded-worn #gani-ed .ge-sp,
html.ded-worn #gani-ed .ge-fr,
html.ded-worn #gani-ed .ge-pl div,
html.ded-worn #gani-dlg .ge-pl div,
html.ded-worn #gani-ctx div,
html.ded-worn #gani-dlg .ask .opt,
html.ded-worn #gani-dlg .ask .sugg span,
html.ded-worn #pfx-editor .pfx-btn,
html.ded-worn #pfx-editor .pfx-tab,
html.ded-worn #pfx-editor .pfx-maintab,
html.ded-worn #pfx-editor .pfx-grp-h,
html.ded-worn .ed-btn,
html.ded-worn .ded-sb-thumb,
html.ded-worn .ded-sb-btn,
html.ded-worn .login-buttons button {
  position: relative;
  isolation: isolate;
}
html.ded-worn .ded-btn::after,
html.ded-worn .ded-list > div::after,
html.ded-worn .ded-head::after,
html.ded-worn #gani-ed .ge-btn::after,
html.ded-worn #gani-dlg .ge-btn::after, html.ded-worn #gani-testbar .ge-btn::after,
html.ded-worn #gani-ed .ge-keys span::after,
html.ded-worn #gani-ed .ge-sp::after,
html.ded-worn #gani-ed .ge-fr::after,
html.ded-worn #gani-ed .ge-pl div::after,
html.ded-worn #gani-dlg .ge-pl div::after,
html.ded-worn #gani-ctx div::after,
html.ded-worn #gani-dlg .ask .opt::after,
html.ded-worn #gani-dlg .ask .sugg span::after,
html.ded-worn #pfx-editor .pfx-btn::after,
html.ded-worn #pfx-editor .pfx-tab::after,
html.ded-worn #pfx-editor .pfx-maintab::after,
html.ded-worn #pfx-editor .pfx-grp-h::after,
html.ded-worn .ed-btn::after,
html.ded-worn .ded-sb-thumb::after,
html.ded-worn .ded-sb-btn::after,
html.ded-worn .login-buttons button::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  border-radius: inherit;
  background-image: var(--ded-wear-blotch);
  background-repeat: repeat;
  background-size: 160px 160px;
  background-position: var(--ded-wx, 0px) var(--ded-wy, 0px);
}

/* Scrollbar thumbs. No pseudo-element is possible on one, so the blotch
   goes on directly as a background image over the flat red. The
   compatibility layer above deliberately forces Chrome off the standard
   `scrollbar-color` path precisely so that this can work.

   The URI is written out LITERALLY here rather than as var(--ded-wear-
   blotch). Custom properties do not reliably inherit into WebKit's
   scrollbar shadow pseudo-elements, so the var() resolved to nothing, the
   declaration was invalid, and the thumbs were never textured at all —
   which is why this looked like it had simply not been done.
   One strength for every level: a thumb is 10px wide and the difference
   between light and heavy is invisible at that size. */
html.ded-worn #gani-ed ::-webkit-scrollbar-thumb,
html.ded-worn #gani-dlg ::-webkit-scrollbar-thumb,
html.ded-worn #pfx-editor ::-webkit-scrollbar-thumb,
html.ded-worn #level-editor ::-webkit-scrollbar-thumb,
html.ded-worn .ded-panel ::-webkit-scrollbar-thumb,
html.ded-worn #gani-ed::-webkit-scrollbar-thumb,
html.ded-worn #pfx-editor::-webkit-scrollbar-thumb,
html.ded-worn #level-editor::-webkit-scrollbar-thumb {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='b' filterUnits='userSpaceOnUse' x='0' y='0' width='160' height='160' color-interpolation-filters='sRGB'><feTurbulence type='fractalNoise' baseFrequency='0.07' numOctaves='3' seed='23' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 0.07 0 0 0 0 0.025 0 0 0 0 0.02 1.7 0 0 0 -0.855'/></filter><rect width='160' height='160' filter='url(%23b)'/></svg>") !important;
  background-repeat: repeat !important;
  background-size: 160px 160px !important;
}

/* ── State ──
   Hover and selected used to be a border-colour change on the host. The
   host's border is a ghost now, so the colour goes to the overlay. */
html.ded-worn .ded-btn:hover::before,
html.ded-worn .ded-list > div:hover::before,
html.ded-worn .ded-dd > div.hi::before,
html.ded-worn #gani-ed .ge-btn:hover::before,
html.ded-worn #gani-dlg .ge-btn:hover::before, html.ded-worn #gani-testbar .ge-btn:hover::before,
html.ded-worn #gani-ed .ge-keys span:hover::before,
html.ded-worn #gani-ed .ge-sp:hover::before,
html.ded-worn #gani-ed .ge-fr:hover::before,
html.ded-worn #gani-ed .ge-pl div:hover::before,
html.ded-worn #gani-dlg .ge-pl div:hover::before,
html.ded-worn #gani-ctx div:hover::before,
html.ded-worn #gani-dlg .ask .opt:hover::before,
html.ded-worn #gani-dlg .ask .sugg span:hover::before,
html.ded-worn #pfx-editor .pfx-btn:hover::before,
html.ded-worn #pfx-editor .pfx-swatch:hover::before,
html.ded-worn .pfx-cd .pfx-cd-sw:hover::before,
html.ded-worn .ed-btn:hover::before,
html.ded-worn .login-buttons button:hover::before { --ded-worn-trim: var(--ded-accent); }
html.ded-worn .ded-btn.pri::before,
html.ded-worn .ded-btn.on::before,
html.ded-worn .ded-list > div.on::before,
html.ded-worn #gani-ed .ge-btn.on::before,
html.ded-worn #gani-ed .ge-btn.pri::before,
html.ded-worn #gani-dlg .ge-btn.on::before, html.ded-worn #gani-testbar .ge-btn.on::before,
html.ded-worn #gani-dlg .ge-btn.pri::before, html.ded-worn #gani-testbar .ge-btn.pri::before,
html.ded-worn #gani-ed .ge-keys span.cur::before,
html.ded-worn #gani-ed .ge-sp.on::before,
html.ded-worn #gani-ed .ge-fr.on::before,
html.ded-worn #gani-ed .ge-pl div.on::before,
html.ded-worn #gani-dlg .ge-pl div.on::before,
html.ded-worn #pfx-editor .pfx-tab.on::before,
html.ded-worn #pfx-editor .pfx-maintab.on::before,
html.ded-worn #pfx-editor .pfx-btn.pri::before,
html.ded-worn .ed-btn.active::before,
html.ded-worn .ed-btn.on::before,
html.ded-worn .ed-btn.pri::before    { --ded-worn-trim: var(--ded-accent-hot); }
/* Orange / green buttons keep their own trim colour on the overlay (the
   red state rules above set it on ::before, which outranks the host's). */
html.ded-worn .ded-btn.orange::before, html.ded-worn #gani-ed .ge-btn.orange::before,
html.ded-worn #gani-dlg .ge-btn.orange::before, html.ded-worn #pfx-editor .pfx-btn.orange::before,
html.ded-worn #level-editor .ed-btn.orange::before { --ded-worn-trim: var(--ded-org-line); }
html.ded-worn .ded-btn.orange:hover::before, html.ded-worn .ded-btn.orange.pri::before, html.ded-worn #gani-ed .ge-btn.orange:hover::before,
html.ded-worn #gani-ed .ge-btn.orange.pri::before, html.ded-worn #gani-dlg .ge-btn.orange:hover::before,
html.ded-worn #pfx-editor .pfx-btn.orange:hover::before, html.ded-worn #level-editor .ed-btn.orange:hover::before { --ded-worn-trim: var(--ded-org-accent); }
html.ded-worn .ded-btn.green::before, html.ded-worn #gani-ed .ge-btn.green::before,
html.ded-worn #gani-dlg .ge-btn.green::before, html.ded-worn #pfx-editor .pfx-btn.green::before,
html.ded-worn #level-editor .ed-btn.green::before { --ded-worn-trim: var(--ded-grn-line); }
html.ded-worn .ded-btn.green:hover::before, html.ded-worn .ded-btn.green.pri::before, html.ded-worn #gani-ed .ge-btn.green:hover::before,
html.ded-worn #gani-ed .ge-btn.green.pri::before, html.ded-worn #gani-dlg .ge-btn.green:hover::before,
html.ded-worn #pfx-editor .pfx-btn.green:hover::before, html.ded-worn #level-editor .ed-btn.green:hover::before { --ded-worn-trim: var(--ded-grn-accent); }
/* The login screen's Register is the green button too (index.html). */
html.ded-worn #btn-register::before { --ded-worn-trim: var(--ded-grn-line); }
html.ded-worn #btn-register:hover::before { --ded-worn-trim: var(--ded-grn-accent); }

/* ── Per-element offset ──
   Without this, every button of the same size erodes identically and the
   result reads as a repeating motif instead of as damage. Co-prime
   moduli on the two axes give a pattern that only repeats every 2·5·7 =
   70 siblings, from six cheap rules rather than any JavaScript. Declared
   on the ELEMENT, not on ::before, so the line mask and the fill blotch
   sample the same offset and a worn button looks like one object. */
html.ded-worn :nth-child(2n)   { --ded-wx: 71px; }
html.ded-worn :nth-child(5n+2) { --ded-wx: 19px; }
html.ded-worn :nth-child(5n+4) { --ded-wx: 113px; }
html.ded-worn :nth-child(3n)   { --ded-wy: 47px; }
html.ded-worn :nth-child(7n+3) { --ded-wy: 97px; }
html.ded-worn :nth-child(7n+5) { --ded-wy: 23px; }

/* =====================================================================
 * ONE FONT EVERYWHERE
 * =====================================================================
 * Every piece of UI uses the font the player NAMES are drawn in (Special
 * Elite — see drawNickname in client.js). !important so it beats the
 * inline `font-family`/`font:` declarations scattered through the editors.
 * Canvas text gets the same font from the CanvasRenderingContext2D patch
 * at the top of ui.js.
 *
 * Exempt: code. Script boxes (textareas), <pre> and <code> keep their
 * monospace, because indentation only lines up in a fixed-width font.
 * Anything else that needs it can opt out with class `ded-code`. */
html, body,
body *:not(textarea):not(pre):not(code):not(.ded-code) {
  font-family: 'Special Elite', 'Segoe UI', serif !important;
}

/* ── Level editor header ──
   Its inline style is a GREY gradient. The compat rule above only sets
   background-COLOR, and a gradient is a background-IMAGE drawn on top of
   it, so the grey survived. Same warm strip as the animation editor's. */
#level-editor > div:first-child {
  background: linear-gradient(180deg, #1a0d0b, #120706) !important;
  border-bottom: 2px solid var(--ded-line-strong) !important;
}
#level-editor > div:first-child > span:first-child {
  color: var(--ded-accent) !important;
  font-size: 15px !important; letter-spacing: 2px; text-transform: uppercase;
  text-shadow: 0 0 15px rgba(200,40,20,0.3);
}

/* ── Particle editor colour swatches ──
   A themed frame with the actual colour as a rounded chip inside. They
   used to BE .pfx-btn, whose `background: … !important` above painted
   over the colour set inline — every swatch showed an empty button. */
#pfx-editor .pfx-swatch {
  flex: 1; height: 22px; display: flex; cursor: pointer;
  padding: 3px !important;
  background: var(--ded-field) !important;
  border: 1px solid var(--ded-line-strong) !important;
  border-radius: var(--ded-r-sm) !important;
}
#pfx-editor .pfx-swatch:hover { border-color: var(--ded-accent) !important; box-shadow: var(--ded-glow); }
/* Color variety palette: a wrapping row of small swatches plus "+". */
#pfx-editor .pfx-pal { flex: 1 1 auto; min-width: 0; display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }
#pfx-editor .pfx-pal .pfx-palchip { flex: 0 0 24px; width: 24px; height: 20px; }
#pfx-editor .pfx-pal .pfx-btn { padding: 2px 8px !important; }
#pfx-editor .pfx-swatch > .pfx-chip {
  flex: 1; border-radius: 3px;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,0.45);
  pointer-events: none;
}

/* ── Particle editor bars ──
   The header, toolbar, Save row, tab strip, Preset row, layer tabs, colour
   block and footer are each built with an inline blue-grey background
   (#111114, #0e0e13, #0c0c10 …) that nothing here ever overrode. Same
   treatment as the animation editor: warm strip for the header, the panel
   black everywhere else, red trim under the tab strips. */
#pfx-editor { border-left-color: var(--ded-line-strong) !important; }
#pfx-editor > div:not(.pfx-body) {
  background: var(--ded-bg) !important;
  border-color: var(--ded-line-soft) !important;
}
#pfx-editor > div:first-child {
  background: linear-gradient(180deg, #1a0d0b, #120706) !important;
  border-bottom: 2px solid var(--ded-line-strong) !important;
}
#pfx-editor > div:first-child > span:first-child {
  color: var(--ded-accent) !important;
  font-size: 15px !important; letter-spacing: 2px; text-transform: uppercase;
  text-shadow: 0 0 15px rgba(200,40,20,0.3);
}
#pfx-editor > div:first-child > span:nth-child(2) { color: var(--ded-text-faint) !important; }
#pfx-editor > div:has(> .pfx-maintab),
#pfx-editor > #pfx-layers { border-bottom-color: var(--ded-line-strong) !important; }
#pfx-editor > div:last-child { color: var(--ded-text-faint) !important; }
/* The Save and Preset dropdowns sit outside .pfx-row, so they kept their
   inline blue-grey fill too. */
#pfx-editor select, #pfx-editor input[type=text], #pfx-editor input[type=number] {
  background-color: var(--ded-field) !important;
  border: 1px solid var(--ded-line) !important;
  color: var(--ded-text) !important;
  border-radius: var(--ded-r-sm) !important;
}

/* =====================================================================
 * SCROLLBARS DRAWN BY ui.js
 * =====================================================================
 * His Chrome ignores ::-webkit-scrollbar entirely, and the standard
 * properties can't texture anything or recolour the arrow buttons. ui.js
 * makes the native bar of every editor scroller transparent (`.ded-cs`,
 * which keeps its gutter so no layout moves) and lays its own rail over
 * that gutter. The rail's thumb and arrow keys are real elements, so they
 * take the worn trim + blotch like any button. The arrows are the number
 * spinner's key: red #781910 with a near-black glyph. */
:is(#gani-ed, #gani-dlg, #pfx-editor, #level-editor, .ded-panel, .ded-dd) .ded-cs,
:is(#gani-ed, #gani-dlg, #pfx-editor, #level-editor, .ded-panel, .ded-dd).ded-cs {
  scrollbar-color: transparent transparent !important;
}
.ded-sb {
  position: fixed !important; box-sizing: border-box; overflow: hidden;
  background: #070303; user-select: none; -webkit-user-select: none;
}
.ded-sb-v { border-left: 1px solid #241110; }
.ded-sb-h { border-top: 1px solid #241110; }
.ded-sb > div { position: absolute !important; box-sizing: border-box; }
.ded-sb-thumb {
  background-color: #781e14; border: 1px solid #781e14; border-radius: 4px;
  --ded-worn-trim: #c85032;
}
.ded-sb-thumb:hover, .ded-sb-thumb.drag { background-color: #962817; }
.ded-sb-btn {
  background-color: #781910; border: 1px solid #781910; border-radius: 2px;
  background-repeat: no-repeat; background-position: center; background-size: 8px 8px;
  --ded-worn-trim: #c85032;
}
.ded-sb-btn:hover { background-color: #8e2014; }
.ded-sb-btn.press { background-color: #c85032; }
.ded-sb-v .ded-sb-btn.a { background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><path d='M5 1.5 L9.5 8 L0.5 8 Z' fill='%23140606'/></svg>"); }
.ded-sb-v .ded-sb-btn.b { background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><path d='M5 8.5 L0.5 2 L9.5 2 Z' fill='%23140606'/></svg>"); }
.ded-sb-h .ded-sb-btn.a { background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><path d='M1.5 5 L8 0.5 L8 9.5 Z' fill='%23140606'/></svg>"); }
.ded-sb-h .ded-sb-btn.b { background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><path d='M8.5 5 L2 0.5 L2 9.5 Z' fill='%23140606'/></svg>"); }

/* =====================================================================
 * DROPDOWNS
 * =====================================================================
 * The closed box gets the number spinner's red key with a down arrow. The
 * OPEN list is ui.js's `.ded-list.ded-dd` — the same component as "Pieces
 * in this frame", so it inherits the worn frame, row dividers and the worn
 * hover highlight from the rules above. */
:is(#gani-ed, #gani-dlg, #pfx-editor, #level-editor, .ded-panel) select:not([multiple]):not([size]) {
  appearance: none !important; -webkit-appearance: none !important;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='15' height='24'><rect x='0' y='0' width='15' height='24' fill='%23781910'/><path d='M4 10 L11 10 L7.5 15 Z' fill='%23140606'/></svg>") !important;
  background-repeat: no-repeat !important;
  background-position: right center !important;
  background-size: 15px 100% !important;
  padding-right: 19px !important;
  cursor: pointer;
}
.ded-dd {
  position: fixed !important; z-index: 2147482000;
  max-height: 300px; overflow-y: auto; overflow-x: hidden;
  box-shadow: 0 6px 18px rgba(0,0,0,.65);
  font-size: 11px;
}
.ded-dd > div { padding: 4px 9px; }
.ded-dd > div.hi { background: #240f0c; color: var(--ded-accent-hot); }
.ded-dd > div.on { background: var(--ded-btn-pri); color: #ffc8a0; }
.ded-dd > div.on.hi { background: var(--ded-btn-pri-hot); }
.ded-dd > div.off { opacity: .4; cursor: default; }
.ded-dd > .grp {
  display: block; padding: 5px 9px 2px; color: var(--ded-accent);
  font-size: 10px; letter-spacing: 1px; text-transform: uppercase;
}

/* =====================================================================
 * WORN FIELDS
 * =====================================================================
 * The note in the worn-trim section kept form controls pristine; James
 * wants them worn like everything else. An <input>, <select>, <textarea>
 * or <canvas> cannot carry the ::before overlay the rest of the system
 * uses, so the broken line is a BORDER-IMAGE instead: the same noise as
 * --ded-wear-line, baked red over the ghost colour, sliced 1px from each
 * edge and tiled along it. `.ded-wf` opts any other element in. */
html.ded-worn :is(#gani-ed, #gani-dlg, #pfx-editor, #level-editor, .ded-panel)
  :is(input[type=text], input[type=number], input[type=password], input:not([type]), select, textarea),
html.ded-worn .ded-wf {
  border-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='w' filterUnits='userSpaceOnUse' x='0' y='0' width='160' height='160' color-interpolation-filters='sRGB'><feTurbulence type='fractalNoise' baseFrequency='0.055' numOctaves='3' seed='17' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 0.47 0 0 0 0 0.118 0 0 0 0 0.078 4.5 0 0 0 -1.14'/></filter><rect width='160' height='160' fill='%231d0a08'/><rect width='160' height='160' filter='url(%23w)'/></svg>") 1 / 1px / 0 repeat !important;
}
html.ded-worn :is(#gani-ed, #gani-dlg, #pfx-editor, #level-editor, .ded-panel)
  :is(input[type=text], input[type=number], input[type=password], input:not([type]), select, textarea):is(:focus, :hover) {
  border-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='w' filterUnits='userSpaceOnUse' x='0' y='0' width='160' height='160' color-interpolation-filters='sRGB'><feTurbulence type='fractalNoise' baseFrequency='0.055' numOctaves='3' seed='17' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 0.784 0 0 0 0 0.314 0 0 0 0 0.196 4.5 0 0 0 -0.8'/></filter><rect width='160' height='160' fill='%23401410'/><rect width='160' height='160' filter='url(%23w)'/></svg>") 1 / 1px / 0 repeat !important;
}

/* Test-bar buttons: the shared .ge-btn rules above now include
   #gani-testbar; the editor's own stylesheet never did, so the cursor
   came from nowhere. */
#gani-testbar .ge-btn { cursor: pointer; }

/* Animation editor's "open an animation" button. It is the name display
   too, so a long gani name must stop at the box: clipped with an ellipsis
   (full name in the tooltip), at field size rather than button size, and
   in its real case — it is a filename. The red key matches a dropdown,
   which is what it behaves as. */
#gani-ed #ge-open {
  display: inline-flex !important; align-items: center;
  font-size: 11px !important; text-transform: none !important; letter-spacing: 0 !important;
  padding-right: 22px !important;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='15' height='24'><rect x='0' y='0' width='15' height='24' fill='%23781910'/><path d='M4 10 L11 10 L7.5 15 Z' fill='%23140606'/></svg>") !important;
  background-repeat: no-repeat !important;
  background-position: right center !important;
  background-size: 15px 100% !important;
  min-width: 0; overflow: hidden;
}
#gani-ed #ge-openlabel {
  display: block; min-width: 0; flex: 1 1 auto;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--ded-accent);
  /* The button's line-height:1 clipped underscores off filenames. */
  line-height: 1.5; margin: -3px 0;
}

/* =====================================================================
 * ROUGH EDGES — html.ded-fogedge (UI.setFogEdge; the class kept its old name).
 * =====================================================================
 * The window's edge is ROUGH paint, not a ruled line: the interior colour
 * (--ded-fog-rgb = --ded-bg) runs out past the border in a ragged, torn
 * band with splatter flecks thrown beyond it, and breaks the red trim in
 * places. (The previous version scattered soft cloud puffs along the edges,
 * which read as fog stuck to the box, not as a rough edge.)
 *
 * It is ONE SVG mask (--ded-rough-mask), rendered at the window's own size
 * (mask-size 100%), so the edge is continuous noise all the way round —
 * no tile repeats, any size, corners included. Inside it (all feTurbulence,
 * user units = CSS px, so the grain is the same on every window):
 *   - outer edge: the box grown ~8px and blurred, plus streaky noise +
 *     a slow wander of the edge depth, thresholded hard;
 *   - splatter: a finer noise thresholded against a wider, softer field,
 *     so flecks crowd the edge and thin out with distance;
 *   - inner edge: ragged too, but clamped to the box (never a gap outside
 *     it) and never more than 6px in (never over content). Where it
 *     reaches the box edge the trim shows; elsewhere the paint covers it.
 * Generated by gen_rough_edges.js (scratchpad) — regenerate, don't
 * hand-edit. Rasterised by the browser once per window size; static.
 * Lives on ::after (the worn trim is ::before), 26px out from the padding
 * box. Never sets position — every window here is already positioned. A
 * window with overflow:hidden clips its own ::after, so those get the
 * plain bleed (--ded-fog-glow, a box-shadow) instead.
 * ===================================================================== */
:root {
  --ded-fog-rgb: 16 8 7;
  --ded-fog-glow: 0 0 6px 2px rgb(16 8 7 / .75), 0 0 16px 6px rgb(16 8 7 / .45), 0 0 30px 10px rgb(16 8 7 / .2);
  --ded-rough-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'><filter id='f' filterUnits='userSpaceOnUse' x='0' y='0' width='100%' height='100%' color-interpolation-filters='sRGB'><feTurbulence type='fractalNoise' baseFrequency='0.03 0.12' numOctaves='4' seed='7' result='nst'/><feColorMatrix in='nst' type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0' result='ns'/><feTurbulence type='fractalNoise' baseFrequency='0.025' numOctaves='2' seed='13' result='nlt'/><feColorMatrix in='nlt' type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0' result='nl'/><feComposite in='ns' in2='nl' operator='arithmetic' k2='0.550' k3='0.450' result='n1'/><feTurbulence type='fractalNoise' baseFrequency='0.041999999999999996 0.12' numOctaves='4' seed='41' result='n2t'/><feColorMatrix in='n2t' type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0' result='n2'/><feMorphology in='SourceGraphic' operator='erode' radius='18' result='oe'/><feGaussianBlur in='oe' stdDeviation='4' result='ob'/><feComposite in='ob' in2='n1' operator='arithmetic' k2='1' k3='2.000' k4='-1.000' result='ov'/><feComponentTransfer in='ov' result='o'><feFuncA type='linear' slope='9' intercept='-4.000'/></feComponentTransfer><feTurbulence type='fractalNoise' baseFrequency='0.1' numOctaves='3' seed='77' result='nspt'/><feColorMatrix in='nspt' type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0' result='nsp'/><feMorphology in='SourceGraphic' operator='erode' radius='11' result='se'/><feGaussianBlur in='se' stdDeviation='5' result='sb'/><feComposite in='sb' in2='nsp' operator='arithmetic' k2='1' k3='2.600' k4='-1.300' result='spv'/><feComponentTransfer in='spv' result='sp'><feFuncA type='linear' slope='12' intercept='-5.500'/></feComponentTransfer><feMerge result='om'><feMergeNode in='o'/><feMergeNode in='sp'/></feMerge><feMorphology in='SourceGraphic' operator='erode' radius='25' result='ie'/><feGaussianBlur in='ie' stdDeviation='1.5' result='ib'/><feComposite in='ib' in2='n2' operator='arithmetic' k2='1' k3='1.400' k4='-0.700' result='iv'/><feComponentTransfer in='iv' result='i'><feFuncA type='linear' slope='9' intercept='-4.000'/></feComponentTransfer><feMorphology in='SourceGraphic' operator='erode' radius='25' result='box'/><feMorphology in='SourceGraphic' operator='erode' radius='32' result='deep'/><feComposite in='i' in2='box' operator='in' result='ic'/><feComposite in='ic' in2='deep' operator='over' result='ic2'/><feComposite in='om' in2='ic2' operator='out'/></filter><rect width='100%' height='100%' fill='white' filter='url(%23f)'/></svg>");
}
html.ded-fogedge .ded-panel::after,
html.ded-fogedge #login-container::after {
  content: "";
  position: absolute;
  inset: -26px;
  pointer-events: none;
  z-index: 3;
  background: rgb(var(--ded-fog-rgb));
  -webkit-mask: var(--ded-rough-mask) 0 0 / 100% 100% no-repeat;
  mask: var(--ded-rough-mask) 0 0 / 100% 100% no-repeat;
}
/* In-game GUI windows (gui.js GuiWindowCtrl) — same edge, so every window
   in the game matches. They are skinned from a sprite frame (no CSS border),
   so this variant (--ded-rough-mask-thin: bw 0, deep 3) only reaches 3px in
   and the metal frame art survives. They clip their children
   (overflow:hidden), which would clip the ::after too: overflow:clip with a
   26px clip margin lets just the edge paint outside. !important because
   clipchildren sets overflow inline. */
:root { --ded-rough-mask-thin: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'><filter id='f' filterUnits='userSpaceOnUse' x='0' y='0' width='100%' height='100%' color-interpolation-filters='sRGB'><feTurbulence type='fractalNoise' baseFrequency='0.03 0.12' numOctaves='4' seed='7' result='nst'/><feColorMatrix in='nst' type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0' result='ns'/><feTurbulence type='fractalNoise' baseFrequency='0.025' numOctaves='2' seed='13' result='nlt'/><feColorMatrix in='nlt' type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0' result='nl'/><feComposite in='ns' in2='nl' operator='arithmetic' k2='0.550' k3='0.450' result='n1'/><feTurbulence type='fractalNoise' baseFrequency='0.041999999999999996 0.12' numOctaves='4' seed='41' result='n2t'/><feColorMatrix in='n2t' type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0' result='n2'/><feMorphology in='SourceGraphic' operator='erode' radius='18' result='oe'/><feGaussianBlur in='oe' stdDeviation='4' result='ob'/><feComposite in='ob' in2='n1' operator='arithmetic' k2='1' k3='2.000' k4='-1.000' result='ov'/><feComponentTransfer in='ov' result='o'><feFuncA type='linear' slope='9' intercept='-4.000'/></feComponentTransfer><feTurbulence type='fractalNoise' baseFrequency='0.1' numOctaves='3' seed='77' result='nspt'/><feColorMatrix in='nspt' type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0' result='nsp'/><feMorphology in='SourceGraphic' operator='erode' radius='11' result='se'/><feGaussianBlur in='se' stdDeviation='5' result='sb'/><feComposite in='sb' in2='nsp' operator='arithmetic' k2='1' k3='2.600' k4='-1.300' result='spv'/><feComponentTransfer in='spv' result='sp'><feFuncA type='linear' slope='12' intercept='-5.500'/></feComponentTransfer><feMerge result='om'><feMergeNode in='o'/><feMergeNode in='sp'/></feMerge><feMorphology in='SourceGraphic' operator='erode' radius='26' result='ie'/><feGaussianBlur in='ie' stdDeviation='1.5' result='ib'/><feComposite in='ib' in2='n2' operator='arithmetic' k2='1' k3='1.400' k4='-0.700' result='iv'/><feComponentTransfer in='iv' result='i'><feFuncA type='linear' slope='9' intercept='-4.000'/></feComponentTransfer><feMorphology in='SourceGraphic' operator='erode' radius='26' result='box'/><feMorphology in='SourceGraphic' operator='erode' radius='29' result='deep'/><feComposite in='i' in2='box' operator='in' result='ic'/><feComposite in='ic' in2='deep' operator='over' result='ic2'/><feComposite in='om' in2='ic2' operator='out'/></filter><rect width='100%' height='100%' fill='white' filter='url(%23f)'/></svg>"); }
html.ded-fogedge .gui-window {
  overflow: clip !important;
  overflow-clip-margin: 26px;
}
html.ded-fogedge .gui-window::after {
  content: "";
  position: absolute;
  inset: -26px;
  pointer-events: none;
  z-index: 3;
  background: rgb(var(--ded-fog-rgb));
  -webkit-mask: var(--ded-rough-mask-thin) 0 0 / 100% 100% no-repeat;
  mask: var(--ded-rough-mask-thin) 0 0 / 100% 100% no-repeat;
}
/* The editors: the level and particle editors are panels docked to the
   right, so only their left edge shows (the rest of the ring is off
   screen); the animation editor fills the screen, so its pop-up dialogs
   get it instead. All three are already positioned. */
html.ded-fogedge #level-editor::after,
html.ded-fogedge #pfx-editor::after,
html.ded-fogedge #gani-dlg .box::after {
  content: "";
  position: absolute;
  inset: -26px;
  pointer-events: none;
  z-index: 3;
  background: rgb(var(--ded-fog-rgb));
  -webkit-mask: var(--ded-rough-mask) 0 0 / 100% 100% no-repeat;
  mask: var(--ded-rough-mask) 0 0 / 100% 100% no-repeat;
}
html.ded-fogedge #gani-dlg .box,
html.ded-fogedge #pfx-editor,
html.ded-fogedge #level-editor { box-shadow: none !important; }
/* a straight drop shadow would show through the gaps in the rough edge */
html.ded-fogedge .ded-panel,
html.ded-fogedge #login-container { box-shadow: inset 0 0 60px rgba(80,10,5,0.10); }

/* =====================================================================
 * IN-GAME GUI SKIN — gui.js controls on the default -GUI/Profiles
 * =====================================================================
 * A GuiControl profile whose bitmap is a reserved skin name (Deden_Window,
 * Deden_Button1/2/3, Deden_Scroll, …; see DED_SKINS in gui.js) is drawn
 * with these components instead of a sliced sprite. Wherever a ded-*
 * class exists it is used as is (.ded-head, .ded-btn, .ded-input, .ded-list
 * .ded-dd, the .ded-sb rails); the window and scroll box restate .ded-panel
 * / .ded-list on their own class rather than carrying them, because those
 * two are ui.js scrollbar-scan roots and a game window would be rescanned
 * on every style change inside it.
 * Everything is under #gui-container so it outranks gui.js's own injected
 * rules (which come later) without !important. */

/* Window = .ded-panel. Title bar = .ded-head (gui.js adds the class). */
#gui-container .gui-window.ded-skin-window {
  background: var(--ded-bg);
  border: 1px solid var(--ded-line-strong);
  border-radius: var(--ded-r);
  color: var(--ded-text);
  box-shadow: var(--ded-drop), 0 0 40px rgba(0,0,0,0.8), inset 0 0 60px rgba(80,10,5,0.10);
}
#gui-container .ded-skin-window > .gui-window-title .gui-text-content {
  overflow: hidden; text-overflow: ellipsis;
}
/* The skinned window has a real 1px trim, so it takes the full rough-edge
   mask (bw 1, like .ded-panel), not the thin one made for sprite frames. */
html.ded-fogedge #gui-container .gui-window.ded-skin-window::after {
  -webkit-mask: var(--ded-rough-mask) 0 0 / 100% 100% no-repeat;
  mask: var(--ded-rough-mask) 0 0 / 100% 100% no-repeat;
}
html.ded-fogedge #gui-container .gui-window.ded-skin-window { box-shadow: inset 0 0 60px rgba(80,10,5,0.10); }

/* Buttons and tabs = .ded-btn (.orange / .green, .on = selected tab).
   Script sets the size, so no padding to squeeze the label. */
#gui-container .gui-button.ded-btn,
#gui-container .gui-tab-item.ded-btn {
  padding: 0 4px;
  white-space: nowrap;
}
#gui-container .gui-tab-item.ded-btn {
  display: flex; align-items: center; justify-content: center;
  margin-right: 2px;
  user-select: none;
}
/* ...drawn as the particle editor's tabs (#pfx-editor .pfx-tab): a raised
   strip of top-rounded tabs with dim labels, the selected one filled red
   (Deden_Tabs) or orange (Deden_Tabs2). */
#gui-container .gui-tab-item.ded-tab {
  background: var(--ded-bg-raised);
  border: 1px solid var(--ded-line);
  border-radius: var(--ded-r-sm) var(--ded-r-sm) 0 0;
  color: var(--ded-text-dim);
  box-shadow: none;
  --ded-worn-trim: var(--ded-line);
}
#gui-container .gui-tab-item.ded-tab:hover { background: var(--ded-btn-hover); color: var(--ded-text); border-color: var(--ded-accent); }
#gui-container .gui-tab-item.ded-tab:active { transform: none; box-shadow: none; }
#gui-container .gui-tab-item.ded-tab.on { background: var(--ded-btn-pri); border-color: var(--ded-accent); color: #ffc8a0; --ded-worn-trim: var(--ded-accent); }
#gui-container .gui-tab-item.ded-tab.orange.on { background: var(--ded-org-pri); border-color: var(--ded-org-accent); color: var(--ded-org-text); --ded-worn-trim: var(--ded-org-accent); }

/* Scroll box = .ded-list's sunken well; rails = ui.js .ded-sb, laid inside
   the control (UI.localScrollbar) instead of fixed on <body>. */
#gui-container .ded-skin-scroll {
  background: var(--ded-bg-sunken);
  border: 1px solid var(--ded-line-soft);
  border-radius: var(--ded-r-sm);
  /* the inner content area scrolls; the box itself never shows a native bar
     (clipchildren = false only drops the inline overflow back to .gui-scroll's auto) */
  overflow: hidden;
}
#gui-container .ded-skin-scroll.ded-skin-noborder,
html.ded-worn #gui-container .ded-skin-scroll.ded-skin-noborder { border-color: transparent !important; }
html.ded-worn #gui-container .ded-skin-scroll.ded-skin-noborder::before { display: none; }
.ded-sb.ded-sb-local { position: absolute !important; z-index: 2; }

/* Text list rows = .ded-list rows' colours (no divider: row height is
   the script's). */
#gui-container .ded-skin-row { color: var(--ded-text); }
#gui-container .ded-skin-row:hover { background: #240f0c; color: var(--ded-accent-hot); }
#gui-container .ded-skin-row.on { background: var(--ded-btn-pri); color: #ffc8a0; }

/* Popup menu's closed box = an editor <select>: field + the red key. */
#gui-container .ded-skin-select {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='15' height='24'><rect x='0' y='0' width='15' height='24' fill='%23781910'/><path d='M4 10 L11 10 L7.5 15 Z' fill='%23140606'/></svg>");
  background-repeat: no-repeat;
  background-position: right center;
  background-size: 15px 100%;
  padding-right: 19px;
}

/* =====================================================================
 * MOBILE CONTROLS  (mobile.js)
 * =====================================================================
 * Only exists on a phone/tablet (html.ded-mobile). The buttons are plain
 * .ded-btn — red / .orange / .green and the worn trim come from above —
 * made round and laid out for thumbs. #ded-touch sits OUTSIDE the scaled
 * game wrapper, so these sizes are real screen pixels.
 * ===================================================================== */
html.ded-mobile #game-wrapper .resize-handle { display: none; }
html.ded-mobile #game-wrapper {
  touch-action: none;
  -webkit-user-select: none; user-select: none;
  -webkit-touch-callout: none;
}
/* The chat bar lives inside the scaled wrapper: move it to the top, out
   from under the joystick, and size it back up to a real ~17px so iOS
   doesn't zoom the page when it takes focus. */
html.ded-mobile #chat-input {
  top: calc(8px / var(--ded-ui-scale, 1)) !important;
  bottom: auto !important;
  left: 50% !important;
  transform: translateX(-50%);
  width: min(92%, calc(560px / var(--ded-ui-scale, 1))) !important;
  height: calc(40px / var(--ded-ui-scale, 1)) !important;
  font-size: calc(17px / var(--ded-ui-scale, 1)) !important;
}

#ded-touch {
  position: fixed; inset: 0;
  z-index: 60;
  pointer-events: none;
  -webkit-user-select: none; user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}
#ded-touch.dt-hidden { display: none; }
/* Script control (DedMobile / hide*MobileControls builtins). */
#ded-touch.dt-hide-left .dt-stick, #ded-touch.dt-hide-left .dt-left,
#ded-touch.dt-hide-right .dt-pad,
#ded-touch.dt-hide-top .dt-top { display: none; }
#ded-touch.dt-inactive { opacity: .35; }
#ded-touch.dt-inactive * { pointer-events: none !important; }
#ded-touch > * , #ded-touch .ded-btn { pointer-events: auto; touch-action: none; }

/* Joystick: a sunken well with the red trim, and a filled red knob. */
#ded-touch .dt-stick {
  position: absolute;
  left: calc(18px + env(safe-area-inset-left, 0px));
  bottom: calc(18px + env(safe-area-inset-bottom, 0px));
  width: 136px; height: 136px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(7,3,3,.35) 0 40%, rgba(16,8,7,.72) 72%);
  border: 2px solid var(--ded-line-strong);
  box-shadow: var(--ded-drop), inset 0 0 18px rgba(0,0,0,.7);
  opacity: .8;
  display: flex; align-items: center; justify-content: center;
}
#ded-touch .dt-stick.dt-press { opacity: 1; border-color: var(--ded-accent); }
#ded-touch .dt-knob {
  width: 58px; height: 58px; border-radius: 50%;
  background: var(--ded-btn-pri);
  border: 1px solid var(--ded-accent);
  box-shadow: var(--ded-drop), inset 0 0 10px rgba(0,0,0,.45);
  pointer-events: none;
}
#ded-touch .dt-stick.dt-press .dt-knob { background: var(--ded-btn-pri-hot); box-shadow: var(--ded-drop), var(--ded-glow); }

/* Round buttons (pad + left pair). */
#ded-touch .dt-pad .ded-btn, #ded-touch .dt-left .ded-btn {
  position: absolute;
  width: 54px; height: 54px;
  padding: 4px 0 0;
  border-radius: 50%;
  font-size: 12px; letter-spacing: .5px;
  opacity: .88;
  --ded-worn-corner: 0px;
}

/* Left thumb: Grab / Reload / Shove in a row above the joystick. */
#ded-touch .dt-left {
  position: absolute;
  left: calc(18px + env(safe-area-inset-left, 0px));
  bottom: calc(166px + env(safe-area-inset-bottom, 0px));
  width: 164px; height: 50px;
  pointer-events: none;
}
#ded-touch .dt-util { width: 48px !important; height: 48px !important; font-size: 10px !important; letter-spacing: 0 !important; bottom: 0; }
#ded-touch .dt-grab   { left: 0; }
#ded-touch .dt-reload { left: 58px; }
#ded-touch .dt-shove  { left: 116px; }
/* Sprinting: the movement ring lights green. */
#ded-touch .dt-stick.dt-sprinting { border-color: var(--ded-grn-accent); box-shadow: var(--ded-drop), var(--ded-grn-glow), inset 0 0 18px rgba(0,0,0,.7); }
#ded-touch .dt-stick.dt-sprinting .dt-knob { background: var(--ded-grn-pri); border-color: var(--ded-grn-accent); }

/* Right thumb: the aim stick in the corner, skills 1-4 on an arc round it
   (radius 112px, 180°→75°), Roll + Sprint in a row above. */
#ded-touch .dt-pad {
  position: absolute;
  right: calc(8px + env(safe-area-inset-right, 0px));
  bottom: calc(8px + env(safe-area-inset-bottom, 0px));
  width: 222px; height: 276px;
  pointer-events: none;
}
#ded-touch .dt-aim {
  position: absolute; right: 12px; bottom: 12px;
  width: 128px; height: 128px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(7,3,3,.35) 0 40%, rgba(16,8,7,.72) 72%);
  border: 2px solid var(--ded-line-strong);
  box-shadow: var(--ded-drop), inset 0 0 18px rgba(0,0,0,.7);
  opacity: .85;
  display: flex; align-items: center; justify-content: center;
  pointer-events: auto; touch-action: none;
}
#ded-touch .dt-aim.dt-press { opacity: 1; border-color: var(--ded-accent-hot); }
/* Knob = a crosshair, so it reads as "aim" next to the movement stick. */
#ded-touch .dt-aimknob {
  position: relative;
  width: 54px; height: 54px; border-radius: 50%;
  background: var(--ded-btn-pri);
  border: 1px solid var(--ded-accent);
  box-shadow: var(--ded-drop), inset 0 0 10px rgba(0,0,0,.45);
  pointer-events: none;
}
#ded-touch .dt-aimknob::before, #ded-touch .dt-aimknob::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  background: #ffc8a0; opacity: .75;
  transform: translate(-50%, -50%);
}
#ded-touch .dt-aimknob::before { width: 22px; height: 2px; }
#ded-touch .dt-aimknob::after  { width: 2px; height: 22px; }
#ded-touch .dt-aim.dt-press .dt-aimknob { background: var(--ded-btn-pri-hot); box-shadow: var(--ded-drop), var(--ded-glow); }

#ded-touch .dt-skill { font-size: 18px !important; padding-top: 6px !important; }
#ded-touch .dt-s1 { right: 161px; bottom: 49px; }
#ded-touch .dt-s2 { right: 141px; bottom: 113px; }
#ded-touch .dt-s3 { right: 87px;  bottom: 154px; }
#ded-touch .dt-s4 { right: 20px;  bottom: 157px; }

#ded-touch .dt-roll   { right: 80px; bottom: 222px; }
#ded-touch .dt-sprint { right: 14px; bottom: 222px; }

/* Menu row, top right (the HUD's hotbar owns the top left/centre). */
#ded-touch .dt-top {
  position: absolute;
  top: calc(6px + env(safe-area-inset-top, 0px));
  right: calc(8px + env(safe-area-inset-right, 0px));
  display: flex; gap: 8px;
}
#ded-touch .dt-top .ded-btn { padding: 9px 14px 7px; font-size: 14px; opacity: .9; }

/* One-line help strip (rotate / tap for full screen / iPhone home screen). */
#ded-touch .dt-hint {
  position: absolute;
  top: calc(56px + env(safe-area-inset-top, 0px));
  left: 50%; transform: translateX(-50%);
  max-width: calc(100% - 32px);
  padding: 8px 14px 6px;
  background: rgba(16,8,7,.88);
  border: 1px solid var(--ded-line-strong);
  border-radius: var(--ded-r-sm);
  box-shadow: var(--ded-drop);
  font-family: var(--ded-font-ui);
  font-size: 14px; letter-spacing: .5px; text-align: center;
  color: var(--ded-text);
  pointer-events: none;
}

/* Touch has no hover: the pressed look is driven by .dt-press instead
   (and :hover would otherwise stick after a tap). */
#ded-touch .ded-btn:hover { background: var(--ded-btn); color: var(--ded-text-dim); }
#ded-touch .ded-btn.pri:hover, #ded-touch .ded-btn.on:hover { background: var(--ded-btn-pri); color: #ffc8a0; box-shadow: var(--ded-drop); }
#ded-touch .ded-btn.orange:hover { background: var(--ded-org-btn); color: var(--ded-org-accent); }
#ded-touch .ded-btn.green:hover { background: var(--ded-grn-btn); color: var(--ded-grn-accent); }
#ded-touch .ded-btn.green.on:hover { background: var(--ded-grn-pri); color: var(--ded-grn-text); }
#ded-touch .ded-btn.dt-press { opacity: 1; transform: translateY(1px); box-shadow: var(--ded-drop-press), var(--ded-glow); background: var(--ded-btn-pri-hot); color: #ffd8b8; }
#ded-touch .ded-btn.orange.dt-press { background: var(--ded-org-pri-hot); color: var(--ded-org-text); box-shadow: var(--ded-drop-press), var(--ded-org-glow); }
#ded-touch .ded-btn.green.dt-press { background: var(--ded-grn-pri-hot); color: var(--ded-grn-text); box-shadow: var(--ded-drop-press), var(--ded-grn-glow); }
