Files
florian.berthold e11dc1eed5 per-floor extras + Ascension overrides + layer toggles
- atlasloot_extras: fit one transform per (kg_dungeon, floor_id) instead of mixing all kg bosses into one fit. Each AL extra is assigned to whichever floor's anchors it's nearest to (in AL coord space). Strat's Stonespine now correctly lands on floor 235 (Undead) instead of being hidden because the mixed-floor fit pushed it off.
- new data/ascension_overrides.json: per-name position/floor patches for places where Ascension diverges from retail. Seeded with Magistrate Barthilas → moved to the southern courtyard (3498, 3300 on Undead Side) per Ascension spawns.
- frontend renders extras only on their assigned floor; previously hard-coded to floor 0.
- new layer-toggle checkboxes (Enemies / Packs / Patrols / Icons) in the toolbar — flip patrols off if mob routes are noise for your route.
2026-04-25 23:26:24 +02:00

435 lines
9.1 KiB
CSS

/* Ascension M+ Route Planner — dark, gold-accented, no frameworks. */
:root {
--bg: #0e0d10;
--panel: #17151b;
--panel-2: #1f1c25;
--line: #2e2932;
--text: #d8d2c4;
--text-dim: #8d8576;
--accent: #d4a44a; /* WoW gold */
--accent-2: #f0c674;
--boss: #d63b3b; /* boss pin */
--waypoint: #6ea8ff; /* route point */
--pull: #6ad17b; /* pull marker */
--route: #f0c674;
--shadow: 0 1px 0 rgba(0,0,0,.4), 0 6px 18px rgba(0,0,0,.35);
}
* { box-sizing: border-box; }
html, body {
margin: 0;
height: 100%;
background: var(--bg);
color: var(--text);
font: 14px/1.45 -apple-system, "SF Pro Text", "Inter", "Segoe UI", system-ui, sans-serif;
overflow: hidden;
}
body {
display: grid;
grid-template-columns: 280px 1fr;
}
/* --- sidebar -------------------------------------------------------------- */
.sidebar {
background: var(--panel);
border-right: 1px solid var(--line);
display: flex;
flex-direction: column;
min-height: 0;
}
.brand {
padding: 18px 20px 12px;
border-bottom: 1px solid var(--line);
}
.brand h1 {
margin: 0;
font-size: 18px;
letter-spacing: .04em;
color: var(--accent);
font-weight: 600;
}
.brand .sub {
margin: 2px 0 0;
font-size: 11px;
color: var(--text-dim);
letter-spacing: .08em;
text-transform: uppercase;
}
.filters {
padding: 10px 14px;
display: flex;
flex-direction: column;
gap: 8px;
border-bottom: 1px solid var(--line);
}
.filters input,
.filters select {
background: var(--panel-2);
color: var(--text);
border: 1px solid var(--line);
border-radius: 4px;
padding: 6px 10px;
font: inherit;
outline: none;
}
.filters input:focus,
.filters select:focus { border-color: var(--accent); }
.dungeon-list {
list-style: none;
margin: 0;
padding: 6px 0;
overflow-y: auto;
flex: 1 1 auto;
scrollbar-width: thin;
scrollbar-color: var(--line) transparent;
}
.dungeon-list li {
padding: 7px 16px;
cursor: pointer;
display: flex;
justify-content: space-between;
gap: 8px;
border-left: 3px solid transparent;
font-size: 13px;
}
.dungeon-list li:hover { background: var(--panel-2); }
.dungeon-list li.active {
background: var(--panel-2);
border-left-color: var(--accent);
color: var(--accent-2);
}
.dungeon-list li .acronym {
color: var(--text-dim);
font-size: 11px;
font-variant-numeric: tabular-nums;
}
.dungeon-list .group-header {
padding: 12px 16px 4px;
color: var(--text-dim);
font-size: 10px;
text-transform: uppercase;
letter-spacing: .12em;
cursor: default;
}
.dungeon-list .group-header:hover { background: transparent; }
/* --- main viewer ---------------------------------------------------------- */
.viewer {
display: flex;
flex-direction: column;
min-width: 0;
min-height: 0;
}
.viewer-header {
padding: 14px 18px;
border-bottom: 1px solid var(--line);
display: flex;
align-items: center;
gap: 18px;
flex-wrap: wrap;
}
.title-block { flex: 0 1 auto; min-width: 200px; }
.title-block h2 {
margin: 0;
font-size: 18px;
color: var(--accent-2);
font-weight: 600;
}
.title-block .meta {
margin: 2px 0 0;
font-size: 12px;
color: var(--text-dim);
}
.floor-tabs {
display: flex;
gap: 4px;
flex-wrap: wrap;
}
.floor-tabs button {
background: var(--panel);
color: var(--text-dim);
border: 1px solid var(--line);
border-radius: 4px;
padding: 5px 10px;
font: inherit;
font-size: 12px;
cursor: pointer;
}
.floor-tabs button:hover { color: var(--text); }
.floor-tabs button.active {
border-color: var(--accent);
color: var(--accent-2);
background: var(--panel-2);
}
.toolbar {
margin-left: auto;
display: flex;
gap: 6px;
}
.toolbar button {
background: var(--panel);
border: 1px solid var(--line);
color: var(--text);
border-radius: 4px;
padding: 5px 12px;
font: inherit;
font-size: 12px;
cursor: pointer;
}
.toolbar button:hover { border-color: var(--accent); color: var(--accent-2); }
.toolbar .tool.active {
background: var(--accent);
color: #1a1208;
border-color: var(--accent);
font-weight: 600;
}
.toolbar-sep {
width: 1px;
height: 22px;
background: var(--line);
align-self: center;
margin: 0 4px;
}
.layer-toggle {
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 12px;
color: var(--text-dim);
cursor: pointer;
padding: 4px 6px;
user-select: none;
white-space: nowrap;
}
.layer-toggle:hover { color: var(--text); }
.layer-toggle input { margin: 0; cursor: pointer; }
/* --- canvas / overlay ----------------------------------------------------- */
.canvas-wrap {
flex: 1 1 auto;
display: grid;
grid-template-columns: 1fr 240px;
min-height: 0;
}
.canvas-host {
position: relative;
background: #050507;
background-image:
linear-gradient(rgba(255,255,255,.015) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,.015) 1px, transparent 1px);
background-size: 32px 32px;
overflow: hidden;
user-select: none;
cursor: grab;
}
.canvas-host.panning { cursor: grabbing; }
/* The pan/zoom transform is applied to this wrapper. Image and SVG share it. */
.canvas-stage {
position: absolute;
top: 50%;
left: 50%;
transform-origin: 0 0;
/* set by JS: transform: translate(...) scale(...) */
will-change: transform;
}
.canvas-stage img {
display: block;
pointer-events: none;
user-select: none;
-webkit-user-drag: none;
}
.canvas-stage svg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
pointer-events: auto;
cursor: crosshair;
}
.zoom-controls {
position: absolute;
right: 12px;
bottom: 12px;
display: flex;
flex-direction: column;
gap: 4px;
z-index: 5;
}
.zoom-controls button {
background: var(--panel-2);
border: 1px solid var(--line);
color: var(--text);
border-radius: 4px;
width: 32px;
height: 32px;
font-size: 16px;
cursor: pointer;
font-weight: 600;
}
.zoom-controls button:hover { border-color: var(--accent); color: var(--accent-2); }
/* boss pins (non-interactive markers from AtlasLoot data) */
.boss-pin circle {
fill: var(--boss);
stroke: #200;
stroke-width: 6;
}
/* user-placed waypoints */
.waypoint circle {
fill: var(--waypoint);
stroke: #002;
stroke-width: 6;
cursor: grab;
}
.waypoint.dragging circle { cursor: grabbing; }
.pull circle {
fill: var(--pull);
stroke: #020;
stroke-width: 6;
cursor: grab;
}
/* labels for all pin types */
.boss-pin text,
.waypoint text,
.pull text {
fill: #fff;
font-weight: 700;
font-family: system-ui, sans-serif;
text-anchor: middle;
paint-order: stroke;
stroke: #000;
stroke-width: 6;
pointer-events: none;
}
.route-line {
fill: none;
stroke: var(--route);
stroke-width: 8;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 16 12;
opacity: .85;
pointer-events: none;
}
/* --- info pane ----------------------------------------------------------- */
.info-pane {
border-left: 1px solid var(--line);
background: var(--panel);
padding: 14px 16px;
overflow-y: auto;
scrollbar-width: thin;
}
.info-pane h3 {
margin: 14px 0 6px;
font-size: 11px;
text-transform: uppercase;
letter-spacing: .12em;
color: var(--text-dim);
}
.info-pane h3:first-child { margin-top: 0; }
.boss-list, .waypoint-list {
list-style: none;
margin: 0;
padding: 0;
font-size: 13px;
}
.boss-list li,
.waypoint-list li {
display: flex;
align-items: center;
gap: 8px;
padding: 4px 6px;
border-radius: 3px;
}
.boss-list li:hover,
.waypoint-list li:hover { background: var(--panel-2); }
.boss-list .swatch,
.waypoint-list .swatch {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
flex-shrink: 0;
}
.boss-list .swatch.boss { background: var(--boss); }
.boss-list .swatch.rare { background: #bfd6f0; outline: 1px solid #3b6db0; }
.boss-list .tag {
margin-left: auto;
font-size: 10px;
text-transform: uppercase;
letter-spacing: .08em;
color: var(--text-dim);
}
.waypoint-list .swatch { background: var(--waypoint); }
.waypoint-list li button {
margin-left: auto;
background: transparent;
border: 0;
color: var(--text-dim);
cursor: pointer;
font-size: 14px;
}
.waypoint-list li button:hover { color: var(--boss); }
/* --- instant tooltip ------------------------------------------------------ */
.custom-tooltip {
position: fixed;
pointer-events: none;
z-index: 200;
background: rgba(20, 18, 24, 0.96);
color: var(--text);
border: 1px solid var(--accent);
border-radius: 4px;
padding: 6px 10px;
font-size: 13px;
line-height: 1.35;
max-width: 360px;
box-shadow: 0 6px 18px rgba(0, 0, 0, .45);
opacity: 0;
transition: opacity .08s ease;
white-space: pre-wrap;
}
.custom-tooltip.show { opacity: 1; }
/* --- toast ---------------------------------------------------------------- */
.toast {
position: fixed;
bottom: 18px;
left: 50%;
transform: translateX(-50%);
background: var(--panel-2);
color: var(--accent-2);
border: 1px solid var(--accent);
padding: 8px 18px;
border-radius: 4px;
font-size: 13px;
box-shadow: var(--shadow);
pointer-events: none;
opacity: 0;
transition: opacity .2s ease;
z-index: 99;
}
.toast.show { opacity: 1; }