/* ============================================================ NetworkView — Global Styles Dark network-tool aesthetic ============================================================ */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } :root { --bg: #0d1117; --surface: #161b22; --surface2: #21262d; --surface3: #30363d; --border: #30363d; --border2: #21262d; --text: #e6edf3; --text2: #8b949e; --text3: #6e7681; --accent: #58a6ff; --accent-dim: #1f6feb; --success: #3fb950; --warning: #d29922; --danger: #f85149; --sidebar-w: 200px; --radius: 6px; --radius-lg: 10px; } html, body, #root { height: 100%; width: 100%; overflow-x: hidden; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; font-size: 14px; background: var(--bg); color: var(--text); scrollbar-width: thin; scrollbar-color: var(--surface3) transparent; } /* Slim scrollbar for all scrollable elements — Chrome/Safari/Edge */ ::-webkit-scrollbar { width: 6px; height: 6px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: var(--surface3); border-radius: 999px; } ::-webkit-scrollbar-thumb:hover { background: var(--border); } ::-webkit-scrollbar-corner { background: transparent; } a { color: var(--accent); text-decoration: none; } a:hover { text-decoration: underline; } /* ============================================================ Layout */ .app-layout { display: flex; height: 100vh; overflow: hidden; } .main-content { flex: 1; min-width: 0; overflow-x: hidden; overflow-y: auto; padding: 0; background: var(--bg); scrollbar-gutter: stable; } /* ============================================================ Sidebar */ .sidebar { width: var(--sidebar-w); min-width: var(--sidebar-w); background: var(--surface); border-right: 1px solid var(--border); display: flex; flex-direction: column; overflow-y: auto; overflow-x: hidden; } .sidebar-header { padding: 16px; border-bottom: 1px solid var(--border); } .sidebar-logo { display: flex; align-items: center; gap: 8px; color: var(--text) !important; text-decoration: none !important; font-weight: 600; font-size: 16px; } .logo-icon { font-size: 20px; } .logo-text { color: var(--accent); } .sidebar-search { padding: 10px 12px; position: relative; } .search-input { width: 100%; background: var(--surface2); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); padding: 6px 10px; font-size: 13px; outline: none; transition: border-color 0.15s; } .search-input:focus { border-color: var(--accent); } .search-results { position: absolute; top: calc(100% - 4px); left: 8px; right: 8px; background: var(--surface2); border: 1px solid var(--border); border-radius: var(--radius); z-index: 100; max-height: 300px; overflow-y: auto; box-shadow: 0 8px 24px rgba(0,0,0,0.4); } .search-result-item { display: flex; align-items: center; gap: 8px; padding: 8px 12px; color: var(--text) !important; font-size: 13px; text-decoration: none !important; transition: background 0.1s; } .search-result-item:hover { background: var(--surface3); } .search-result-icon { font-size: 12px; } .search-result-sub { color: var(--text2); font-size: 11px; } .search-no-results { padding: 12px; color: var(--text2); text-align: center; } .sidebar-nav { padding: 8px 0; flex: 1; } .sidebar-section-header { display: flex; align-items: center; justify-content: space-between; padding: 6px 12px; color: var(--text2); font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; } .icon-btn { background: none; border: none; color: var(--text2); cursor: pointer; font-size: 16px; line-height: 1; padding: 2px 4px; border-radius: var(--radius); transition: background 0.1s, color 0.1s; } .icon-btn:hover { background: var(--surface3); color: var(--accent); } .inline-add-form { display: flex; gap: 4px; padding: 4px 8px; align-items: center; } .inline-input { flex: 1; background: var(--surface2); border: 1px solid var(--accent); border-radius: var(--radius); color: var(--text); padding: 4px 8px; font-size: 12px; outline: none; } .inline-btn { background: var(--accent-dim); border: none; border-radius: var(--radius); color: white; cursor: pointer; font-size: 11px; padding: 4px 8px; } .inline-btn.secondary { background: var(--surface3); } /* Tree nodes */ .tree-node { display: flex; flex-direction: column; } .tree-children { padding-left: 16px; } .tree-empty { padding: 4px 8px 4px 32px; color: var(--text3); font-size: 12px; font-style: italic; } .tree-item { display: flex; align-items: center; padding: 2px 4px; border-radius: var(--radius); margin: 1px 4px; transition: background 0.1s; } .tree-item:hover { background: var(--surface2); } .tree-item.active { background: var(--accent-dim) !important; } .tree-item.active .tree-label { color: white; } .tree-expand { background: none; border: none; color: var(--text3); cursor: pointer; font-size: 10px; padding: 2px; width: 18px; text-align: center; flex-shrink: 0; } .tree-expand:hover { color: var(--text); } .tree-label { display: flex; align-items: center; gap: 5px; flex: 1; color: var(--text); text-decoration: none !important; font-size: 13px; padding: 2px 0; min-width: 0; } .tree-icon { font-size: 12px; flex-shrink: 0; } .tree-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .tree-count { background: var(--surface3); color: var(--text3); border-radius: 10px; font-size: 10px; padding: 1px 6px; flex-shrink: 0; } .tree-rack .tree-label { padding-left: 20px; } .tree-rack { margin-left: 4px; } .sidebar-loading { padding: 12px; color: var(--text2); font-size: 13px; } .sidebar-empty { padding: 16px; color: var(--text2); font-size: 13px; } .sidebar-empty p { margin-bottom: 8px; } /* ============================================================ Pages */ .page { padding: 20px 24px; max-width: 1400px; width: 100%; box-sizing: border-box; } .page-loading { padding: 48px; color: var(--text2); text-align: center; } .page-header { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 20px; gap: 16px; } .breadcrumb { display: flex; align-items: center; gap: 4px; color: var(--text2); font-size: 13px; flex-wrap: wrap; } .breadcrumb a { color: var(--accent); } .breadcrumb span:last-child { color: var(--text); } .page-actions { display: flex; gap: 8px; flex-shrink: 0; align-items: center; } /* Entity header */ .entity-header { margin-bottom: 24px; } .entity-title { font-size: 24px; font-weight: 600; display: flex; align-items: center; gap: 10px; margin-bottom: 8px; } .entity-icon { font-size: 22px; } .entity-location { color: var(--text2); font-size: 14px; } .entity-meta-row { display: flex; gap: 8px; flex-wrap: wrap; } .entity-meta-chip { background: var(--surface2); border: 1px solid var(--border); border-radius: var(--radius); padding: 3px 10px; font-size: 12px; color: var(--text2); } /* Content sections */ .content-section { margin-bottom: 32px; } .section-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } .section-title { font-size: 16px; font-weight: 600; color: var(--text); border-bottom: 1px solid var(--border); padding-bottom: 8px; flex: 1; margin-right: 16px; } .empty-state { padding: 24px; color: var(--text2); text-align: center; border: 1px dashed var(--border); border-radius: var(--radius-lg); } .empty-state-sm { padding: 12px; color: var(--text3); font-size: 13px; } /* ============================================================ Buttons */ .btn-primary { background: var(--accent-dim); border: 1px solid var(--accent-dim); border-radius: var(--radius); color: white; cursor: pointer; font-size: 14px; padding: 6px 14px; transition: background 0.15s; } .btn-primary:hover { background: var(--accent); } .btn-primary:disabled { opacity: 0.5; cursor: not-allowed; } .btn-secondary { background: var(--surface2); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); cursor: pointer; font-size: 14px; padding: 6px 14px; transition: background 0.15s; } .btn-secondary:hover { background: var(--surface3); } .btn-danger { background: transparent; border: 1px solid var(--danger); border-radius: var(--radius); color: var(--danger); cursor: pointer; font-size: 14px; padding: 6px 14px; transition: background 0.15s, color 0.15s; } .btn-danger:hover { background: var(--danger); color: white; } .btn-sm { font-size: 12px; padding: 4px 10px; } /* ============================================================ Forms */ .form-group { display: flex; flex-direction: column; gap: 4px; min-width: 0; flex: 1; } .form-group-lg { flex: 2; } .form-row { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; width: 100%; box-sizing: border-box; } .form-group label { font-size: 12px; color: var(--text2); font-weight: 500; } .form-input { background: var(--surface2); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); font-size: 14px; padding: 7px 10px; outline: none; transition: border-color 0.15s; width: 100%; } .form-input:focus { border-color: var(--accent); } .form-hint { font-size: 11px; color: var(--text3); } .form-error { background: rgba(248,81,73,0.1); border: 1px solid var(--danger); border-radius: var(--radius); color: var(--danger); padding: 8px 12px; font-size: 13px; margin-bottom: 12px; } .form-actions { display: flex; gap: 8px; margin-top: 8px; } .edit-meta-form { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 16px; margin-bottom: 24px; } /* ============================================================ Modal */ .modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.7); display: flex; align-items: center; justify-content: center; z-index: 200; padding: 16px; } .modal { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); width: 100%; max-width: 680px; max-height: 90vh; overflow-y: auto; box-shadow: 0 16px 48px rgba(0,0,0,0.6); } .modal-sm { max-width: 400px; } .modal-header { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; border-bottom: 1px solid var(--border); } .modal-title { font-size: 17px; font-weight: 600; } .modal-close { background: none; border: none; color: var(--text2); cursor: pointer; font-size: 16px; padding: 4px 8px; border-radius: var(--radius); } .modal-close:hover { background: var(--surface2); color: var(--text); } .modal-form { padding: 20px; } .modal-footer { display: flex; gap: 8px; justify-content: flex-end; padding-top: 16px; border-top: 1px solid var(--border); margin-top: 16px; } /* ============================================================ Rack Visual */ .rack-wrapper { width: 100%; max-width: 800px; } .rack-stats-bar { display: flex; gap: 16px; padding: 8px 0; margin-bottom: 16px; color: var(--text2); font-size: 13px; } .rack-stat strong { color: var(--text); } .rack-stat-used strong { color: var(--warning); } .rack-stat-free strong { color: var(--success); } .rack-cabinet { background: #0d1117; border: 3px solid #30363d; border-radius: 8px; overflow: hidden; box-shadow: 0 4px 24px rgba(0,0,0,0.5), inset 0 0 0 1px #21262d; } .rack-top-strip { background: linear-gradient(180deg, #21262d 0%, #161b22 100%); border-bottom: 2px solid #30363d; padding: 8px 12px; display: flex; align-items: center; gap: 12px; } .rack-label-text { color: var(--text); font-weight: 600; font-size: 13px; letter-spacing: 0.05em; } .rack-label-sub { color: var(--text2); font-size: 11px; } .rack-body { display: flex; flex-direction: column; } .rack-slot { display: flex; align-items: stretch; border-bottom: 1px solid #21262d; cursor: pointer; transition: filter 0.1s; min-height: 30px; position: relative; } .rack-slot:hover { filter: brightness(1.15); } .rack-slot-empty { background: #0d1117; } .rack-slot-empty:hover { background: #161b22; } .rack-unit-num { width: 34px; min-width: 34px; display: flex; align-items: flex-start; justify-content: center; padding-top: 7px; font-size: 10px; color: #484f58; border-right: 1px solid #21262d; font-family: 'SF Mono', 'Fira Code', monospace; background: #0d1117; flex-shrink: 0; } .rack-empty-label { flex: 1; display: flex; align-items: center; padding: 0 12px; color: #21262d; font-size: 11px; font-family: monospace; letter-spacing: 0.1em; } .rack-add-hint { opacity: 0; margin-right: 10px; font-size: 11px; color: var(--accent); transition: opacity 0.15s; } .rack-slot-empty:hover .rack-add-hint { opacity: 1; } .rack-slot-empty:hover .rack-empty-label { color: #484f58; } .rack-component-body { flex: 1; display: flex; flex-direction: column; justify-content: center; padding: 4px 10px; overflow: hidden; gap: 2px; } .rack-component-top { display: flex; align-items: center; gap: 8px; } .rack-component-type-badge { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; background: rgba(255,255,255,0.07); padding: 1px 5px; border-radius: 3px; flex-shrink: 0; } .rack-component-name { font-size: 13px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1; } .rack-component-status { font-size: 10px; flex-shrink: 0; } .rack-component-sub { display: flex; align-items: center; gap: 8px; } .rack-component-model, .rack-component-ip { font-size: 11px; color: rgba(255,255,255,0.5); font-family: 'SF Mono', 'Fira Code', monospace; } .rack-component-units { font-size: 11px; font-weight: 600; } .rack-component-model-inline, .rack-component-ip-inline { font-size: 11px; color: rgba(255,255,255,0.45); font-family: 'SF Mono', monospace; } .rack-component-units-inline { font-size: 11px; font-weight: 600; opacity: 0.7; } .rack-screw-col { display: flex; flex-direction: column; align-items: center; justify-content: space-around; padding: 4px 6px; gap: 4px; } .rack-screw { width: 8px; height: 8px; border-radius: 50%; background: #30363d; box-shadow: inset 1px 1px 0 rgba(255,255,255,0.1); } .rack-bottom-strip { height: 10px; background: linear-gradient(0deg, #21262d 0%, #161b22 100%); border-top: 2px solid #30363d; } /* Unpositioned */ .rack-unpositioned { margin-top: 20px; } .rack-unpositioned-title { font-size: 13px; color: var(--text2); margin-bottom: 8px; } .rack-unpositioned-list { display: flex; flex-direction: column; gap: 4px; } .rack-unpositioned-item { display: flex; align-items: center; gap: 10px; padding: 8px 12px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); cursor: pointer; transition: background 0.1s; font-size: 13px; } .rack-unpositioned-item:hover { background: var(--surface2); } .rack-unpositioned-name { color: var(--text); font-weight: 500; } /* ============================================================ Rack Cards */ .racks-grid { display: flex; flex-direction: column; gap: 12px; } .rack-card { display: flex; align-items: center; gap: 16px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 16px; text-decoration: none !important; transition: border-color 0.15s, background 0.15s; color: var(--text); } .rack-card:hover { border-color: var(--accent); background: var(--surface2); } .rack-card-visual { display: flex; flex-direction: column; gap: 2px; width: 30px; background: #0d1117; border: 1px solid #30363d; border-radius: 3px; padding: 3px; } .rack-card-unit { height: 5px; background: #21262d; border-radius: 1px; } .rack-card-info { flex: 1; } .rack-card-name { font-size: 16px; font-weight: 600; margin-bottom: 4px; } .rack-card-meta { font-size: 12px; color: var(--text2); } .rack-card-count { font-size: 12px; color: var(--text3); margin-top: 2px; } /* ============================================================ Dashboard */ .dashboard-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; } .dashboard-card { display: flex; align-items: center; gap: 12px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 20px; text-decoration: none !important; color: var(--text); transition: border-color 0.15s; } .dashboard-card:hover { border-color: var(--accent); } .dashboard-card-icon { font-size: 28px; } .dashboard-card-name { font-size: 16px; font-weight: 600; margin-bottom: 2px; } .dashboard-card-sub { font-size: 12px; color: var(--text2); } .dashboard-card-meta { font-size: 12px; color: var(--text3); margin-top: 4px; } /* ============================================================ Welcome */ .welcome-screen { max-width: 600px; margin: 80px auto; text-align: center; padding: 32px; } .welcome-icon { font-size: 56px; margin-bottom: 16px; } .welcome-title { font-size: 28px; font-weight: 700; margin-bottom: 12px; } .welcome-sub { color: var(--text2); font-size: 15px; line-height: 1.6; margin-bottom: 32px; } .welcome-steps { display: flex; flex-direction: column; gap: 16px; text-align: left; } .step { display: flex; align-items: flex-start; gap: 12px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 16px; font-size: 14px; color: var(--text2); } .step strong { color: var(--text); } .step-num { background: var(--accent-dim); color: white; width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; flex-shrink: 0; } /* ============================================================ Cards grid */ .cards-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; } .entity-card { display: flex; align-items: center; gap: 10px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 14px; text-decoration: none !important; color: var(--text); transition: border-color 0.15s; } .entity-card:hover { border-color: var(--accent); } .entity-card-icon { font-size: 20px; } .entity-card-name { font-size: 14px; font-weight: 500; } .entity-card-meta { font-size: 12px; color: var(--text3); } /* ============================================================ Component Table */ .component-table { width: 100%; border-collapse: collapse; font-size: 13px; table-layout: fixed; } .component-table th { padding: 8px 12px; text-align: left; color: var(--text2); font-weight: 500; font-size: 11px; text-transform: uppercase; border-bottom: 1px solid var(--border); } .component-table td { padding: 9px 12px; border-bottom: 1px solid var(--border2); } .component-table-row { cursor: pointer; transition: background 0.1s; } .component-table-row:hover { background: var(--surface); } .td-position { color: var(--text2); font-family: monospace; font-size: 12px; width: 36px; } .td-name { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .td-type { color: var(--text2); text-transform: capitalize; width: 90px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .td-model, .td-ip { color: var(--text3); font-family: monospace; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* Status badges */ .status-badge { display: inline-flex; padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; } .status-active { background: rgba(63,185,80,0.15); color: #3fb950; } .status-maintenance { background: rgba(210,153,34,0.15); color: #d29922; } .status-decommissioned { background: rgba(248,81,73,0.15); color: #f85149; } /* ============================================================ Component Detail */ .component-header { display: flex; align-items: center; gap: 14px; padding: 16px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); margin-bottom: 24px; flex-wrap: wrap; } .component-type-badge { padding: 4px 10px; border-radius: var(--radius); font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; } .component-meta-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; } .meta-field { background: var(--surface2); border-radius: var(--radius); padding: 10px 12px; } .meta-field-label { font-size: 11px; color: var(--text2); text-transform: uppercase; letter-spacing: 0.06em; display: block; margin-bottom: 4px; } .meta-field-value { font-size: 14px; color: var(--text); font-family: 'SF Mono', monospace; } /* ============================================================ Ports */ .add-port-form { background: var(--surface2); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 16px; margin-bottom: 16px; } .ports-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 8px; } .port-item { display: flex; align-items: center; gap: 10px; background: var(--surface2); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; } .port-number { width: 32px; height: 32px; background: var(--accent-dim); color: white; border-radius: var(--radius); display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; font-family: monospace; flex-shrink: 0; } .port-info { flex: 1; min-width: 0; } .port-label { font-size: 13px; font-weight: 500; color: var(--text); } .port-type { font-size: 11px; color: var(--text2); } .port-notes { font-size: 11px; color: var(--text3); font-style: italic; margin-top: 2px; } .port-link-chain { font-size: 11px; color: var(--accent); margin-top: 4px; font-family: monospace; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* Patch Panel link picker (inside switch port edit/add) */ .pp-link-picker { background: rgba(88,166,255,0.05); border: 1px solid rgba(88,166,255,0.18); border-radius: var(--radius); padding: 10px 12px; display: flex; flex-direction: column; gap: 7px; } .pp-link-label { font-size: 11px; font-weight: 700; color: var(--accent); text-transform: uppercase; letter-spacing: 0.08em; } .pp-link-row { display: flex; gap: 8px; flex-wrap: wrap; } .pp-link-row .form-input-sm { flex: 1; min-width: 140px; } .pp-link-device-hint { font-size: 12px; color: var(--text2); padding: 5px 8px; background: rgba(88,166,255,0.08); border-radius: 4px; } .pp-link-device-hint strong { color: var(--text1); } .pp-link-clear { background: none; border: 1px solid var(--border); color: var(--text3); cursor: pointer; font-size: 11px; padding: 3px 8px; border-radius: var(--radius); align-self: flex-start; transition: color 0.1s, border-color 0.1s; } .pp-link-clear:hover { color: var(--danger); border-color: var(--danger); } .port-delete { background: none; border: none; color: var(--text3); cursor: pointer; font-size: 12px; padding: 4px; border-radius: var(--radius); transition: color 0.1s, background 0.1s; flex-shrink: 0; } .port-delete:hover { background: rgba(248,81,73,0.15); color: var(--danger); }\n\n.port-edit-btn {\n background: none;\n border: none;\n color: var(--text3);\n cursor: pointer;\n font-size: 14px;\n padding: 4px;\n border-radius: var(--radius);\n transition: color 0.1s, background 0.1s;\n flex-shrink: 0;\n}\n.port-edit-btn:hover { background: rgba(88,166,255,0.1); color: var(--accent); } /* ============================================================ Markdown Editor */ .md-editor { border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; background: var(--surface); } .md-editor-toolbar { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; background: var(--surface2); border-bottom: 1px solid var(--border); gap: 12px; } .md-mode-tabs { display: flex; gap: 4px; } .md-mode-btn { background: none; border: 1px solid transparent; border-radius: var(--radius); color: var(--text2); cursor: pointer; font-size: 12px; padding: 4px 10px; transition: all 0.1s; } .md-mode-btn:hover { background: var(--surface3); color: var(--text); } .md-mode-btn.active { background: var(--accent-dim); border-color: var(--accent-dim); color: white; } .md-toolbar-actions { display: flex; align-items: center; gap: 12px; } .save-status { font-size: 12px; } .save-status-saved { color: var(--success); } .save-status-saving { color: var(--warning); } .save-status-unsaved { color: var(--text2); } .md-help-hint { font-size: 11px; color: var(--text3); } .md-editor-body { display: flex; min-height: 280px; } .md-mode-edit .md-editor-body { } .md-mode-preview .md-editor-body { } .md-mode-split .md-editor-body { } .md-textarea { flex: 1; background: var(--surface); border: none; color: var(--text); font-family: 'SF Mono', 'Fira Code', 'JetBrains Mono', monospace; font-size: 13px; line-height: 1.7; outline: none; padding: 16px; resize: vertical; min-height: 280px; width: 50%; } .md-mode-edit .md-textarea { width: 100%; } .md-mode-preview .md-textarea { display: none; } .md-preview { flex: 1; padding: 16px 20px; overflow-y: auto; min-height: 280px; border-left: 1px solid var(--border); width: 50%; } .md-mode-edit .md-preview { display: none; } .md-mode-preview .md-preview { width: 100%; border-left: none; } .md-preview-empty { color: var(--text3); font-style: italic; font-size: 13px; } /* Markdown content styles */ .md-preview h1, .md-preview h2, .md-preview h3, .md-preview h4, .md-preview h5, .md-preview h6 { color: var(--text); margin: 16px 0 8px; font-weight: 600; line-height: 1.3; } .md-preview h1 { font-size: 20px; border-bottom: 1px solid var(--border); padding-bottom: 8px; } .md-preview h2 { font-size: 17px; } .md-preview h3 { font-size: 15px; } .md-preview p { color: var(--text2); line-height: 1.7; margin-bottom: 12px; font-size: 14px; } .md-preview a { color: var(--accent); } .md-preview strong { color: var(--text); } .md-preview em { color: var(--text2); } .md-preview code { background: var(--surface2); border: 1px solid var(--border); border-radius: 4px; color: #ff7b72; font-family: 'SF Mono', monospace; font-size: 12px; padding: 1px 6px; } .md-preview pre { background: var(--surface2); border: 1px solid var(--border); border-radius: var(--radius); padding: 14px; overflow-x: auto; margin-bottom: 14px; } .md-preview pre code { background: none; border: none; color: var(--text); padding: 0; font-size: 13px; } .md-preview ul, .md-preview ol { color: var(--text2); padding-left: 24px; margin-bottom: 12px; } .md-preview li { margin-bottom: 4px; line-height: 1.6; } .md-preview li input[type="checkbox"] { margin-right: 6px; } .md-preview table { width: 100%; border-collapse: collapse; margin-bottom: 16px; font-size: 13px; } .md-preview th { background: var(--surface2); color: var(--text); padding: 8px 12px; text-align: left; border: 1px solid var(--border); font-weight: 600; } .md-preview td { padding: 7px 12px; border: 1px solid var(--border); color: var(--text2); } .md-preview tr:nth-child(even) td { background: var(--surface2); } .md-preview blockquote { border-left: 3px solid var(--accent-dim); padding: 8px 16px; color: var(--text2); margin: 12px 0; background: var(--surface2); border-radius: 0 var(--radius) var(--radius) 0; } .md-preview hr { border: none; border-top: 1px solid var(--border); margin: 20px 0; } /* ═══════════════════════════════════════════════════════════ RACK PAGE — TWO-COLUMN LAYOUT ═══════════════════════════════════════════════════════════ */ .page-rack { max-width: 100%; } .rack-page-columns { display: grid; grid-template-columns: 35fr 65fr; gap: 20px; align-items: start; margin-top: 12px; width: 100%; box-sizing: border-box; } .rack-page-left { display: flex; flex-direction: column; gap: 24px; min-width: 0; overflow: hidden; } .rack-page-right { position: sticky; top: 80px; align-self: start; min-width: 0; } .rack-page-right-scroll { max-height: calc(100vh - 120px); overflow-y: auto; overflow-x: hidden; } .rack-page-right-header { display: flex; align-items: baseline; gap: 12px; margin-bottom: 10px; } .rack-page-right-title { font-size: 13px; font-weight: 600; color: var(--text1); letter-spacing: 0.04em; } .rack-page-right-hint { font-size: 11px; color: var(--text3); } .rack-unpositioned-title { font-size: 11px; color: var(--text3); letter-spacing: 0.08em; text-transform: uppercase; margin-bottom: 8px; } .rack-unpositioned-list { display: flex; flex-direction: column; gap: 4px; } .rack-unpositioned-item { display: flex; align-items: center; gap: 8px; padding: 5px 8px; background: var(--surface); border-radius: 4px; cursor: pointer; transition: background 0.15s; } .rack-unpositioned-item:hover { background: var(--surface2); } .rack-unpositioned-name { font-size: 12px; color: var(--text1); } .port-count-row { background: rgba(99,179,237,0.05); border-radius: 6px; padding: 8px 4px 0; } /* ═══════════════════════════════════════════════════════════ RACK GRAPHIC — CABINET & SLOTS ═══════════════════════════════════════════════════════════ */ .rack-gfx { width: 100%; } .rack-gfx-cabinet { background: #06080d; border: 3px solid #2d3748; border-radius: 6px; overflow: hidden; box-shadow: 0 4px 24px rgba(0,0,0,0.7), inset 0 1px 0 rgba(255,255,255,0.04); } .rack-gfx-top { background: linear-gradient(180deg,#1a202c,#0d111a); border-bottom: 2px solid #2d3748; padding: 6px 10px; display: flex; gap: 10px; align-items: center; } .rack-gfx-title { color: #e2e8f0; font-weight: 700; font-size: 12px; letter-spacing: 0.1em; } .rack-gfx-sub { color: #718096; font-size: 10px; } .rack-gfx-units-badge { margin-left: auto; background: #1f2937; color: #6b7280; font-size: 10px; padding: 2px 6px; border-radius: 4px; font-family: monospace; } .rack-gfx-body { display: flex; flex-direction: column; } .rack-gfx-bottom { height: 8px; background: linear-gradient(0deg,#1a202c,#0d111a); border-top: 2px solid #2d3748; } .rack-gfx-slot { display: flex; align-items: stretch; border-bottom: 1px solid #1a202c; cursor: pointer; position: relative; transition: filter 0.12s; } .rack-gfx-slot:hover { filter: brightness(1.15); } .rack-gfx-empty { background: #060810; } .rack-gfx-empty:hover { background: #0c0e18; } .rack-gfx-u { width: 28px; min-width: 28px; font-size: 9px; color: #2d3748; font-family: monospace; text-align: center; align-self: stretch; display: flex; align-items: flex-start; justify-content: center; padding-top: 5px; background: #040609; border-right: 1px solid #1a202c; flex-shrink: 0; } .rack-gfx-empty-txt { flex: 1; text-align: center; font-size: 10px; color: #1a202c; font-family: monospace; letter-spacing: 0.1em; align-self: center; } .rack-gfx-add-hint { opacity: 0; font-size: 10px; color: #4299e1; margin-right: 8px; align-self: center; transition: opacity 0.15s; } .rack-gfx-empty:hover .rack-gfx-add-hint { opacity: 1; } .rack-gfx-empty:hover .rack-gfx-empty-txt { color: #2d3748; } .rack-gfx-face { flex: 1; overflow: hidden; min-width: 0; } .rack-gfx-screws { width: 10px; min-width: 10px; display: flex; flex-direction: column; align-items: center; justify-content: space-around; padding: 3px 2px; flex-shrink: 0; } .rack-gfx-screw { width: 6px; height: 6px; border-radius: 50%; background: #2d3748; box-shadow: inset 1px 1px 0 rgba(255,255,255,0.08); } /* ═══════════════════════════════════════════════════════════ HARDWARE FACES — BASE ═══════════════════════════════════════════════════════════ */ .hw-face { display: flex; align-items: stretch; width: 100%; overflow: hidden; } .hw-ports-main { flex: 1; display: flex; flex-direction: column; justify-content: center; padding: 3px 6px; gap: 2px; overflow: hidden; min-width: 0; } .hw-face-labels { width: 86px; min-width: 86px; display: flex; flex-direction: column; justify-content: center; align-items: flex-end; padding: 3px 6px; border-left: 1px solid rgba(255,255,255,0.06); gap: 1px; overflow: hidden; flex-shrink: 0; } .hw-pp { background: #1c2333; } .hw-sw { background: #0f1b35; } .hw-server { background: #111827; } .hw-fw { background: #120404; } .hw-router { background: #0a0518; } .hw-ups { background: #100c00; } .hw-pdu { background: #0a0012; } .hw-kvm { background: #00191a; } .hw-storage { background: #0a0900; } .hw-lbl-type { font-size: 7px; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase; } .hw-lbl-name { font-size: 9px; color: #e2e8f0; font-weight: 600; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; max-width: 80px; text-align: right; } .hw-lbl-model { font-size: 7.5px; color: #4a5568; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 80px; text-align: right; } .hw-lbl-ip { font-size: 7px; color: #68d391; font-family: monospace; text-align: right; white-space: nowrap; overflow: hidden; max-width: 80px; } .hw-lbl-count { font-size: 10px; font-weight: 700; } /* Patch Panel */ .hw-pp-header { margin-bottom: 1px; } .hw-pp-row { display: flex; gap: 2px; align-items: flex-end; flex-wrap: nowrap; overflow: hidden; } .hw-rj45-wrap { display: flex; flex-direction: column; align-items: center; gap: 1px; flex-shrink: 0; } .hw-rj45-jack { width: 12px; height: 9px; background: #000; border: 1.5px solid #4a5568; border-radius: 1px 1px 2px 2px; position: relative; flex-shrink: 0; } .hw-rj45-jack::before { content:''; position:absolute; top:1px; left:1.5px; right:1.5px; height:3px; background:#1a202c; } .hw-rj45-jack::after { content:''; position:absolute; bottom:1px; left:50%; transform:translateX(-50%); width:6px; height:1px; background:#2d3748; } .hw-rj45-num { font-size: 5px; color: #4a5568; font-family: monospace; line-height: 1; } /* Switch */ .hw-sw-row { display: flex; gap: 2px; align-items: flex-end; flex-wrap: nowrap; overflow: hidden; } .hw-sw-gap { width: 4px; flex-shrink: 0; } .hw-sw-port-wrap { display: flex; flex-direction: column; align-items: center; gap: 2px; flex-shrink: 0; } .hw-eth-port { width: 10px; height: 7px; background: #000; border: 1.5px solid #4a5568; border-radius: 1px; flex-shrink: 0; } .hw-eth-wan { border-color: #f6ad55; } .hw-sfp-wrap { display: flex; flex-direction: column; align-items: center; gap: 2px; flex-shrink: 0; } .hw-sfp-port { width: 13px; height: 7px; background: #000; border: 1.5px solid #4a5568; border-radius: 1px; display: flex; align-items: center; justify-content: center; } .hw-sfp-label { font-size: 5px; color: #4a5568; font-weight: 700; } /* LEDs */ .hw-led { width: 4px; height: 4px; border-radius: 50%; flex-shrink: 0; } .hw-led-green { background: #48bb78; box-shadow: 0 0 3px rgba(72,187,120,0.8); } .hw-led-amber { background: #ed8936; box-shadow: 0 0 3px rgba(237,137,54,0.8); } .hw-led-red { background: #fc8181; box-shadow: 0 0 3px rgba(252,129,129,0.8); } .hw-led-off { background: #2d3748; } /* Server / Storage */ .hw-server-body { flex-direction: row !important; align-items: center; gap: 8px; padding: 4px 8px !important; } .hw-drive-bays { display: flex; gap: 2px; align-items: center; flex-wrap: wrap; } .hw-drive-bay { width: 18px; height: 12px; background: #111; border: 1px solid #2d3748; border-radius: 2px; position: relative; flex-shrink: 0; } .hw-drive-bay::after { content:''; position:absolute; right:2px; top:50%; transform:translateY(-50%); width:2px; height:5px; background:#1a202c; border-radius:1px; } .hw-drive-bay-storage { width: 16px; height: 14px; background: #0a0800; border-color: #4a5568; } .hw-server-panel { display: flex; flex-direction: column; gap: 4px; align-items: center; } .hw-power-btn { width: 9px; height: 9px; border-radius: 50%; background: #1a202c; border: 1.5px solid #48bb78; position: relative; flex-shrink: 0; } .hw-power-btn::after { content:''; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); width:3px; height:3px; border-radius:50%; background:#48bb78; opacity:0.8; } .hw-usb { width: 7px; height: 4px; background: #000; border: 1px solid #4a5568; border-radius: 1px; flex-shrink: 0; } .hw-indicators-col { display: flex; flex-direction: column; gap: 3px; align-items: center; padding: 4px; } .hw-default-body { flex-direction: row !important; align-items: center; padding: 4px 8px !important; } .hw-kvm-ports { display: flex; gap: 3px; align-items: center; } /* UPS */ .hw-ups-body { flex-direction: row !important; align-items: center; gap: 8px; padding: 4px 8px !important; } .hw-ups-screen { background: #001400; border: 1px solid #1a3020; border-radius: 2px; padding: 3px 5px; display: flex; flex-direction: column; gap: 2px; min-width: 52px; } .hw-ups-bar-outer { background: #0a1a0a; border: 1px solid #1a3020; border-radius: 1px; height: 6px; width: 44px; } .hw-ups-bar-inner { height: 100%; background: #fb923c; border-radius: 1px; } .hw-ups-pct { color: #fb923c; font-size: 7px; font-family: monospace; } .hw-ups-indicators { display: flex; gap: 4px; align-items: center; } /* PDU */ .hw-pdu-outlet { display: flex; flex-direction: column; align-items: center; gap: 2px; flex-shrink: 0; } .hw-outlet-socket { width: 11px; height: 11px; background: #000; border: 1.5px solid #4a5568; border-radius: 50%; position: relative; } .hw-outlet-socket::before, .hw-outlet-socket::after { content:''; position:absolute; background:#000; border:1px solid #2d3748; border-radius:1px; } .hw-outlet-socket::before { width:2px; height:4px; top:1.5px; left:2.5px; } .hw-outlet-socket::after { width:2px; height:4px; top:1.5px; right:2.5px; } /* Back button */ .btn-back { background: transparent; border: 1px solid var(--border); color: var(--text2); border-radius: var(--radius); cursor: pointer; font-size: 12px; padding: 4px 10px; transition: background 0.15s, color 0.15s; } .btn-back:hover { background: var(--surface2); color: var(--text1); } /* Patch panel single-row layout */ .hw-pp-fullrow { flex-direction: row !important; align-items: center !important; padding: 0 6px !important; overflow: hidden !important; } .hw-pp-fullrow .hw-pp-row { flex: 1; min-width: 0; justify-content: space-between; gap: 1px; overflow: hidden; } .hw-pp-fullrow .hw-rj45-wrap { flex: 1; max-width: 18px; min-width: 0; } /* Force label area to always be visible */ .hw-face-labels { flex: 0 0 86px !important; width: 86px; min-width: 86px; } /* Switch full-width layout */ .hw-sw-body { flex: 1; display: flex; flex-direction: row; align-items: stretch; overflow: hidden; min-width: 0; padding: 2px 4px; gap: 4px; } .hw-sw-rj45-area { flex: 1; display: flex; flex-direction: column; justify-content: center; overflow: hidden; min-width: 0; } /* One row of pairs, fills 100% of rj45-area width */ .hw-sw-pairs-row { display: flex; gap: 2px; align-items: flex-start; overflow: hidden; width: 100%; } /* Each pair = column: top port (odd) + bottom port (even) + label */ .hw-sw-pair { display: flex; flex-direction: column; align-items: center; gap: 1px; flex: 1; min-width: 0; } .hw-sw-jack2 { width: 100%; height: 11px; background: #0a0a0a; border: 1.5px solid #2d3748; border-radius: 1px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; } .hw-sw-jack2-active { border-color: #3a5a3a; } .hw-sw-jack2-empty { opacity: 0; } .hw-sw-led2 { width: 4px; height: 4px; border-radius: 50%; flex-shrink: 0; } .hw-sw-num2 { font-size: 5px; color: #4a5568; font-family: monospace; line-height: 1; margin-top: 1px; } /* SFP/fiber uplink column */ .hw-sw-sfp-col { flex: 0 0 26px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; border-left: 1px solid rgba(255,165,0,0.2); padding: 2px 3px; } .hw-sw-sfp-title { font-size: 5px; color: #f6ad55; font-family: monospace; letter-spacing: 0.06em; margin-bottom: 1px; } .hw-sw-sfp-slot { width: 18px; height: 9px; background: #000; border: 1.5px solid #4a5568; border-radius: 1px; display: flex; align-items: center; justify-content: center; }