UI
This commit is contained in:
parent
6cef9a3abe
commit
440dba2f09
5 changed files with 2626 additions and 347 deletions
370
index.html
370
index.html
|
|
@ -8,115 +8,279 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="app">
|
<div class="app">
|
||||||
<main class="layout">
|
<main class="page-shell">
|
||||||
<section class="main-panel">
|
<section class="top-stage">
|
||||||
<header class="header">
|
<section class="map-section" aria-label="Map area">
|
||||||
<div>
|
<div class="canvas-shell" id="canvasShell">
|
||||||
<h1>Prefecture Map Generator v17</h1>
|
<div class="canvas-stage">
|
||||||
<p>Terrain, municipalities, transport, viewport panning, patch terrain generation, and hover inspection in one generated map.</p>
|
<canvas id="mapCanvas" class="map-canvas"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
<svg id="mapSelectionSvg" class="map-selection-svg" aria-hidden="true"></svg>
|
||||||
|
<div id="mapSelection" class="map-selection" aria-hidden="true"></div>
|
||||||
|
|
||||||
<div class="canvas-shell">
|
<div id="toolHint" class="map-hint hidden">
|
||||||
<canvas id="mapCanvas" class="map-canvas"></canvas>
|
Patch Area mode: right-drag to select a regeneration area.
|
||||||
<svg id="mapSelectionSvg" class="map-selection-svg" aria-hidden="true"></svg>
|
</div>
|
||||||
<div id="mapSelection" class="map-selection" aria-hidden="true"></div>
|
|
||||||
<div id="generationProgress" class="generation-progress hidden" role="status" aria-live="polite">
|
<div class="zoom-control" aria-label="Map zoom controls">
|
||||||
<div class="progress-title">Generating map...</div>
|
<button id="zoomIn" type="button" aria-label="Zoom in">+</button>
|
||||||
<div id="generationProgressStage" class="progress-stage">Preparing</div>
|
<button id="zoomOut" type="button" aria-label="Zoom out">−</button>
|
||||||
<div id="generationProgressTimings" class="progress-timings"></div>
|
<button id="zoomReset" type="button">100%</button>
|
||||||
|
<button id="centerMap" type="button">Center</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<aside class="map-dock" aria-label="Layer and legend controls">
|
||||||
|
<details class="dock-card" open>
|
||||||
|
<summary>
|
||||||
|
<span>Layer</span>
|
||||||
|
<span class="summary-button" aria-hidden="true">Toggle</span>
|
||||||
|
</summary>
|
||||||
|
<div class="dock-body">
|
||||||
|
<div class="field-label">Display mode</div>
|
||||||
|
<div id="modeGrid" class="mode-grid"></div>
|
||||||
|
|
||||||
|
<div class="divider"></div>
|
||||||
|
|
||||||
|
<label class="checkbox-row">
|
||||||
|
<input id="showFeatures" type="checkbox" checked />
|
||||||
|
<span>Map features</span>
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-row">
|
||||||
|
<input id="showLabels" type="checkbox" checked />
|
||||||
|
<span>Labels</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details class="dock-card">
|
||||||
|
<summary>
|
||||||
|
<span>Legend</span>
|
||||||
|
<span class="summary-button" aria-hidden="true">Toggle</span>
|
||||||
|
</summary>
|
||||||
|
<div id="mainLegendGrid" class="legend-grid dock-body"></div>
|
||||||
|
</details>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<div id="generationProgress" class="generation-progress hidden" role="status" aria-live="polite">
|
||||||
|
<div class="progress-title">Generating map...</div>
|
||||||
|
<div id="generationProgressStage" class="progress-stage">Preparing</div>
|
||||||
|
<div id="generationProgressTimings" class="progress-timings"></div>
|
||||||
|
</div>
|
||||||
|
<div id="mapTooltip" class="map-tooltip" role="status" aria-live="polite"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="mapTooltip" class="map-tooltip" role="status" aria-live="polite"></div>
|
</section>
|
||||||
</div>
|
|
||||||
|
<aside class="generation-section" aria-label="Generation settings">
|
||||||
|
<section class="settings-card settings-header-card">
|
||||||
|
<div class="card-heading tight">
|
||||||
|
<div>
|
||||||
|
<div class="eyebrow">Controls</div>
|
||||||
|
<h2>Generation Settings</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="settings-card">
|
||||||
|
<label class="field">
|
||||||
|
<span class="field-label">Seed</span>
|
||||||
|
<input id="seed" class="input" value="114514" />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="field">
|
||||||
|
<span class="field-label">Terrain type</span>
|
||||||
|
<select id="generationType" class="input">
|
||||||
|
<option value="auto">Auto</option>
|
||||||
|
<option value="tohoku_spine">Tohoku spine</option>
|
||||||
|
<option value="chubu_mountain">Chubu mountain</option>
|
||||||
|
<option value="setouchi_inland_sea">Setouchi inland sea</option>
|
||||||
|
<option value="oceanic_archipelago">Oceanic archipelago</option>
|
||||||
|
<option value="kanto_alluvial">Kanto alluvial plain</option>
|
||||||
|
<option value="mixed_archipelago">Mixed archipelago</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="button-stack two-col">
|
||||||
|
<button id="generateMap" type="button" class="primary-button">Generate</button>
|
||||||
|
<button id="randomSeed" type="button" class="secondary-button">Random</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="settings-card">
|
||||||
|
<div class="card-heading">
|
||||||
|
<div>
|
||||||
|
<div class="eyebrow">Interaction</div>
|
||||||
|
<h3>Mode</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="segmented-group" aria-label="Interaction mode">
|
||||||
|
<button id="toolPan" type="button" class="segmented-button active" data-tool="pan">Pan</button>
|
||||||
|
<button id="toolPatch" type="button" class="segmented-button" data-tool="patch">Patch Area</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="settings-card patch-card">
|
||||||
|
<div class="card-heading">
|
||||||
|
<div>
|
||||||
|
<div class="eyebrow">Right-drag selection</div>
|
||||||
|
<h3>Patch Generation</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="field">
|
||||||
|
<span class="field-label">Patch terrain</span>
|
||||||
|
<select id="patchTerrainType" class="input">
|
||||||
|
<option value="auto">Auto</option>
|
||||||
|
<option value="tohoku_spine">Tohoku spine</option>
|
||||||
|
<option value="chubu_mountain">Chubu mountain</option>
|
||||||
|
<option value="setouchi_inland_sea">Setouchi inland sea</option>
|
||||||
|
<option value="oceanic_archipelago">Oceanic archipelago</option>
|
||||||
|
<option value="kanto_alluvial">Kanto alluvial plain</option>
|
||||||
|
<option value="mixed_archipelago">Mixed archipelago</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="field">
|
||||||
|
<span class="field-label">Variant</span>
|
||||||
|
<input id="patchVariant" class="input" type="number" min="0" step="1" value="0" />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="button-stack two-col">
|
||||||
|
<button id="generatePatch" type="button" class="primary-button" disabled>Preview Area</button>
|
||||||
|
<button id="alternativePatch" type="button" class="secondary-button" disabled>Alternative</button>
|
||||||
|
</div>
|
||||||
|
<div class="button-stack two-col">
|
||||||
|
<button id="applyPatch" type="button" class="secondary-button" disabled>Apply Preview</button>
|
||||||
|
<button id="discardPatch" type="button" class="ghost-button" disabled>Discard</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p id="patchStatus" class="patch-status">Switch to Patch Area, then right-drag to draw a freeform area.</p>
|
||||||
|
</section>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<aside class="sidebar">
|
<section class="summary-section" aria-label="Summary">
|
||||||
<section class="card">
|
<div class="section-title-row compact">
|
||||||
<label class="label" for="seed">Seed</label>
|
<div>
|
||||||
<input id="seed" class="input" value="114514" />
|
<div class="eyebrow">Current map</div>
|
||||||
<label class="label inline-label" for="generationType">Generation Type</label>
|
<h2>Summary</h2>
|
||||||
<select id="generationType" class="input">
|
|
||||||
<option value="auto">Auto</option>
|
|
||||||
<option value="tohoku_spine">Tohoku spine</option>
|
|
||||||
<option value="chubu_mountain">Chubu mountain</option>
|
|
||||||
<option value="setouchi_inland_sea">Setouchi inland sea</option>
|
|
||||||
<option value="oceanic_archipelago">Ocean</option>
|
|
||||||
<option value="kanto_alluvial">Kanto alluvial plain</option>
|
|
||||||
<option value="mixed_archipelago">Mixed archipelago</option>
|
|
||||||
</select>
|
|
||||||
<button id="randomSeed" type="button" class="primary-button">Generate Random Seed</button>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
<section class="card">
|
|
||||||
<div class="card-title">Patch Generation</div>
|
|
||||||
<label class="label" for="patchTerrainType">Patch Terrain Type</label>
|
|
||||||
<select id="patchTerrainType" class="input">
|
|
||||||
<option value="auto">Auto</option>
|
|
||||||
<option value="tohoku_spine">Tohoku spine</option>
|
|
||||||
<option value="chubu_mountain">Chubu mountain</option>
|
|
||||||
<option value="setouchi_inland_sea">Setouchi inland sea</option>
|
|
||||||
<option value="oceanic_archipelago">Ocean</option>
|
|
||||||
<option value="kanto_alluvial">Kanto alluvial plain</option>
|
|
||||||
<option value="mixed_archipelago">Mixed archipelago</option>
|
|
||||||
</select>
|
|
||||||
<div class="patch-variant-row">
|
|
||||||
<label class="label patch-variant-label" for="patchVariant">Patch Variant</label>
|
|
||||||
<input id="patchVariant" class="input patch-variant-input" type="number" min="0" step="1" value="0" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="patch-button-row">
|
</div>
|
||||||
<button id="generatePatch" type="button" class="primary-button" disabled>Generate Selected Area</button>
|
<div id="stats" class="stats summary-stats"></div>
|
||||||
<button id="alternativePatch" type="button" class="secondary-button" disabled>Alternative</button>
|
</section>
|
||||||
|
|
||||||
|
<section class="advanced-section" aria-label="Advanced and debug data">
|
||||||
|
<details class="advanced-details">
|
||||||
|
<summary>
|
||||||
|
<span>Advanced / Debug Data</span>
|
||||||
|
<span class="advanced-summary-actions">
|
||||||
|
<button id="copyImportantData" type="button" class="copy-debug-button">Copy Important Data</button>
|
||||||
|
<span id="copyDebugStatus" class="copy-debug-status" aria-live="polite"></span>
|
||||||
|
<span class="advanced-toggle" aria-hidden="true">Open</span>
|
||||||
|
</span>
|
||||||
|
</summary>
|
||||||
|
<div class="advanced-body">
|
||||||
|
<section class="debug-panel wide">
|
||||||
|
<header class="debug-panel-header">
|
||||||
|
<div>
|
||||||
|
<div class="eyebrow">Last 10</div>
|
||||||
|
<h3>Full Generation Performance</h3>
|
||||||
|
</div>
|
||||||
|
<p>Average, max, P95, breakdown, terrain type, area, and seconds per 1,000 cells.</p>
|
||||||
|
</header>
|
||||||
|
<div id="advancedGenerationStats" class="metric-grid"></div>
|
||||||
|
<div id="advancedGenerationHistory" class="perf-history"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="debug-panel wide">
|
||||||
|
<header class="debug-panel-header">
|
||||||
|
<div>
|
||||||
|
<div class="eyebrow">Last 10</div>
|
||||||
|
<h3>Patch / Additional Generation Performance</h3>
|
||||||
|
</div>
|
||||||
|
<p>Preview and alternative patch generation, including selected area and normalized cost.</p>
|
||||||
|
</header>
|
||||||
|
<div id="advancedPatchStats" class="metric-grid"></div>
|
||||||
|
<div id="advancedPatchHistory" class="perf-history"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="debug-panel wide">
|
||||||
|
<header class="debug-panel-header">
|
||||||
|
<div>
|
||||||
|
<div class="eyebrow">Last 10</div>
|
||||||
|
<h3>Viewport Interaction Latency</h3>
|
||||||
|
</div>
|
||||||
|
<p>Pan and zoom are aggregated separately for fast redraw and full redraw phases.</p>
|
||||||
|
</header>
|
||||||
|
<div class="aggregate-header" aria-hidden="true">
|
||||||
|
<span>Group</span><small>Count</small><strong>Avg</strong><strong>Max</strong><strong>P95</strong>
|
||||||
|
</div>
|
||||||
|
<div id="advancedInteractionStats" class="perf-history compact"></div>
|
||||||
|
<div id="advancedInteractionHistory" class="perf-history compact"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="debug-panel wide">
|
||||||
|
<header class="debug-panel-header">
|
||||||
|
<div>
|
||||||
|
<div class="eyebrow">Current render</div>
|
||||||
|
<h3>Viewport / Feature Diagnostics</h3>
|
||||||
|
</div>
|
||||||
|
<p>Drawn cell count, viewport size, canvas size, and feature counts used to separate generation cost from rendering cost.</p>
|
||||||
|
</header>
|
||||||
|
<div id="advancedViewportDiagnostics" class="diagnostic-grid"></div>
|
||||||
|
<div id="advancedFeatureCounts" class="diagnostic-table"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="debug-panel">
|
||||||
|
<header class="debug-panel-header">
|
||||||
|
<div>
|
||||||
|
<div class="eyebrow">Patch area</div>
|
||||||
|
<h3>Selection / Write Ratio</h3>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div id="advancedPatchDiagnostics" class="diagnostic-table"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="debug-panel">
|
||||||
|
<header class="debug-panel-header">
|
||||||
|
<div>
|
||||||
|
<div class="eyebrow">Runtime</div>
|
||||||
|
<h3>Worker / World Diagnostics</h3>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div id="advancedWorkerDiagnostics" class="diagnostic-table"></div>
|
||||||
|
<div id="advancedWorldDiagnostics" class="diagnostic-table stacked"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="debug-panel wide">
|
||||||
|
<header class="debug-panel-header">
|
||||||
|
<div>
|
||||||
|
<div class="eyebrow">Last 10</div>
|
||||||
|
<h3>Warnings / Errors</h3>
|
||||||
|
</div>
|
||||||
|
<p>Recent invalid selections, worker fallbacks, failed generations, and other UI-visible diagnostics.</p>
|
||||||
|
</header>
|
||||||
|
<div id="advancedWarningHistory" class="diagnostic-log"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="debug-panel">
|
||||||
|
<header class="debug-panel-header">
|
||||||
|
<div>
|
||||||
|
<div class="eyebrow">Notes</div>
|
||||||
|
<h3>Debug Policy</h3>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="debug-note flat">
|
||||||
|
<strong>Patch workflow</strong>
|
||||||
|
<p>Preview Area inspects a candidate patch. Apply Preview commits it; Discard resets it.</p>
|
||||||
|
</div>
|
||||||
|
<div class="debug-note flat">
|
||||||
|
<strong>Readable default UI</strong>
|
||||||
|
<p>Verbose debug layers and unused legend entries stay outside the default controls.</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<p id="patchStatus" class="patch-status">Right-drag to lasso a freeform patch area.</p>
|
</details>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="card">
|
|
||||||
<div class="card-title">Display Layers</div>
|
|
||||||
<div id="modeGrid" class="mode-grid"></div>
|
|
||||||
|
|
||||||
<label class="checkbox-row">
|
|
||||||
<input id="showFeatures" type="checkbox" checked />
|
|
||||||
Show features
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label class="checkbox-row">
|
|
||||||
<input id="showLabels" type="checkbox" checked />
|
|
||||||
Show labels
|
|
||||||
</label>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="card">
|
|
||||||
<div class="card-title">Generated Features</div>
|
|
||||||
<div id="stats" class="stats"></div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="card legend">
|
|
||||||
<div class="card-title">Legend</div>
|
|
||||||
<div class="legend-grid" aria-label="Map legend">
|
|
||||||
<div class="legend-row"><span class="legend-swatch border-swatch"></span><span>Prefecture region / municipal border</span></div>
|
|
||||||
<div class="legend-row"><span class="legend-line river-major"></span><span>Main river / tributary</span></div>
|
|
||||||
<div class="legend-row"><span class="legend-line rail-line"></span><span>Railway / solid ring railway</span></div>
|
|
||||||
<div class="legend-row"><span class="legend-line road-line"></span><span>National road / trunk ring road</span></div>
|
|
||||||
<div class="legend-row"><span class="legend-line express-line"></span><span>Expressway / environmental ring segment</span></div>
|
|
||||||
<div class="legend-row"><span class="legend-line old-road-line"></span><span>Premodern / minor road</span></div>
|
|
||||||
<div class="legend-row"><span class="legend-icon city-icon"></span><span>Prefectural capital / city</span></div>
|
|
||||||
<div class="legend-row"><span class="legend-icon port-icon"></span><span>Major / regional / fishing / lake port</span></div>
|
|
||||||
<div class="legend-row"><span class="legend-icon castle-icon"></span><span>Castle / ruins</span></div>
|
|
||||||
<div class="legend-row"><span class="legend-swatch cbd-swatch"></span><span>CBD / central city cells</span></div>
|
|
||||||
<div class="legend-row"><span class="legend-icon satellite-icon"></span><span>Satellite city</span></div>
|
|
||||||
<div class="legend-row"><span class="legend-icon station-icon"></span><span>Station</span></div>
|
|
||||||
<div class="legend-row"><span class="legend-icon industry-icon"></span><span>Industry / logistics</span></div>
|
|
||||||
<div class="legend-row"><span class="legend-icon newtown-icon"></span><span>New town</span></div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="card legend">
|
|
||||||
<div class="card-title">Notes</div>
|
|
||||||
<p>Open <code>index.html</code> with Live Server. Left-drag pans the viewport; right-drag draws a freeform regeneration area; use Patch Generation to write terrain into that area.</p>
|
|
||||||
<p>Add preferred reusable place names in <code>CUSTOM_NAME_LIST</code> inside <code>names.js</code>.</p>
|
|
||||||
</section>
|
|
||||||
</aside>
|
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
790
mapPatch.js
790
mapPatch.js
File diff suppressed because it is too large
Load diff
68
renderer.js
68
renderer.js
|
|
@ -645,6 +645,71 @@ function getDisplayBorderSegments(map, fieldName, precomputedSegments, mode) {
|
||||||
return Array.isArray(precomputedSegments) ? precomputedSegments : [];
|
return Array.isArray(precomputedSegments) ? precomputedSegments : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function borderSegmentMidpoint(seg) {
|
||||||
|
return { x: ((seg?.[0]?.[0] || 0) + (seg?.[1]?.[0] || 0)) * 0.5, y: ((seg?.[0]?.[1] || 0) + (seg?.[1]?.[1] || 0)) * 0.5 };
|
||||||
|
}
|
||||||
|
function borderSegmentOrientation(seg) {
|
||||||
|
return Math.atan2((seg?.[1]?.[1] || 0) - (seg?.[0]?.[1] || 0), (seg?.[1]?.[0] || 0) - (seg?.[0]?.[0] || 0));
|
||||||
|
}
|
||||||
|
function borderAngleDistance(a, b) {
|
||||||
|
let d = Math.abs(a - b) % Math.PI;
|
||||||
|
if (d > Math.PI / 2) d = Math.PI - d;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
function borderPointSegmentDistance(px, py, ax, ay, bx, by) {
|
||||||
|
const dx = bx - ax, dy = by - ay;
|
||||||
|
const len2 = dx * dx + dy * dy;
|
||||||
|
if (len2 <= 1e-9) return Math.hypot(px - ax, py - ay);
|
||||||
|
const t = Math.max(0, Math.min(1, ((px - ax) * dx + (py - ay) * dy) / len2));
|
||||||
|
return Math.hypot(px - (ax + dx * t), py - (ay + dy * t));
|
||||||
|
}
|
||||||
|
function borderSegmentsNear(a, b, tolerance = 1.35) {
|
||||||
|
if (!a || !b) return false;
|
||||||
|
if (borderAngleDistance(borderSegmentOrientation(a), borderSegmentOrientation(b)) > 0.72) return false;
|
||||||
|
const am = borderSegmentMidpoint(a);
|
||||||
|
const bm = borderSegmentMidpoint(b);
|
||||||
|
if (Math.hypot(am.x - bm.x, am.y - bm.y) <= tolerance) return true;
|
||||||
|
const ax = a?.[0]?.[0] || 0, ay = a?.[0]?.[1] || 0;
|
||||||
|
const bx = a?.[1]?.[0] || 0, by = a?.[1]?.[1] || 0;
|
||||||
|
const cx = b?.[0]?.[0] || 0, cy = b?.[0]?.[1] || 0;
|
||||||
|
const dx = b?.[1]?.[0] || 0, dy = b?.[1]?.[1] || 0;
|
||||||
|
return Math.min(
|
||||||
|
borderPointSegmentDistance(ax, ay, cx, cy, dx, dy),
|
||||||
|
borderPointSegmentDistance(bx, by, cx, cy, dx, dy),
|
||||||
|
borderPointSegmentDistance(cx, cy, ax, ay, bx, by),
|
||||||
|
borderPointSegmentDistance(dx, dy, ax, ay, bx, by)
|
||||||
|
) <= tolerance;
|
||||||
|
}
|
||||||
|
function suppressMunicipalBordersNearPrefectures(adminSegments, prefectureSegments) {
|
||||||
|
if (!adminSegments?.length || !prefectureSegments?.length) return adminSegments || [];
|
||||||
|
const tolerance = 1.35;
|
||||||
|
const cellSize = tolerance;
|
||||||
|
const grid = new Map();
|
||||||
|
const cell = (v) => Math.floor(v / cellSize);
|
||||||
|
for (const seg of prefectureSegments) {
|
||||||
|
const m = borderSegmentMidpoint(seg);
|
||||||
|
const key = `${cell(m.x)},${cell(m.y)}`;
|
||||||
|
const bucket = grid.get(key) || [];
|
||||||
|
bucket.push(seg);
|
||||||
|
grid.set(key, bucket);
|
||||||
|
}
|
||||||
|
const out = [];
|
||||||
|
for (const seg of adminSegments) {
|
||||||
|
const m = borderSegmentMidpoint(seg);
|
||||||
|
const gx = cell(m.x), gy = cell(m.y);
|
||||||
|
let near = false;
|
||||||
|
for (let yy = gy - 2; yy <= gy + 2 && !near; yy++) {
|
||||||
|
for (let xx = gx - 2; xx <= gx + 2 && !near; xx++) {
|
||||||
|
for (const pref of grid.get(`${xx},${yy}`) || []) {
|
||||||
|
if (borderSegmentsNear(seg, pref, tolerance)) { near = true; break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!near) out.push(seg);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
function drawRiverPath(ctx, map, path, color, widthFn, alpha = 1) {
|
function drawRiverPath(ctx, map, path, color, widthFn, alpha = 1) {
|
||||||
if (!path || path.length < 2) return;
|
if (!path || path.length < 2) return;
|
||||||
ctx.save();
|
ctx.save();
|
||||||
|
|
@ -1123,8 +1188,9 @@ export function drawMap(canvas, map, options) {
|
||||||
const showPrefectureRegions = ["all", "admin", "borders-debug"].includes(mode);
|
const showPrefectureRegions = ["all", "admin", "borders-debug"].includes(mode);
|
||||||
if (showPrefectureRegions) drawPrefectureRegionFill(ctx, map, mode);
|
if (showPrefectureRegions) drawPrefectureRegionFill(ctx, map, mode);
|
||||||
|
|
||||||
const adminBorderSegments = getDisplayBorderSegments(map, "adminId", map.adminBorders, mode);
|
|
||||||
const prefectureBorderSegments = getDisplayBorderSegments(map, "prefectureRegionId", map.regionalPrefectureBorders, mode);
|
const prefectureBorderSegments = getDisplayBorderSegments(map, "prefectureRegionId", map.regionalPrefectureBorders, mode);
|
||||||
|
const rawAdminBorderSegments = getDisplayBorderSegments(map, "adminId", map.adminBorders, mode);
|
||||||
|
const adminBorderSegments = mode === "borders-debug" ? rawAdminBorderSegments : suppressMunicipalBordersNearPrefectures(rawAdminBorderSegments, prefectureBorderSegments);
|
||||||
if (showAdmin && adminBorderSegments?.length) {
|
if (showAdmin && adminBorderSegments?.length) {
|
||||||
drawVectorSegments(ctx, adminBorderSegments, "rgba(255, 255, 255, 0.8)", 2.8, false, { iterations: 1, tolerance: 0.05, offsetX: -0.5, offsetY: -0.5 });
|
drawVectorSegments(ctx, adminBorderSegments, "rgba(255, 255, 255, 0.8)", 2.8, false, { iterations: 1, tolerance: 0.05, offsetX: -0.5, offsetY: -0.5 });
|
||||||
drawVectorSegments(ctx, adminBorderSegments, "rgba(150, 140, 150, 0.9)", 1.2, true, { iterations: 1, tolerance: 0.05, offsetX: -0.5, offsetY: -0.5 });
|
drawVectorSegments(ctx, adminBorderSegments, "rgba(150, 140, 150, 0.9)", 1.2, true, { iterations: 1, tolerance: 0.05, offsetX: -0.5, offsetY: -0.5 });
|
||||||
|
|
|
||||||
616
styles.css
616
styles.css
|
|
@ -1,94 +1,532 @@
|
||||||
*{box-sizing:border-box}
|
*{box-sizing:border-box}
|
||||||
body{margin:0;background:#f0f2f5;color:#2c2c2c;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
|
:root{
|
||||||
|
--bg:#090b10;
|
||||||
|
--bg-2:#0d1118;
|
||||||
|
--surface:#11161f;
|
||||||
|
--surface-2:#151d28;
|
||||||
|
--surface-3:#1a2431;
|
||||||
|
--surface-soft:rgba(20,28,39,.88);
|
||||||
|
--panel:rgba(20,28,39,.9);
|
||||||
|
--line:rgba(173,190,211,.12);
|
||||||
|
--line-strong:rgba(173,190,211,.24);
|
||||||
|
--text:#e8eef8;
|
||||||
|
--muted:#97a6bb;
|
||||||
|
--muted-2:#6f8199;
|
||||||
|
--accent:#4f8cff;
|
||||||
|
--accent-strong:#3e75d9;
|
||||||
|
--accent-soft:rgba(79,140,255,.14);
|
||||||
|
--danger:#ff6861;
|
||||||
|
--shadow:0 24px 60px rgba(0,0,0,.36);
|
||||||
|
--shadow-soft:0 10px 26px rgba(0,0,0,.24);
|
||||||
|
}
|
||||||
|
html,body{height:100%}
|
||||||
|
body{
|
||||||
|
margin:0;
|
||||||
|
background:radial-gradient(circle at top left,#131a25 0,#0d1118 38%,#090b10 100%);
|
||||||
|
color:var(--text);
|
||||||
|
font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
||||||
|
}
|
||||||
button,input,select{font:inherit}
|
button,input,select{font:inherit}
|
||||||
code{background:#e8e8e8;border-radius:4px;padding:1px 4px}
|
button{user-select:none}
|
||||||
.app{min-height:100vh;padding:16px}
|
code{background:#121820;border-radius:4px;padding:1px 4px}
|
||||||
.layout{display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:16px;max-width:1360px;margin:0 auto}
|
.app{min-height:100dvh}
|
||||||
.header{margin-bottom:12px}
|
.page-shell{width:100%}
|
||||||
.header h1{margin:0 0 6px;font-size:24px;letter-spacing:-0.02em;color:#1a1a1a;font-weight:700}
|
.top-stage{
|
||||||
.header p{margin:0;color:#5a5a5a;line-height:1.6;font-size:14px}
|
height:100dvh;
|
||||||
.canvas-shell,.card{background:#ffffff;border:1px solid rgba(0,0,0,0.08);border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,0.04)}
|
display:grid;
|
||||||
.canvas-shell{padding:12px;overflow:auto;position:relative;cursor:grab;user-select:none;touch-action:none}
|
grid-template-columns:minmax(0,1fr) 320px;
|
||||||
.map-canvas{display:block;border-radius:8px;background:#f8f9fa}
|
gap:12px;
|
||||||
.sidebar{display:flex;flex-direction:column;gap:12px}
|
padding:12px 12px 0;
|
||||||
.card{padding:14px}
|
}
|
||||||
.label,.card-title{display:block;margin-bottom:12px;color:#1a1a1a;font-size:14px;font-weight:600}
|
.map-section,.generation-section{min-height:0}
|
||||||
.inline-label{margin-top:12px}
|
.map-section{min-width:0}
|
||||||
.input{width:100%;border:1px solid rgba(0,0,0,0.15);background:#fff;color:#2c2c2c;border-radius:8px;padding:10px 12px;outline:none;transition:border-color 0.2s}
|
.canvas-shell{
|
||||||
.input:focus{border-color:#1a73e8;box-shadow:0 0 0 3px rgba(26,115,232,0.15)}
|
position:relative;
|
||||||
.primary-button,.mode-button{border:0;border-radius:8px;padding:10px 12px;cursor:pointer;font-weight:600;transition:background 0.2s}
|
height:100%;
|
||||||
.primary-button{margin-top:12px;width:100%;background:#1a73e8;color:#fff}
|
border:1px solid var(--line);
|
||||||
.primary-button:hover{background:#1557b0}
|
border-radius:18px;
|
||||||
.mode-grid{display:grid;grid-template-columns:1fr 1fr;gap:6px}
|
overflow:hidden;
|
||||||
.mode-button{background:#f1f3f4;color:#3c4043;border:1px solid transparent}
|
background:linear-gradient(180deg,#0d131c 0,#0b1017 100%);
|
||||||
.mode-button:hover{background:#e8eaed}
|
box-shadow:var(--shadow);
|
||||||
.mode-button.active{background:#e8f0fe;color:#1a73e8;border:1px solid #1a73e8}
|
cursor:grab;
|
||||||
.checkbox-row{display:flex;gap:8px;align-items:center;margin-top:12px;color:#3c4043;font-size:14px;cursor:pointer}
|
user-select:none;
|
||||||
.stats{display:flex;flex-direction:column;gap:8px}
|
touch-action:none;
|
||||||
.stat-row{display:flex;justify-content:space-between;gap:12px;color:#5f6368;font-size:13px;align-items:baseline}
|
}
|
||||||
.stat-row strong{color:#202124;font-family:ui-monospace,monospace}
|
.canvas-shell::before{
|
||||||
.legend{color:#5f6368;font-size:13px;line-height:1.6}
|
content:"";
|
||||||
.legend p{margin:8px 0 0}
|
position:absolute;
|
||||||
|
inset:0;
|
||||||
@media (max-width:1100px){.layout{grid-template-columns:1fr}}
|
background:linear-gradient(180deg,rgba(255,255,255,.02),rgba(255,255,255,0));
|
||||||
|
pointer-events:none;
|
||||||
.legend-grid{display:flex;flex-direction:column;gap:8px;margin-top:12px}
|
}
|
||||||
.legend-row{display:grid;grid-template-columns:32px 1fr;gap:8px;align-items:center;min-height:22px}
|
|
||||||
|
|
||||||
/* Layered GIS style CSS equivalents */
|
|
||||||
.legend-line{display:inline-block;width:28px;height:4px;border-radius:2px;}
|
|
||||||
.express-line{background:#6eb982; border:1px solid #508c64;}
|
|
||||||
.road-line{background:#f5e182; border:1px solid #beaf8c;}
|
|
||||||
|
|
||||||
/* Fishbone Railway Style */
|
|
||||||
.rail-line{background:#6e6e6e; height:1.5px; position:relative; border:none; margin-top:2px; border-radius:0}
|
|
||||||
.rail-line::after{content:"";position:absolute;top:-2.5px;left:0;right:0;height:7px;background:repeating-linear-gradient(90deg, transparent, transparent 5px, #6e6e6e 5px, #6e6e6e 6px);}
|
|
||||||
|
|
||||||
.river-major{background:#a0cdf0; border:none; height:3px;}
|
|
||||||
.old-road-line{background:#fff; border:1px solid #dcdcdc; height:3px; border-top:none;}
|
|
||||||
|
|
||||||
.legend-swatch{display:inline-block;width:24px;height:14px;border-radius:4px;background:#f5f5f5}
|
|
||||||
.border-swatch{border:2px dashed rgba(110,90,110,1);box-shadow:inset 0 0 0 1px rgba(255,255,255,1), 0 0 0 1px rgba(255,255,255,1)}
|
|
||||||
.cbd-swatch{background:#f0dccd; border:1px solid rgba(0,0,0,0.1)}
|
|
||||||
|
|
||||||
.legend-icon{display:inline-block;width:14px;height:14px;justify-self:center;border:2px solid #fff;border-radius:50%;box-shadow:0 0 0 1px rgba(0,0,0,0.15)}
|
|
||||||
.city-icon{background:#f06e6e;}
|
|
||||||
.satellite-icon{background:#d75f91;}
|
|
||||||
.station-icon{background:#fff;border-color:#444;}
|
|
||||||
.industry-icon{background:#8caaa0; border-radius:3px;}
|
|
||||||
.castle-icon{background:#b44646; border-radius:3px;}
|
|
||||||
|
|
||||||
.port-icon{width:0;height:0;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:14px solid #4682c8;border-top:0;box-shadow:none;background:transparent;border-radius:0}
|
|
||||||
.newtown-icon{width:0;height:0;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:14px solid #b4d2f0;border-top:0;box-shadow:none;background:transparent;border-radius:0}
|
|
||||||
|
|
||||||
.map-tooltip{position:absolute;z-index:20;pointer-events:none;min-width:200px;max-width:280px;background:rgba(255,255,255,0.74);border:1px solid rgba(0,0,0,0.10);border-radius:8px;box-shadow:0 10px 30px rgba(0,0,0,0.10);backdrop-filter:blur(4px);padding:10px 12px;color:#2c2c2c;font-size:12px;line-height:1.5;opacity:0;transform:translateY(4px);transition:opacity 0.15s ease, transform 0.15s ease;font-weight:500}
|
|
||||||
.map-tooltip.visible{opacity:1;transform:translateY(0)}
|
|
||||||
|
|
||||||
.generation-progress{position:absolute;inset:24px auto auto 24px;z-index:30;min-width:300px;max-width:440px;background:rgba(255,255,255,0.96);border:1px solid rgba(0,0,0,0.12);border-radius:12px;box-shadow:0 14px 36px rgba(0,0,0,0.14);padding:14px 16px;color:#202124;font-size:13px;line-height:1.5}
|
|
||||||
.generation-progress.hidden{display:none}
|
|
||||||
.progress-title{font-weight:700;margin-bottom:4px}
|
|
||||||
.progress-stage{color:#5f6368;margin-bottom:10px}
|
|
||||||
.progress-timings{display:flex;flex-direction:column;gap:4px;font-family:ui-monospace,monospace;font-size:12px;color:#3c4043}
|
|
||||||
.progress-timing-row{display:flex;justify-content:space-between;gap:16px;border-top:1px solid rgba(0,0,0,0.06);padding-top:4px}
|
|
||||||
|
|
||||||
.canvas-shell.panning{cursor:grabbing}
|
.canvas-shell.panning{cursor:grabbing}
|
||||||
.map-selection-svg{position:absolute;left:0;top:0;width:0;height:0;z-index:18;display:none;pointer-events:none;overflow:visible}
|
.canvas-shell.selecting,.canvas-shell.patch-intent{cursor:crosshair}
|
||||||
.map-selection-svg polygon{fill:rgba(26,115,232,0.16);stroke:rgba(26,115,232,0.88);stroke-width:2;vector-effect:non-scaling-stroke;stroke-linejoin:round}
|
.canvas-stage{
|
||||||
.map-selection-svg.invalid polygon{fill:rgba(179,38,30,0.14);stroke:rgba(179,38,30,0.88)}
|
position:absolute;
|
||||||
.canvas-shell.selecting{cursor:crosshair}
|
inset:0;
|
||||||
.map-selection{position:absolute;z-index:18;display:none;pointer-events:none;border:2px solid rgba(26,115,232,0.86);background:rgba(26,115,232,0.16);box-shadow:0 0 0 1px rgba(255,255,255,0.70) inset,0 8px 22px rgba(26,115,232,0.20)}
|
display:flex;
|
||||||
.primary-button:disabled{background:#a8b6c8;color:#eef3f8;cursor:not-allowed}
|
align-items:flex-end;
|
||||||
.patch-status{margin:10px 0 0;color:#5f6368;font-size:12px;line-height:1.45}
|
justify-content:center;
|
||||||
.patch-status.invalid{color:#b3261e;font-weight:600}
|
padding:12px;
|
||||||
.map-selection.invalid{border-color:rgba(179,38,30,0.88);background:rgba(179,38,30,0.14);box-shadow:0 0 0 1px rgba(255,255,255,0.70) inset,0 8px 22px rgba(179,38,30,0.18)}
|
}
|
||||||
|
.map-canvas{
|
||||||
|
display:block;
|
||||||
|
background:#0c1118;
|
||||||
|
border-radius:16px;
|
||||||
|
box-shadow:0 0 0 1px rgba(255,255,255,.06),0 12px 40px rgba(0,0,0,.28);
|
||||||
|
}
|
||||||
|
.generation-section{
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
gap:10px;
|
||||||
|
overflow:auto;
|
||||||
|
padding-bottom:0;
|
||||||
|
}
|
||||||
|
.settings-card,
|
||||||
|
.summary-section,
|
||||||
|
.advanced-details{
|
||||||
|
background:var(--surface);
|
||||||
|
border:1px solid var(--line);
|
||||||
|
border-radius:16px;
|
||||||
|
box-shadow:var(--shadow-soft);
|
||||||
|
}
|
||||||
|
.settings-card{padding:12px}
|
||||||
|
.settings-header-card{padding:10px 12px}
|
||||||
|
.section-title-row{display:flex;align-items:end;justify-content:space-between;gap:14px;margin:0 0 10px}
|
||||||
|
.section-title-row.compact{align-items:center;margin-bottom:10px}
|
||||||
|
.card-heading{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:10px}
|
||||||
|
.card-heading.tight{margin-bottom:0}
|
||||||
|
.eyebrow{font-size:10px;line-height:1;text-transform:uppercase;letter-spacing:.12em;color:var(--muted-2);font-weight:900;margin-bottom:5px}
|
||||||
|
h2,h3{margin:0;letter-spacing:-.02em}h2{font-size:16px}h3{font-size:14px}
|
||||||
|
.field{display:block;margin-bottom:10px}.field:last-child{margin-bottom:0}
|
||||||
|
.field-label{display:block;margin:0 0 6px;color:#cad5e5;font-size:12px;font-weight:850;letter-spacing:.01em}
|
||||||
|
.input{
|
||||||
|
width:100%;
|
||||||
|
border:1px solid var(--line-strong);
|
||||||
|
background:var(--surface-3);
|
||||||
|
color:var(--text);
|
||||||
|
border-radius:10px;
|
||||||
|
padding:9px 10px;
|
||||||
|
outline:none;
|
||||||
|
transition:border-color .16s,box-shadow .16s,background .16s;
|
||||||
|
min-height:38px;
|
||||||
|
}
|
||||||
|
.input:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(79,140,255,.18)}
|
||||||
|
.primary-button,.secondary-button,.ghost-button,.mode-button,.segmented-button{
|
||||||
|
border-radius:10px;
|
||||||
|
padding:9px 10px;
|
||||||
|
cursor:pointer;
|
||||||
|
font-weight:800;
|
||||||
|
transition:background .16s,border-color .16s,color .16s,transform .16s, box-shadow .16s;
|
||||||
|
min-height:38px;
|
||||||
|
}
|
||||||
|
.primary-button{border:1px solid var(--accent);background:var(--accent);color:#fff;box-shadow:0 8px 16px rgba(79,140,255,.18)}
|
||||||
|
.primary-button:hover{background:var(--accent-strong);border-color:var(--accent-strong)}
|
||||||
|
.secondary-button{border:1px solid rgba(79,140,255,.42);background:rgba(79,140,255,.10);color:#dbe7ff}
|
||||||
|
.secondary-button:hover{background:rgba(79,140,255,.16);border-color:rgba(79,140,255,.55)}
|
||||||
|
.ghost-button{border:1px solid var(--line-strong);background:transparent;color:#c8d2e1}.ghost-button:hover{background:rgba(255,255,255,.04);border-color:rgba(173,190,211,.32)}
|
||||||
|
.primary-button:disabled,.secondary-button:disabled,.ghost-button:disabled{background:#1a2029;color:#76859a;border-color:rgba(173,190,211,.08);box-shadow:none;cursor:not-allowed}
|
||||||
|
.button-stack{display:grid;gap:8px;margin-top:8px}.button-stack.two-col{grid-template-columns:1fr 1fr}.button-stack button{width:100%;font-size:12px;white-space:nowrap}
|
||||||
|
.segmented-group{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:6px;width:100%}
|
||||||
|
.segmented-button,.mode-button{border:1px solid var(--line-strong);background:var(--surface-3);color:#d2dbeb;box-shadow:none;white-space:nowrap}
|
||||||
|
.segmented-button{width:100%;display:flex;align-items:center;justify-content:center;text-align:center;padding-left:8px;padding-right:8px}
|
||||||
|
.segmented-button:hover,.mode-button:hover{background:#202b3a}
|
||||||
|
.segmented-button.active,.mode-button.active{background:var(--accent);border-color:var(--accent);color:#fff;box-shadow:0 8px 16px rgba(79,140,255,.16)}
|
||||||
|
.microcopy{margin:9px 0 0;color:var(--muted);font-size:11px;line-height:1.45}
|
||||||
|
.mode-grid{display:grid;grid-template-columns:1fr;gap:6px}.mode-button{min-height:32px;padding:7px 9px;font-size:12px;border-radius:9px;text-align:left}
|
||||||
|
.checkbox-row{display:flex;gap:8px;align-items:center;margin-top:9px;color:#d2dbeb;font-size:13px;cursor:pointer}.checkbox-row input{accent-color:var(--accent)}
|
||||||
|
.patch-status{margin:10px 0 0;color:var(--muted);font-size:12px;line-height:1.45}.patch-status.invalid{color:var(--danger);font-weight:750}
|
||||||
|
|
||||||
.patch-variant-row{display:grid;grid-template-columns:1fr 92px;gap:8px;align-items:end;margin-top:12px}
|
.map-dock{
|
||||||
.patch-variant-label{margin-bottom:0;align-self:center}
|
position:absolute;
|
||||||
.patch-variant-input{padding:8px 10px;text-align:right;font-family:ui-monospace,monospace}
|
top:16px;
|
||||||
.patch-button-row{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:12px}
|
right:16px;
|
||||||
.patch-button-row .primary-button,.patch-button-row .secondary-button{margin-top:0;width:100%}
|
z-index:24;
|
||||||
.secondary-button{border:1px solid rgba(26,115,232,0.35);border-radius:8px;padding:10px 12px;cursor:pointer;font-weight:600;background:#eef4ff;color:#1557b0;transition:background 0.2s,border-color 0.2s}
|
width:min(240px,28vw);
|
||||||
.secondary-button:hover{background:#e1edff;border-color:rgba(26,115,232,0.55)}
|
display:flex;
|
||||||
.secondary-button:disabled{background:#eef1f4;color:#8a98a8;border-color:rgba(0,0,0,0.08);cursor:not-allowed}
|
flex-direction:column;
|
||||||
|
gap:10px;
|
||||||
|
}
|
||||||
|
.dock-card{
|
||||||
|
background:rgba(17,22,31,.86);
|
||||||
|
border:1px solid rgba(173,190,211,.16);
|
||||||
|
border-radius:14px;
|
||||||
|
box-shadow:0 16px 34px rgba(0,0,0,.26);
|
||||||
|
overflow:hidden;
|
||||||
|
backdrop-filter:blur(12px);
|
||||||
|
}
|
||||||
|
.dock-card summary{
|
||||||
|
list-style:none;
|
||||||
|
cursor:pointer;
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
justify-content:space-between;
|
||||||
|
gap:8px;
|
||||||
|
min-height:42px;
|
||||||
|
padding:10px 10px;
|
||||||
|
font-size:13px;
|
||||||
|
font-weight:900;
|
||||||
|
color:var(--text);
|
||||||
|
border-bottom:1px solid transparent;
|
||||||
|
}
|
||||||
|
.dock-card summary::-webkit-details-marker{display:none}
|
||||||
|
.dock-card[open] summary{border-bottom-color:rgba(173,190,211,.10)}
|
||||||
|
.summary-button{
|
||||||
|
border:1px solid rgba(173,190,211,.24);
|
||||||
|
background:rgba(255,255,255,.04);
|
||||||
|
color:var(--muted);
|
||||||
|
border-radius:999px;
|
||||||
|
padding:2px 7px;
|
||||||
|
font-size:10px;
|
||||||
|
font-weight:850;
|
||||||
|
line-height:1.4;
|
||||||
|
}
|
||||||
|
.dock-card:not([open]) .summary-button::before{content:"Open"}
|
||||||
|
.dock-card[open] .summary-button::before{content:"Close"}
|
||||||
|
.summary-button{font-size:0}.summary-button::before{font-size:10px}
|
||||||
|
.dock-body{padding:10px}.divider{height:1px;background:rgba(173,190,211,.10);margin:10px 0}
|
||||||
|
|
||||||
|
.stats{display:flex;flex-direction:column;gap:0}
|
||||||
|
.summary-section{margin:12px;padding:12px}
|
||||||
|
.summary-stats{display:grid;grid-template-columns:repeat(6,minmax(120px,1fr));gap:0;border:1px solid var(--line);border-radius:12px;overflow:hidden;background:var(--surface-2)}
|
||||||
|
.stat-row{display:flex;justify-content:space-between;gap:12px;color:var(--muted);font-size:13px;align-items:baseline;padding:9px 10px;border-bottom:1px solid rgba(173,190,211,.08)}
|
||||||
|
.summary-stats .stat-row{display:grid;grid-template-columns:1fr;gap:4px;border-bottom:0;border-right:1px solid rgba(173,190,211,.08);min-height:58px}.summary-stats .stat-row:last-child{border-right:0}
|
||||||
|
.stat-row strong{color:#edf3fe;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;text-align:right;white-space:nowrap}.summary-stats .stat-row strong{text-align:left;font-size:15px}
|
||||||
|
.legend-grid{display:flex;flex-direction:column;gap:8px}.legend-row{display:grid;grid-template-columns:28px 1fr;gap:8px;align-items:center;min-height:20px;color:#d0dae9;font-size:12px;line-height:1.35}
|
||||||
|
.legend-line{display:inline-block;width:24px;height:4px;border-radius:999px;justify-self:center}.express-line{background:#87a087;border:1px solid #697d69}.road-line{background:#f5e182;border:1px solid #beaf8c}.river-major{background:#74a5ca;border:none;height:3px}.old-road-line{background:#fff;border:1px solid #8f8f86;height:3px}.minor-road-line{background:#fff;border:1px solid #a7a7a0;height:3px}.rail-line{background:#e8e8e8;height:1.5px;position:relative;border:none;margin-top:2px;border-radius:0}.rail-line::after{content:"";position:absolute;top:-2.5px;left:0;right:0;height:7px;background:repeating-linear-gradient(90deg,transparent,transparent 5px,#343a43 5px,#343a43 6px)}
|
||||||
|
.legend-swatch{display:inline-block;width:24px;height:14px;border-radius:4px;background:#f5f5f5;justify-self:center}.border-swatch{border:2px dashed rgba(171,145,171,1);box-shadow:inset 0 0 0 1px rgba(255,255,255,1),0 0 0 1px rgba(255,255,255,1)}.admin-swatch{border:2px dashed rgba(190,180,190,.9);background:#fff}.terrain-swatch{background:linear-gradient(90deg,#7da564,#e3daa2,#98a58f)}.urban-swatch{background:#f0dccd;border:1px solid rgba(0,0,0,.1)}.sea-swatch{background:#9fc7df;border:1px solid rgba(70,120,160,.35)}
|
||||||
|
.legend-icon{display:inline-block;width:14px;height:14px;justify-self:center;border:2px solid #fff;border-radius:50%;box-shadow:0 0 0 1px rgba(0,0,0,.15)}.city-icon{background:#f06e6e}.town-icon{width:10px;height:10px;border-radius:3px;background:#f06e6e}.station-icon{background:#fff;border-color:#444}.industry-icon{background:#8caaa0;border-radius:3px}.port-icon{width:0;height:0;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:14px solid #4682c8;border-top:0;box-shadow:none;background:transparent;border-radius:0}.castle-icon{background:#b44646;border-radius:3px}
|
||||||
|
|
||||||
|
.zoom-control{position:absolute;left:18px;bottom:18px;z-index:25;display:flex;flex-direction:column;overflow:hidden;border-radius:14px;background:rgba(12,17,24,.84);border:1px solid rgba(173,190,211,.16);box-shadow:0 14px 30px rgba(0,0,0,.26);backdrop-filter:blur(10px)}
|
||||||
|
.zoom-control button{min-width:58px;height:40px;border:0;border-bottom:1px solid rgba(173,190,211,.10);background:transparent;color:var(--text);font-weight:850;cursor:pointer}.zoom-control button:last-child{border-bottom:0;font-size:12px}.zoom-control button:hover{background:rgba(79,140,255,.12);color:#fff}
|
||||||
|
.map-hint{position:absolute;left:18px;top:18px;z-index:25;max-width:440px;background:rgba(14,27,50,.86);border:1px solid rgba(79,140,255,.36);color:#dce8ff;border-radius:13px;box-shadow:0 10px 26px rgba(0,0,0,.22);padding:10px 12px;font-size:13px;font-weight:800;backdrop-filter:blur(8px)}
|
||||||
|
.hidden{display:none!important}
|
||||||
|
.map-tooltip{position:absolute;z-index:26;pointer-events:none;min-width:200px;max-width:280px;background:rgba(17,22,31,.92);border:1px solid rgba(173,190,211,.16);border-radius:10px;box-shadow:0 10px 30px rgba(0,0,0,.28);backdrop-filter:blur(10px);padding:10px 12px;color:var(--text);font-size:12px;line-height:1.5;opacity:0;transform:translateY(4px);transition:opacity .15s ease,transform .15s ease;font-weight:500}.map-tooltip.visible{opacity:1;transform:translateY(0)}
|
||||||
|
.generation-progress{position:absolute;inset:18px auto auto 18px;z-index:30;min-width:300px;max-width:440px;background:rgba(17,22,31,.96);border:1px solid rgba(173,190,211,.16);border-radius:14px;box-shadow:0 14px 36px rgba(0,0,0,.34);padding:14px 16px;color:var(--text);font-size:13px;line-height:1.5}.progress-title{font-weight:850;margin-bottom:4px}.progress-stage{color:var(--muted);margin-bottom:10px}.progress-timings{display:flex;flex-direction:column;gap:4px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;color:#cfdbef}.progress-timing-row{display:flex;justify-content:space-between;gap:16px;border-top:1px solid rgba(173,190,211,.08);padding-top:4px}
|
||||||
|
.map-selection-svg{position:absolute;left:0;top:0;width:0;height:0;z-index:18;display:none;pointer-events:none;overflow:visible}.map-selection-svg polygon{fill:rgba(79,140,255,.16);stroke:rgba(79,140,255,.92);stroke-width:2;vector-effect:non-scaling-stroke;stroke-linejoin:round}.map-selection-svg.invalid polygon{fill:rgba(255,104,97,.14);stroke:rgba(255,104,97,.92)}.map-selection{position:absolute;z-index:18;display:none;pointer-events:none;border:2px solid rgba(79,140,255,.88);background:rgba(79,140,255,.16);box-shadow:0 0 0 1px rgba(255,255,255,.20) inset,0 8px 22px rgba(79,140,255,.20)}.map-selection.invalid{border-color:rgba(255,104,97,.92);background:rgba(255,104,97,.14);box-shadow:0 0 0 1px rgba(255,255,255,.20) inset,0 8px 22px rgba(255,104,97,.18)}
|
||||||
.map-canvas.is-zooming{image-rendering:auto;pointer-events:auto}
|
.map-canvas.is-zooming{image-rendering:auto;pointer-events:auto}
|
||||||
|
|
||||||
|
.advanced-section{padding:0 12px 12px}
|
||||||
|
.advanced-details{overflow:hidden}
|
||||||
|
.advanced-details summary{list-style:none;cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:10px;padding:10px 12px;color:var(--text);font-size:13px;font-weight:900}
|
||||||
|
.advanced-details summary::-webkit-details-marker{display:none}
|
||||||
|
.advanced-summary-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;min-width:0}
|
||||||
|
.copy-debug-button{
|
||||||
|
border:1px solid rgba(79,140,255,.42);
|
||||||
|
background:rgba(79,140,255,.10);
|
||||||
|
color:#dbe7ff;
|
||||||
|
border-radius:999px;
|
||||||
|
padding:4px 10px;
|
||||||
|
font-size:11px;
|
||||||
|
font-weight:850;
|
||||||
|
line-height:1.2;
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
.copy-debug-button:hover{background:rgba(79,140,255,.18);border-color:rgba(79,140,255,.58)}
|
||||||
|
.copy-debug-status{min-width:48px;color:var(--muted-2);font-size:11px;font-weight:800;text-align:right}
|
||||||
|
.copy-debug-status.error{color:var(--danger)}
|
||||||
|
.advanced-toggle{display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--line-strong);background:rgba(255,255,255,.04);color:var(--muted);border-radius:999px;padding:3px 10px;font-size:11px;font-weight:850;min-width:52px}
|
||||||
|
.advanced-details[open] .advanced-toggle{font-size:0}.advanced-details[open] .advanced-toggle::before{content:"Close";font-size:11px}
|
||||||
|
.advanced-body{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;border-top:1px solid var(--line);padding:12px;background:var(--surface-2)}
|
||||||
|
.debug-note{background:var(--surface-3);border:1px solid var(--line);border-radius:12px;padding:10px}.debug-note strong{display:block;font-size:12px;color:var(--text);margin-bottom:4px}.debug-note p{margin:0;color:var(--muted);font-size:12px;line-height:1.5}
|
||||||
|
|
||||||
|
@media (max-width:1280px){
|
||||||
|
.top-stage{grid-template-columns:minmax(0,1fr) 292px}
|
||||||
|
.summary-stats{grid-template-columns:repeat(3,minmax(120px,1fr))}
|
||||||
|
}
|
||||||
|
@media (max-width:980px){
|
||||||
|
.top-stage{height:auto;grid-template-columns:1fr;padding-top:10px}
|
||||||
|
.map-section{min-height:72dvh}
|
||||||
|
.generation-section{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));align-items:start}
|
||||||
|
.settings-header-card{grid-column:1/-1}
|
||||||
|
.map-dock{width:min(220px,38vw)}
|
||||||
|
.advanced-body{grid-template-columns:1fr}
|
||||||
|
}
|
||||||
|
@media (max-width:720px){
|
||||||
|
.generation-section{grid-template-columns:1fr}
|
||||||
|
.summary-stats{grid-template-columns:1fr}
|
||||||
|
.summary-stats .stat-row{border-right:0;border-bottom:1px solid rgba(173,190,211,.08)}
|
||||||
|
.summary-stats .stat-row:last-child{border-bottom:0}
|
||||||
|
.map-dock{top:12px;right:12px;left:auto;width:min(210px,48vw)}
|
||||||
|
.map-hint{left:12px;right:12px;top:12px;max-width:none}
|
||||||
|
.zoom-control{left:12px;bottom:12px}
|
||||||
|
}
|
||||||
|
@media (max-width:560px){
|
||||||
|
.top-stage{padding:8px 8px 0;gap:8px}
|
||||||
|
.summary-section{margin:8px;padding:10px}
|
||||||
|
.advanced-section{padding:0 8px 8px}
|
||||||
|
.canvas-stage{padding:8px}
|
||||||
|
.map-dock{position:absolute;width:min(200px,58vw)}
|
||||||
|
.button-stack.two-col,.segmented-group{grid-template-columns:1fr}
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug-panel{
|
||||||
|
background:var(--surface-3);
|
||||||
|
border:1px solid var(--line);
|
||||||
|
border-radius:14px;
|
||||||
|
padding:12px;
|
||||||
|
min-width:0;
|
||||||
|
}
|
||||||
|
.debug-panel.wide{grid-column:span 2}
|
||||||
|
.debug-panel-header{
|
||||||
|
display:flex;
|
||||||
|
align-items:flex-start;
|
||||||
|
justify-content:space-between;
|
||||||
|
gap:16px;
|
||||||
|
margin-bottom:10px;
|
||||||
|
}
|
||||||
|
.debug-panel-header p{
|
||||||
|
margin:0;
|
||||||
|
color:var(--muted);
|
||||||
|
font-size:12px;
|
||||||
|
line-height:1.45;
|
||||||
|
max-width:360px;
|
||||||
|
text-align:right;
|
||||||
|
}
|
||||||
|
.debug-note.flat{
|
||||||
|
background:rgba(255,255,255,.03);
|
||||||
|
box-shadow:none;
|
||||||
|
margin-top:8px;
|
||||||
|
}
|
||||||
|
.perf-history{
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
gap:8px;
|
||||||
|
}
|
||||||
|
.perf-empty{
|
||||||
|
border:1px dashed rgba(173,190,211,.18);
|
||||||
|
border-radius:12px;
|
||||||
|
padding:12px;
|
||||||
|
color:var(--muted);
|
||||||
|
font-size:12px;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
.perf-empty.inline{
|
||||||
|
grid-column:1/-1;
|
||||||
|
text-align:left;
|
||||||
|
}
|
||||||
|
.perf-run{
|
||||||
|
background:rgba(10,14,20,.34);
|
||||||
|
border:1px solid rgba(173,190,211,.12);
|
||||||
|
border-radius:12px;
|
||||||
|
overflow:hidden;
|
||||||
|
}
|
||||||
|
.perf-run summary::-webkit-details-marker{display:none}
|
||||||
|
.perf-summary{
|
||||||
|
list-style:none;
|
||||||
|
cursor:pointer;
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns:42px 1.2fr .7fr 1fr 1fr;
|
||||||
|
gap:10px;
|
||||||
|
align-items:center;
|
||||||
|
padding:9px 10px;
|
||||||
|
color:#dbe5f4;
|
||||||
|
font-size:12px;
|
||||||
|
}
|
||||||
|
.perf-summary strong{
|
||||||
|
color:#fff;
|
||||||
|
font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
|
||||||
|
font-size:12px;
|
||||||
|
}
|
||||||
|
.perf-summary span:last-child{color:#b8c8dd}
|
||||||
|
.perf-rank{
|
||||||
|
display:inline-flex;
|
||||||
|
align-items:center;
|
||||||
|
justify-content:center;
|
||||||
|
min-width:32px;
|
||||||
|
border-radius:999px;
|
||||||
|
padding:3px 7px;
|
||||||
|
background:rgba(79,140,255,.14);
|
||||||
|
color:#dce8ff;
|
||||||
|
font-weight:900;
|
||||||
|
}
|
||||||
|
.timing-grid{
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns:repeat(3,minmax(0,1fr));
|
||||||
|
gap:6px;
|
||||||
|
padding:0 10px 10px;
|
||||||
|
}
|
||||||
|
.timing-pill{
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
justify-content:space-between;
|
||||||
|
gap:8px;
|
||||||
|
padding:7px 8px;
|
||||||
|
border-radius:9px;
|
||||||
|
background:rgba(255,255,255,.04);
|
||||||
|
color:var(--muted);
|
||||||
|
font-size:11px;
|
||||||
|
min-width:0;
|
||||||
|
}
|
||||||
|
.timing-pill span{
|
||||||
|
overflow:hidden;
|
||||||
|
text-overflow:ellipsis;
|
||||||
|
white-space:nowrap;
|
||||||
|
}
|
||||||
|
.timing-pill strong{
|
||||||
|
color:#edf3fe;
|
||||||
|
font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
|
||||||
|
font-size:11px;
|
||||||
|
white-space:nowrap;
|
||||||
|
}
|
||||||
|
.interaction-table{
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
overflow:hidden;
|
||||||
|
border:1px solid rgba(173,190,211,.10);
|
||||||
|
border-radius:12px;
|
||||||
|
}
|
||||||
|
.interaction-row{
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns:1.1fr .75fr .55fr .55fr;
|
||||||
|
gap:8px;
|
||||||
|
align-items:center;
|
||||||
|
min-height:34px;
|
||||||
|
padding:7px 9px;
|
||||||
|
border-bottom:1px solid rgba(173,190,211,.08);
|
||||||
|
color:var(--muted);
|
||||||
|
font-size:12px;
|
||||||
|
}
|
||||||
|
.interaction-row:last-child{border-bottom:0}
|
||||||
|
.interaction-row strong{
|
||||||
|
color:#fff;
|
||||||
|
font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
|
||||||
|
font-size:12px;
|
||||||
|
}
|
||||||
|
@media (max-width:1280px){.debug-panel.wide{grid-column:1/-1}.timing-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
||||||
|
@media (max-width:720px){.advanced-summary-actions{gap:6px}.copy-debug-status{display:none}.copy-debug-button{padding:4px 8px}.debug-panel-header{display:block}.debug-panel-header p{text-align:left;margin-top:6px}.perf-summary{grid-template-columns:36px 1fr;gap:7px}.perf-summary strong,.perf-summary span:nth-child(n+4){grid-column:2}.timing-grid{grid-template-columns:1fr}.interaction-row{grid-template-columns:1fr .8fr}}
|
||||||
|
.metric-grid{
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns:repeat(6,minmax(0,1fr));
|
||||||
|
gap:6px;
|
||||||
|
margin-bottom:10px;
|
||||||
|
}
|
||||||
|
.metric-card{
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
gap:3px;
|
||||||
|
min-width:0;
|
||||||
|
padding:8px 9px;
|
||||||
|
border:1px solid rgba(173,190,211,.10);
|
||||||
|
border-radius:10px;
|
||||||
|
background:rgba(255,255,255,.035);
|
||||||
|
}
|
||||||
|
.metric-card span{
|
||||||
|
color:var(--muted);
|
||||||
|
font-size:10px;
|
||||||
|
font-weight:800;
|
||||||
|
text-transform:uppercase;
|
||||||
|
letter-spacing:.06em;
|
||||||
|
}
|
||||||
|
.metric-card strong{
|
||||||
|
color:#fff;
|
||||||
|
font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
|
||||||
|
font-size:13px;
|
||||||
|
white-space:nowrap;
|
||||||
|
}
|
||||||
|
.metric-card small{
|
||||||
|
color:var(--muted-2);
|
||||||
|
font-size:10px;
|
||||||
|
line-height:1.25;
|
||||||
|
}
|
||||||
|
.perf-summary.patch{
|
||||||
|
grid-template-columns:42px 1.5fr .7fr 1.25fr 1fr;
|
||||||
|
}
|
||||||
|
.timing-pill.meta{
|
||||||
|
background:rgba(79,140,255,.08);
|
||||||
|
border:1px solid rgba(79,140,255,.12);
|
||||||
|
}
|
||||||
|
.aggregate-header,
|
||||||
|
.aggregate-row{
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns:minmax(160px,1.3fr) .55fr .7fr .7fr .7fr;
|
||||||
|
gap:8px;
|
||||||
|
align-items:center;
|
||||||
|
}
|
||||||
|
.aggregate-header{
|
||||||
|
padding:0 9px 6px;
|
||||||
|
color:var(--muted-2);
|
||||||
|
font-size:10px;
|
||||||
|
font-weight:900;
|
||||||
|
text-transform:uppercase;
|
||||||
|
letter-spacing:.06em;
|
||||||
|
}
|
||||||
|
.aggregate-table{
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
overflow:hidden;
|
||||||
|
border:1px solid rgba(173,190,211,.10);
|
||||||
|
border-radius:12px;
|
||||||
|
margin-bottom:10px;
|
||||||
|
}
|
||||||
|
.aggregate-row{
|
||||||
|
min-height:34px;
|
||||||
|
padding:7px 9px;
|
||||||
|
border-bottom:1px solid rgba(173,190,211,.08);
|
||||||
|
color:var(--muted);
|
||||||
|
font-size:12px;
|
||||||
|
}
|
||||||
|
.aggregate-row:last-child{border-bottom:0}
|
||||||
|
.aggregate-row span{color:#dbe5f4;font-weight:800}
|
||||||
|
.aggregate-row small{color:var(--muted-2)}
|
||||||
|
.aggregate-row strong{
|
||||||
|
color:#fff;
|
||||||
|
font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
|
||||||
|
font-size:12px;
|
||||||
|
}
|
||||||
|
@media (max-width:1280px){.metric-grid{grid-template-columns:repeat(3,minmax(0,1fr))}}
|
||||||
|
@media (max-width:720px){.metric-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.aggregate-header,.aggregate-row{grid-template-columns:1fr .55fr}.aggregate-header strong,.aggregate-row strong{grid-column:2}.aggregate-header strong:nth-of-type(1)::before,.aggregate-row strong:nth-of-type(1)::before{content:"Avg ";color:var(--muted-2);font-family:inherit}.aggregate-header strong:nth-of-type(2)::before,.aggregate-row strong:nth-of-type(2)::before{content:"Max ";color:var(--muted-2);font-family:inherit}.aggregate-header strong:nth-of-type(3)::before,.aggregate-row strong:nth-of-type(3)::before{content:"P95 ";color:var(--muted-2);font-family:inherit}}
|
||||||
|
.diagnostic-grid{margin-bottom:10px}
|
||||||
|
.diagnostic-table{
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
overflow:hidden;
|
||||||
|
border:1px solid rgba(173,190,211,.10);
|
||||||
|
border-radius:12px;
|
||||||
|
background:rgba(10,14,20,.22);
|
||||||
|
}
|
||||||
|
.diagnostic-table.stacked{margin-top:10px}
|
||||||
|
.diagnostic-row{
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns:minmax(130px,1fr) minmax(90px,.75fr) minmax(120px,1fr);
|
||||||
|
gap:8px;
|
||||||
|
align-items:center;
|
||||||
|
min-height:34px;
|
||||||
|
padding:7px 9px;
|
||||||
|
border-bottom:1px solid rgba(173,190,211,.08);
|
||||||
|
color:var(--muted);
|
||||||
|
font-size:12px;
|
||||||
|
}
|
||||||
|
.diagnostic-row:last-child{border-bottom:0}
|
||||||
|
.diagnostic-row span{color:#dbe5f4;font-weight:800}
|
||||||
|
.diagnostic-row strong{color:#fff;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||||
|
.diagnostic-row small{color:var(--muted-2);line-height:1.25}
|
||||||
|
.diagnostic-log{
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
overflow:hidden;
|
||||||
|
border:1px solid rgba(173,190,211,.10);
|
||||||
|
border-radius:12px;
|
||||||
|
background:rgba(10,14,20,.22);
|
||||||
|
}
|
||||||
|
.diagnostic-log-row{
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns:86px minmax(140px,.7fr) minmax(0,1.8fr);
|
||||||
|
gap:10px;
|
||||||
|
align-items:start;
|
||||||
|
padding:8px 10px;
|
||||||
|
border-bottom:1px solid rgba(173,190,211,.08);
|
||||||
|
color:var(--muted);
|
||||||
|
font-size:12px;
|
||||||
|
}
|
||||||
|
.diagnostic-log-row:last-child{border-bottom:0}
|
||||||
|
.diagnostic-log-row span{color:var(--muted-2);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;white-space:nowrap}
|
||||||
|
.diagnostic-log-row strong{color:#edf3fe;font-size:12px}
|
||||||
|
.diagnostic-log-row p{margin:0;color:var(--muted);line-height:1.35;overflow-wrap:anywhere}
|
||||||
|
.diagnostic-log-row.warning strong{color:#ffd38a}
|
||||||
|
.diagnostic-log-row.error strong{color:#ff9c96}
|
||||||
|
.diagnostic-log-row.info strong{color:#b8d0ff}
|
||||||
|
@media (max-width:720px){
|
||||||
|
.diagnostic-row{grid-template-columns:1fr;gap:3px}
|
||||||
|
.diagnostic-log-row{grid-template-columns:1fr;gap:4px}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue