/* === CSS Variables (academic palette) === */ :root { --color-room: #E69F00; --color-door: #009E73; --color-corridor: #CC79A7; --color-outside: #56B4E9; --color-transition: #D55E00; --bg-primary: #f8f9fa; --bg-card: #ffffff; --bg-sidebar: #ffffff; --border-color: #dee2e6; --text-primary: #212529; --text-secondary: #6c757d; --accent: #0d6efd; --accent-hover: #0b5ed7; --sidebar-width: 320px; } /* === Reset / Base === */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background: var(--bg-primary); color: var(--text-primary); line-height: 1.5; } /* === App layout === */ .app { display: flex; flex-direction: column; height: 100vh; overflow: hidden; } /* === Header === */ .header { display: flex; align-items: center; justify-content: space-between; padding: 12px 24px; background: var(--bg-card); border-bottom: 1px solid var(--border-color); flex-shrink: 0; } .header h1 { font-size: 1.25rem; font-weight: 700; letter-spacing: -0.01em; } .header h1 span { color: var(--accent); } .header-subtitle { font-size: 0.8rem; color: var(--text-secondary); } /* === Main content area === */ .main-content { flex: 1; display: flex; overflow: hidden; } /* === Results layout: sidebar + canvas === */ .results-layout { display: flex; width: 100%; height: 100%; } .graph-area { flex: 1; position: relative; background: #fff; overflow: hidden; } .sidebar { width: var(--sidebar-width); min-width: var(--sidebar-width); background: var(--bg-sidebar); overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 16px; } .sidebar-left { border-right: 1px solid var(--border-color); } .sidebar-right { border-left: 1px solid var(--border-color); } /* === Cards / panels === */ .panel { background: var(--bg-card); border: 1px solid var(--border-color); border-radius: 8px; padding: 16px; } .panel h3 { font-size: 0.85rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-secondary); margin-bottom: 12px; } /* === Upload area (idle state) === */ .upload-container { display: flex; flex-direction: column; align-items: center; justify-content: flex-start; width: 100%; height: 100%; padding: 48px 40px; overflow-y: auto; } /* === Intro / landing === */ .intro { text-align: center; max-width: 680px; margin-bottom: 28px; } .intro-title { font-size: 2rem; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 14px; } .intro-title span { color: var(--accent); } .intro-abstract { font-size: 0.95rem; color: var(--text-secondary); line-height: 1.6; } .intro-footer { margin-top: 36px; padding-bottom: 8px; font-size: 0.8rem; color: var(--text-secondary); text-align: center; } .intro-footer a { color: var(--accent); text-decoration: none; } .intro-footer a:hover { text-decoration: underline; } .dropzone { width: 100%; max-width: 600px; border: 2px dashed var(--border-color); border-radius: 12px; padding: 48px 32px; text-align: center; cursor: pointer; transition: border-color 0.2s, background 0.2s; } .dropzone:hover, .dropzone.drag-over { border-color: var(--accent); background: rgba(13, 110, 253, 0.04); } .dropzone-icon { display: block; margin: 0 auto 14px; color: var(--accent); opacity: 0.85; transition: transform 0.2s, opacity 0.2s; } .dropzone:hover .dropzone-icon, .dropzone.drag-over .dropzone-icon { opacity: 1; transform: translateY(-2px); } .dropzone-text { font-size: 1rem; color: var(--text-secondary); margin-bottom: 8px; } .dropzone-sub { font-size: 0.8rem; color: var(--text-secondary); } /* === Example cards === */ .examples-section { margin-top: 32px; width: 100%; max-width: 600px; } .examples-section h2 { font-size: 0.95rem; font-weight: 600; margin-bottom: 16px; color: var(--text-primary); } .example-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; } .example-card { position: relative; border: 1px solid var(--border-color); border-radius: 8px; overflow: hidden; cursor: pointer; transition: box-shadow 0.2s, border-color 0.2s; background: var(--bg-card); } .example-card:hover { border-color: var(--accent); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); } .example-card img { width: 100%; height: 100px; object-fit: cover; display: block; } .example-card-info { padding: 8px 10px; } .example-card-name { font-size: 0.78rem; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .example-card-meta { font-size: 0.7rem; color: var(--text-secondary); display: flex; justify-content: space-between; align-items: center; margin-top: 4px; } .cached-badge { background: #d4edda; color: #155724; font-size: 0.65rem; font-weight: 600; padding: 1px 6px; border-radius: 4px; } /* === Processing overlay === */ .processing-overlay { display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; height: 100%; gap: 16px; } .spinner { width: 48px; height: 48px; border: 4px solid var(--border-color); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.8s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } .processing-text { font-size: 1rem; color: var(--text-secondary); } .processing-stage { font-size: 0.85rem; color: var(--accent); font-weight: 500; } /* === Error state === */ .error-container { display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; height: 100%; gap: 12px; } .error-message { color: #dc3545; font-size: 0.95rem; max-width: 400px; text-align: center; } /* === Graph controls === */ .type-toggle { display: flex; align-items: center; gap: 8px; padding: 4px 0; } .type-toggle label { font-size: 0.82rem; cursor: pointer; display: flex; align-items: center; gap: 6px; } .type-swatch { display: inline-block; width: 12px; height: 12px; border-radius: 3px; } /* === Node type control group (checkbox + slider) === */ .type-control-group { padding: 2px 0; } .size-slider { display: flex; align-items: center; gap: 6px; padding-left: 24px; } .size-slider input[type="range"] { flex: 1; height: 4px; -webkit-appearance: none; appearance: none; background: var(--border-color); border-radius: 2px; outline: none; } .size-slider input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 14px; height: 14px; border-radius: 50%; background: var(--accent); cursor: pointer; } .size-slider input[type="range"]::-moz-range-thumb { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); cursor: pointer; border: none; } .size-value { font-size: 0.72rem; color: var(--text-secondary); min-width: 20px; text-align: right; } /* === Stats panel === */ .stat-row { display: flex; justify-content: space-between; padding: 4px 0; font-size: 0.82rem; } .stat-row .label { color: var(--text-secondary); } .stat-row .value { font-weight: 600; } .stat-divider { border: none; border-top: 1px solid var(--border-color); margin: 8px 0; } /* === Export panel === */ .export-buttons { display: flex; gap: 8px; } .btn { padding: 8px 16px; border: 1px solid var(--border-color); border-radius: 6px; background: var(--bg-card); cursor: pointer; font-size: 0.82rem; font-weight: 500; transition: background 0.15s, border-color 0.15s; } .btn:hover { background: var(--bg-primary); border-color: var(--accent); } .btn-primary { background: var(--accent); color: #fff; border-color: var(--accent); } .btn-primary:hover { background: var(--accent-hover); } .btn-block { width: 100%; } /* === Tooltip === */ .node-tooltip { position: absolute; z-index: 1000; background: rgba(33, 37, 41, 0.95); color: #fff; padding: 8px 12px; border-radius: 6px; font-size: 0.75rem; line-height: 1.5; pointer-events: none; max-width: 220px; white-space: nowrap; } .tooltip-type { font-weight: 600; text-transform: capitalize; } /* === Graph stage toggle === */ .stage-label { font-size: 0.78rem; font-weight: 500; color: var(--text-secondary); display: block; margin-bottom: 6px; } .stage-toggle { display: flex; gap: 0; border: 1px solid var(--border-color); border-radius: 6px; overflow: hidden; } .stage-btn { flex: 1; padding: 6px 12px; font-size: 0.78rem; font-weight: 500; border: none; background: var(--bg-card); cursor: pointer; transition: background 0.15s, color 0.15s; color: var(--text-secondary); } .stage-btn:not(:last-child) { border-right: 1px solid var(--border-color); } .stage-btn.active { background: var(--accent); color: #fff; } .stage-btn:hover:not(.active) { background: var(--bg-primary); } /* === Back button === */ .back-btn-row { padding: 8px 0; } /* === Navigation / routing panel === */ .route-hint { font-size: 0.72rem; color: var(--text-secondary); margin-bottom: 12px; line-height: 1.4; } .route-field { display: flex; align-items: center; gap: 6px; font-size: 0.78rem; font-weight: 500; margin-bottom: 8px; } .route-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; } .route-select { flex: 1; min-width: 0; padding: 5px 8px; font-size: 0.78rem; border: 1px solid var(--border-color); border-radius: 6px; background: var(--bg-card); color: var(--text-primary); cursor: pointer; } .route-select:focus { outline: none; border-color: var(--accent); } .route-buttons { display: flex; gap: 8px; margin-top: 4px; } .route-buttons .btn { flex: 1; } .route-buttons .btn:disabled { opacity: 0.5; cursor: not-allowed; } .route-result { margin-top: 12px; padding: 10px 12px; border: 1px solid var(--border-color); border-left: 3px solid var(--accent); border-radius: 6px; background: var(--bg-primary); } .route-result-fail { border-left-color: #dc3545; font-size: 0.78rem; color: #dc3545; } .route-result-row { display: flex; justify-content: space-between; font-size: 0.8rem; padding: 2px 0; } .route-result-row .label { color: var(--text-secondary); } .route-result-row .value { font-weight: 600; } .route-badge { display: inline-block; margin-top: 6px; background: #fde2d4; color: #8a3b12; font-size: 0.68rem; font-weight: 600; padding: 2px 8px; border-radius: 4px; } /* === Header back button === */ .header-left { display: flex; align-items: center; gap: 14px; } .back-btn { display: inline-flex; align-items: center; gap: 4px; padding: 6px 12px; border: 1px solid var(--border-color); border-radius: 6px; background: var(--bg-card); cursor: pointer; font-size: 0.82rem; font-weight: 500; transition: background 0.15s, border-color 0.15s; } .back-btn:hover { background: var(--bg-primary); border-color: var(--accent); } .back-arrow { font-size: 1.1rem; line-height: 1; } /* === Collapsible panel header === */ .panel-collapse-header { display: flex; align-items: center; justify-content: space-between; width: 100%; background: none; border: none; padding: 0; margin: 0; cursor: pointer; } .panel-collapse-header h3 { margin-bottom: 0; } .panel-collapse-header .chevron { color: var(--text-secondary); transition: transform 0.18s; font-size: 1rem; } .panel-collapse-header .chevron.open { transform: rotate(90deg); } .panel-collapse-body { margin-top: 12px; } .stat-image { max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* === Opacity slider === */ .opacity-slider { padding-left: 0; margin-top: 6px; } .opacity-label { font-size: 0.72rem; color: var(--text-secondary); min-width: 48px; } /* === Floating canvas toolbar (zoom + fit) === */ .canvas-toolbar { position: absolute; right: 14px; bottom: 14px; display: flex; align-items: center; gap: 4px; background: rgba(255, 255, 255, 0.95); border: 1px solid var(--border-color); border-radius: 8px; padding: 4px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12); z-index: 50; } .zoom-btn { min-width: 30px; height: 30px; padding: 0 8px; border: none; border-radius: 6px; background: transparent; cursor: pointer; font-size: 1.1rem; font-weight: 600; color: var(--text-primary); display: inline-flex; align-items: center; justify-content: center; } .zoom-btn:hover { background: var(--bg-primary); } .zoom-btn.fit-btn { font-size: 0.8rem; font-weight: 600; border-left: 1px solid var(--border-color); margin-left: 2px; } .zoom-readout { font-size: 0.74rem; color: var(--text-secondary); min-width: 42px; text-align: center; } /* === Mode banner (overlay hint) === */ .mode-banner { position: absolute; top: 14px; left: 50%; transform: translateX(-50%); background: var(--accent); color: #fff; padding: 7px 16px; border-radius: 999px; font-size: 0.8rem; font-weight: 500; box-shadow: 0 2px 8px rgba(13, 110, 253, 0.3); z-index: 50; pointer-events: none; } /* === Navigation start/end pickers === */ .route-pick { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; } .route-pick-btn { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; min-width: 130px; } .route-pick-btn.arming { background: var(--accent); color: #fff; border-color: var(--accent); } .route-current { font-size: 0.78rem; color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .route-advanced { margin-top: 10px; } .route-advanced summary { font-size: 0.74rem; color: var(--text-secondary); cursor: pointer; margin-bottom: 8px; } /* === Edit toolkit === */ .edit-tools { display: flex; gap: 6px; margin-bottom: 6px; } .edit-tool { flex: 1; padding: 7px 6px; border: 1px solid var(--border-color); border-radius: 6px; background: var(--bg-card); cursor: pointer; font-size: 0.78rem; font-weight: 500; transition: background 0.15s, color 0.15s, border-color 0.15s; } .edit-tool:hover { border-color: var(--accent); } .edit-tool.active { background: var(--accent); color: #fff; border-color: var(--accent); } .edit-nodetype { margin-top: 10px; } .nodetype-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-top: 6px; } .nodetype-chip { display: inline-flex; align-items: center; gap: 6px; padding: 6px 8px; border: 1px solid var(--border-color); border-radius: 6px; background: var(--bg-card); cursor: pointer; font-size: 0.74rem; transition: border-color 0.15s, background 0.15s; } .nodetype-chip.active { border-color: var(--accent); background: rgba(13, 110, 253, 0.06); font-weight: 600; } .edit-hint { margin-top: 10px; padding: 8px 10px; background: var(--bg-primary); border-left: 3px solid var(--accent); border-radius: 6px; font-size: 0.76rem; color: var(--text-primary); } .edit-undo-note { margin-top: 10px; font-size: 0.7rem; color: var(--text-secondary); line-height: 1.4; } /* === Connectivity (stats panel) === */ .conn-ok { color: #1a7f37; font-weight: 700; margin-left: 4px; } .conn-warn { border: none; background: none; cursor: pointer; color: #d39e00; font-size: 0.95rem; margin-left: 4px; padding: 0 2px; line-height: 1; } .conn-warn:hover { color: #b8860b; } .conn-list { margin: 6px 0 4px; padding: 8px; background: #fff8e6; border: 1px solid #f0d98c; border-radius: 6px; } .conn-list-title { font-size: 0.72rem; color: #7a5b00; margin-bottom: 6px; line-height: 1.4; } .conn-node { display: inline-flex; align-items: center; gap: 6px; width: 100%; text-align: left; border: none; background: none; cursor: pointer; font-size: 0.74rem; color: var(--text-primary); padding: 3px 4px; border-radius: 4px; } .conn-node:hover { background: rgba(0, 0, 0, 0.05); } .conn-more { font-size: 0.7rem; color: var(--text-secondary); margin-top: 4px; padding-left: 4px; } .conn-flag { color: #b02a37; font-weight: 700; } .conn-locate { margin-top: 4px; font-style: italic; } .conn-node-id { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .conn-reason { font-size: 0.64rem; font-weight: 600; padding: 1px 6px; border-radius: 4px; flex-shrink: 0; } .conn-reason-isolated { background: #f8d7da; color: #842029; } .conn-reason-room { background: #fff3cd; color: #664d03; } .conn-reason-exit { background: #cfe2ff; color: #084298; }