/* Elbera dev shell + the few CSS affordances the retail UI has no art for.
 *
 * WHAT IS IN HERE, and how it is classified (tools/audit/unsourced.py does
 * NOT scan CSS, so this header is the classification):
 *
 *  - The DEV SHELL: #hud, #help, #online-label, #settings-btn, the loading
 *    spinner, the character-select and creation screens' chrome. Retail has
 *    no counterpart for any of it -- there is no in-game HUD strip, no
 *    "online" toggle, no browser settings button. Every colour, radius and
 *    padding in those rules is AUTHORED and cannot be otherwise.
 *
 *  - HOVER affordances (.shortcut-slot:hover, .inv-cell:hover,
 *    .doll-slot:hover). Retail marks a hovered slot by swapping the control's
 *    TEXTURE, which NWindow does natively; no colour exists to port. The
 *    1px inset ring is AUTHORED.
 *
 *  - #c9a959 (15 uses) is the dev shell's brand gold and is AUTHORED. It is
 *    NOT retail's tan: the decoded palette's equivalent is #B09B79
 *    (Interface.xdat, 121 controls; ClanWnd.uc 'Gold'), and every place the
 *    PORTED UI paints text now reads its colour through Layout.textColor /
 *    Layout.native instead of a literal (editor/world/js/ui is at zero
 *    unsourced literals). This file is deliberately the exception: it is the
 *    scaffolding around the port, not the port.
 *
 *  - Layout that mirrors a decoded value belongs in js/ui, not here. If a
 *    rule in this file starts positioning a RETAIL window, move it.
 */

html, body { margin: 0; height: 100%; overflow: hidden; background: #0b0d12; }
#view { position: fixed; inset: 0; width: 100%; height: 100%; display: block; }

#hud {
  position: fixed; top: 0; left: 0; right: 0; z-index: 40;
  display: none; align-items: center; gap: 14px;
  padding: 8px 14px;
  font: 13px/1.4 -apple-system, "Segoe UI", sans-serif;
  color: #cfd4de;
  background: linear-gradient(rgba(8,10,14,.85), rgba(8,10,14,0));
  pointer-events: none;
}
/* z 40, above the WndMgr windows: the sourced StatusWnd dock (444,0 —
   WindowsInfo.ini) overlaps this dev-only strip otherwise. */
#hud.dev-visible { display: flex; }
#hud .brand { font-weight: 600; color: #c9a959; letter-spacing: .04em; }
#hud label { pointer-events: auto; display: flex; align-items: center; gap: 6px; }
#hud select {
  background: #161a22; color: #cfd4de; border: 1px solid #333a48;
  border-radius: 4px; padding: 3px 6px; font: inherit;
}
#status { margin-left: auto; color: #8a93a5; font-size: 12px; }

#help {
  position: fixed; bottom: 203px; left: 50%; transform: translateX(-50%);
  z-index: 10; padding: 3px 8px 3px 10px; border-radius: 10px;
  font: 11px -apple-system, "Segoe UI", sans-serif; color: #6f7789;
  background: rgba(10,12,17,.45); pointer-events: none;
  display: flex; align-items: center; gap: 8px;
  white-space: nowrap;   /* else the 50% offset caps shrink-to-fit and wraps */
}
/* bottom:203 = directly above the ChatWnd (187 tall docked at bottom:8):
   at bottom:52 the pill sat inside the chat's band and overlapped the tab
   row (audit B11) */
#help.hidden { display: none; }
#help-close {
  pointer-events: auto; cursor: pointer; border: 0; background: none;
  color: #565e70; font: inherit; padding: 0 2px;
}
#help-close:hover { color: #c9a959; }

/* AUTHORED active-toggle marker: a buff with duration -1 is the active
   signal (gateway M10), but neither MagicSkillWnd.uc nor ShortcutWnd.uc
   draws an active-toggle state (both checked — silent), so a plain border
   stands in. */
.l2-toggle-active {
  box-shadow: inset 0 0 0 1px #7fd8e8, 0 0 5px rgba(127,216,232,.8);
  border-radius: 2px;
}
/* SkillWnd cells are pitch-sized wrappers: the ring belongs on the 34px
   slot art (first child), not on the wrapper. */
.l2-skill-cell.l2-toggle-active { box-shadow: none; }
.l2-skill-cell.l2-toggle-active > div:first-child {
  box-shadow: inset 0 0 0 1px #7fd8e8, 0 0 5px rgba(127,216,232,.8);
  border-radius: 2px;
}

#loading {
  position: fixed; inset: 0; z-index: 20;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 14px; background: #0b0d12; color: #8a93a5;
  font: 13px -apple-system, "Segoe UI", sans-serif;
  transition: opacity .4s;
}
#loading.hidden {
  opacity: 0; pointer-events: none; visibility: hidden;
  /* Finish the existing fade, then remove stale progress from visibility/AX. */
  transition: opacity .4s, visibility 0s .4s;
}
/* Account/character menus precede the world. Native HUD state is preserved,
   but gameplay windows cannot leak onto the login or character shell. */
body[data-game-phase="account"] > :is(.l2wnd, [id^="l2-"]:not(#l2-client-status), #hud, #chat, #help,
  #overlays, #damage-floats, #cast-bar, #charsheet-panel, #loot-toasts,
  #death-overlay, #settings-panel) {
  visibility: hidden !important;
  pointer-events: none !important;
}
.spin {
  width: 28px; height: 28px; border-radius: 50%;
  border: 3px solid #2a3140; border-top-color: #c9a959;
  animation: spin 1s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

#online-label { pointer-events: auto; cursor: pointer; }
#online-label input { accent-color: #c9a959; cursor: pointer; }

/* M3: combat UI ----------------------------------------------------------*/

.bar { position: relative; height: 8px; border-radius: 4px; background: #1a1e28; overflow: hidden; }
.bar > div { height: 100%; width: 100%; border-radius: 4px; transition: width .25s; }

/* #self-status retired in Phase C.1 — the player status window is now the
   retail StatusWnd, built by js/ui/statuswnd.js from Interface.xdat
   geometry and the client's own ps_*bar art. */

#overlays, #damage-floats { position: fixed; inset: 0; z-index: 11; pointer-events: none; }

.hp-bar {
  position: absolute; top: 0; left: 0; width: 50px; height: 5px;
  border-radius: 3px; background: rgba(10,12,17,.75); border: 1px solid #222;
  display: none;
}
.hp-bar-fill { height: 100%; background: #d04040; border-radius: 2px; }

.dmg-float {
  position: absolute; top: 0; left: 0;
  font: 700 17px -apple-system, "Segoe UI", sans-serif; color: #fff;
  text-shadow: 0 1px 3px #000, 0 0 6px rgba(0,0,0,.6);
  animation: dmg-rise 1.3s ease-out forwards;
}
.dmg-float.crit { color: #ff5040; font-size: 22px; }
.dmg-float.miss { color: #9aa2b2; font-style: italic; font-weight: 400; }
@keyframes dmg-rise {
  0% { margin-top: 0; opacity: 1; }
  70% { opacity: 1; }
  100% { margin-top: -46px; opacity: 0; }
}

#death-overlay {
  position: fixed; inset: 0; z-index: 18; display: none;
  align-items: center; justify-content: center;
  background: rgba(6,7,10,.55);
}
#death-overlay.visible { display: flex; }
.death-box {
  padding: 26px 44px; border-radius: 10px; text-align: center;
  background: rgba(12,14,20,.92); border: 1px solid #4a2a2a;
  font: 14px -apple-system, "Segoe UI", sans-serif; color: #e6eaf2;
}
.death-title { font-size: 20px; color: #e05656; margin-bottom: 16px; font-weight: 600; }
#respawn-btn {
  padding: 7px 26px; border-radius: 6px; border: 1px solid #c9a959;
  background: #241f14; color: #c9a959; font: inherit; cursor: pointer;
}
#respawn-btn:hover { background: #322a18; }

/* MenuWnd / SystemMenuWnd buttons (js/ui/menuwnd.js) */
.menu-btn { border-radius: 4px; }
.menu-btn:hover { background: rgba(201,169,89,.18); }
.menu-btn.disabled { opacity: .35; cursor: default; pointer-events: none; }
.sysmenu-btn { border-radius: 4px; }
.sysmenu-btn:hover { filter: brightness(1.25); }
.sysmenu-btn.disabled { opacity: .35; cursor: default; }

/* M4: casting bar + inventory + loot (bars moved to ShortcutWnd) */

#cast-bar {
  position: fixed; bottom: 114px; left: 50%; transform: translateX(-50%);
  z-index: 12; display: none; align-items: center; gap: 8px;
  font: 11px -apple-system, sans-serif; color: #cfd4de; pointer-events: none;
}
#cast-bar.visible { display: flex; }
#cast-bar .bar.cast { width: 160px; height: 7px; }
#cast-fill { background: linear-gradient(#7fb3ff, #3a6fc9); width: 0; }

#loot-toasts {
  position: fixed; right: 14px; bottom: 14px; z-index: 14;
  display: flex; flex-direction: column; gap: 4px; pointer-events: none;
}
.loot-toast {
  padding: 4px 10px; border-radius: 5px;
  background: rgba(20,26,16,.9); border: 1px solid #4a6b3a;
  color: #9ce8a9; font: 11px -apple-system, sans-serif;
  opacity: 0; transform: translateY(6px); transition: all .3s;
}
.loot-toast.show { opacity: 1; transform: translateY(0); }

/* sysMsg lines: distinct from player chat */
#chat-log .line.sysmsg { color: #e8d57f; font-style: italic; }

/* M5 chat channels + tabs (L2 colors).
   The tabs are painted with the xdat's own Chatting_Tab1/Tab2 sprites by
   js/chat.js, so nothing here may draw a box: browser <button> chrome was
   what made the strip look like a web form. */
#chat-tabs .chat-tab { background-repeat: no-repeat; }
#chat-log { line-height: 1.35; }
#chat-log .line { padding: 0 1px; }
#chat-log .line.ch-all { color: #e6eaf2; }
#chat-log .line.ch-shout { color: #ff9c40; }
#chat-log .line.ch-tell { color: #e07fe8; }
#chat-log .line.ch-party { color: #7fe87f; }
#chat-log .line.ch-clan { color: #8fb3e8; }
#chat-log .line.ch-trade { color: #e8a0b4; }
#chat-log .line.ch-hero { color: #7fd8e8; }

/* Settings panel.
   #charsheet-panel used to share these rules — a 300px rounded translucent
   card, hand-styled, which is what made Alt+T read as a web panel instead of
   a retail window. Alt+T is now DetailStatusWnd (js/ui/detailstatuswnd.js),
   built on L2Window from the mined 256x335 geometry, so the element keeps no
   styling of its own: js/charsheet.js empties it and keeps it only as the
   `visible` flag main.js still reads. #settings-panel is AUTHORED and stays
   authored — it has no retail counterpart in the xdat. */
#charsheet-panel { display: none !important; }
#settings-panel {
  position: fixed; right: 12px; top: 60px; z-index: 13; width: 300px;
  display: none; border-radius: 8px; padding: 8px 12px 12px;
  background: rgba(10,12,17,.88); border: 1px solid #3a4254;
  font: 12px -apple-system, sans-serif; color: #cfd4de;
}
#settings-panel.visible { display: block; }
/* .sheet-cols / .sheet-sub / .sheet-gear are gone with the authored panel:
   the retail window has no stat tables and no equipped-gear list. */
.settings-note { color: #8a93a5; margin: 0 0 8px; }
.settings-note b { color: #e6eaf2; }
.deviceid-row { display: flex; gap: 6px; align-items: center; }
.deviceid-row code {
  flex: 1; padding: 4px 6px; border-radius: 4px; background: #161a22;
  color: #9ce8a9; font-size: 10px; word-break: break-all;
}
#deviceid-copy {
  padding: 4px 12px; border-radius: 4px; border: 1px solid #c9a959;
  background: #241f14; color: #c9a959; cursor: pointer; font: inherit;
}
#settings-btn {
  pointer-events: auto; background: none; border: none; color: #8a93a5;
  font-size: 14px; cursor: pointer; padding: 0 2px;
}
#settings-btn:hover { color: #c9a959; }

/* One-time discoverability nudge for the dev bar (Backquote). */
#dev-hint {
  position: fixed; top: 10px; left: 50%; transform: translateX(-50%);
  z-index: 30; padding: 6px 14px; border-radius: 4px;
  background: rgba(10,12,17,.85); border: 1px solid #3a4254;
  color: #c9a959; font: 12px -apple-system, "Segoe UI", sans-serif;
  pointer-events: none; transition: opacity .8s;
}
#dev-hint.fade { opacity: 0; }

/* ShortcutWnd slots (js/ui/shortcutwnd.js)

   Exactly the same rule as the InventoryWnd cells below, and for the same
   reason. `L2UI_CH3.ShortcutWnd.shortcut_back` already paints all twelve
   slot wells — 32px interiors inside a 34px bevel, mined by
   tools/ui/mine_shortcutslots.py and now drawn at the offset that puts them
   under the xdat's slot rects. The slot element sits ON that well, so it
   must not fill or frame itself: the rounded rgba box with its own 1px
   border was hiding the retail art. It only became visible as a "bar" at
   all because the plate underneath was missing entirely (see
   shortcutwnd.js's header), so the boxes were the only thing on screen.
   Only the transient states draw, as an inset ring that stays inside the
   32px well rather than growing the element. */
.shortcut-slot {
  position: absolute; cursor: pointer; overflow: visible;
}
.shortcut-slot:hover { box-shadow: inset 0 0 0 1px #c9a959; }
.shortcut-slot img { width: 100%; height: 100%; display: block; }
.shortcut-btn.disabled { opacity: .35; cursor: default; pointer-events: none; }

/* InventoryWnd paperdoll + cells (js/ui/inventorywnd.js)

   These sit exactly ON the slot wells that Inventory_Back already paints
   (rects mined by tools/ui/mine_invslots.py), so they must NOT fill or
   frame themselves: an opaque panel here hides the retail art underneath,
   which is what the port used to do — every well was covered by a
   rgba(12,14,18,.5) box with its own 1px border. Only the transient states
   (hover, drop target) draw anything, as an inset ring so the 34px box
   keeps its mined size with no border-box arithmetic. */
.doll-slot, .inv-cell {
  box-sizing: border-box;
  background: none; border: 0; overflow: hidden; cursor: pointer;
}
.doll-slot { position: absolute; }
.doll-slot.henna { cursor: default; }
.doll-slot img, .inv-cell img { display: block; }
/* AUTHORED affordances: retail marks these with native ItemWindow states we
   have not mined, so hover/equipped are shown as a 1px inset ring rather
   than by guessing at a sprite. */
.inv-cell:hover, .doll-slot:hover { box-shadow: inset 0 0 0 1px #c9a959; }
.doll-slot.filled { box-shadow: inset 0 0 0 1px rgba(90,127,224,.55); }
.doll-slot.filled:hover { box-shadow: inset 0 0 0 1px #c9a959; }
.inv-cell .count {
  position: absolute; right: 1px; bottom: 0; line-height: 0;
}
.inv-cell .count canvas { display: block; }
.inv-bottom-btn:hover { filter: brightness(1.25); }
.inv-bottom-btn.over { filter: brightness(1.6); }

/* ChatWnd side buttons */
.chat-side-btn { pointer-events: auto; cursor: pointer; }
.chat-side-btn.disabled { opacity: .35; cursor: default; pointer-events: none; }

/* TargetStatusWnd bar fills (art is native BarCtrl, colors AUTHORED) */
.target-bar-hp > div { background: linear-gradient(#e05656, #a02828); }
.target-bar-mp > div { background: linear-gradient(#5a7fe0, #2c4aa0); }

/* NPC dialog (js/ui/npcdialog.js + js/ui/npchtml.js)
   ---------------------------------------------------------------------------
   There are no colours, sizes or fonts here. Every one of those comes from
   assets/gamedata/npchtml.json (mined out of NWindow.dll by
   tools/ui/mine_npchtml.py) and is applied in JS, because the client decides
   them: the text colour depends on whether the run is inside an <a>, the link
   colour depends on whether the anchor carries its own, and the glyphs come
   off the retail font sheet through Font.canvas — there is no TTF to fall
   back on. What is left here is layout plumbing only.

   The previous version of this block was the visual defect: a web font
   (-apple-system), a #c9a959 title, a #7fb3ff link and a CSS-drawn button.
   None of those three colours exists anywhere in the client. */
.npc-dialog-body { font-size: 0; }         /* canvases only; kill text leading */
.npc-dialog-content { word-break: break-word; }
/* One word off the retail glyph sheet, and the sheet's own space advance. */
.l2h-w { display: inline-block; vertical-align: top; }
.l2h-w canvas { display: block; }
/* An inter-word space is a real space CHARACTER (js/ui/npcdialog.js
   _pushSpace) so the browser breaks at it, hangs it at the end of a line and
   drops it at the start of the next — an inline-block spacer is an atomic box
   and wraps like a word, which was the one-space indent on every wrapped line.
   `.l2h-sp` survives only for the SURPLUS of a 2+ space run: the browser
   collapses those and no client rule for them has been decoded, so the width
   this port already drew is kept as an explicit box. */
.l2h-sp { display: inline-block; vertical-align: top; }
.npc-link { cursor: pointer; }
/* Retail links are drawn with an underline argument in the same call as the
   text (NWindow.dll 0x100856af pushes 1 where the plain-text branch pushes 0
   at 0x100856de). The rule is on the WORD SPAN rather than on the canvas
   inside it, so that the bridge below has a box to anchor to; the span's only
   content is that canvas, so it lands on the same pixel row it did before. */
span.l2h-w.npc-link, span.l2h-sp.npc-link { border-bottom: 1px solid currentColor; }
/* …and it does not stop at the spaces. The separator between two words of one
   anchor is a space CHARACTER with no box of its own, so the segment of rule
   that belongs to it is painted here, out of the flow: `data-sp` is set by
   _pushSpace on the word to the left of such a space, `left:100%` puts the
   segment in the gap, and `top:100%` is the bottom of the content box — the
   same row the border-bottom above occupies. It takes part in no layout, so
   the space stays a space. */
span.l2h-w.npc-link { position: relative; }
span.l2h-w.npc-link[data-sp]::after {
  content: ''; position: absolute; left: 100%; top: 100%;
  width: var(--l2h-space, 0px); height: 1px; background: currentColor;
}
/* The underline's colour is NOT the run's text colour. The link branch pushes
   the same 0xff6699ff twice — once as the per-run text colour (used only when
   the anchor carries none of its own) and once unconditionally as the draw's
   colour argument (NWindow.dll 0x100856a3 and 0x100856c9). So a
   <font color="98BFBF"> inside an <a> gets teal glyphs on a #6699FF rule,
   which is what retail shows. The literal below is that same mined value; it
   is the only colour in this file and it is the client's. */
.npc-link { color: #6699FF; }
.npc-btn { user-select: none; }
.npc-btn-label {
  position: absolute; left: 0; right: 0; top: 50%;
  transform: translateY(-50%); text-align: center; pointer-events: none;
}
.npc-btn-label canvas { display: inline-block; vertical-align: middle; }
.npc-btn[data-disabled] { cursor: default; }
.npc-table { font-size: 0; }
.npc-table td { vertical-align: top; }
.npc-html-img { background-repeat: no-repeat; }
/* The frame draws its own retail scrollbar (npcdialog.js _buildScrollbar), so
   the native one must not also appear inside the scroller. */
.npc-html-scroll { scrollbar-width: none; }
.npc-html-scroll::-webkit-scrollbar { width: 0; height: 0; }
.npc-sb-btn { background-repeat: no-repeat; }
.npc-sb-thumb { cursor: pointer; }
/* <edit> and <multiedit> now draw the client's OWN art, painted from JS
   (npcdialog.js _element / _paintMultiEdit) out of textures this repo stages
   from the client's packages:
     <edit>       L2UI.EtcWnd.Edit_Back          NCHtmlEdit's ctor, 0x10095346
     <multiedit>  l2ui_ch3.multiedit.M_inputbox01..09
                                                 NCHtmlMultiEdit vt slot 0x98
   This block used to read `background:#000; border:1px solid #555` under a
   comment saying the sprites were "NOT staged yet" and naming
   L2UI_CH3.Etc.inputbox1..3 as the art. Those textures exist but belong to a
   different control (ctor 0x10095670). Neither the background nor the border
   is set here any more — the art carries both.

   What is STILL authored, and cannot not be: the typeface. An <edit> is
   editable, so its glyphs cannot come off the retail bitmap sheet the rest of
   this window draws with; it needs a real caret and a real text run. The
   COLOUR is set from JS out of npchtml.json colors.text, so it is not here.

   <combobox> keeps a plain box: NCHtmlComboBox's ctor (0x10089815) installs no
   texture of its own — it delegates to its base class, and that class's art
   was not decoded. A documented gap, not a guess. */
.npc-edit {
  font: 11px monospace; background-color: transparent;
  border: 0; vertical-align: top;
}
select.npc-edit { background: #000; color: #DCDCDC; border: 1px solid #555; }
/* World diagnostics must not cover character selection or creation. */
body:has(#charcreate-overlay, #charsel-overlay) > [aria-label="Elbera Tools world picking"],
body:has(#charcreate-overlay, #charsel-overlay) > [aria-label="Elbera Tools camera inspection"] {
  display: none;
}
