body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 0;
}
.calc-container {
max-width: 900px;
margin: 20px auto;
padding: 20px;
}
/* ── Header ── */
.calc-header {
margin-bottom: 30px;
padding: 25px;
background: linear-gradient(135deg, #2196F3 0%, #1976D2 100%);
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
color: white;
}
.calc-header h2 {
margin-top: 0;
color: white;
font-size: 28px;
margin-bottom: 10px;
}
.calc-header p {
margin: 10px 0 20px 0;
opacity: 0.95;
font-size: 15px;
}
/* ── Form controls inside header ── */
.form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-top: 20px;
}
.form-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.form-group label {
font-weight: 600;
font-size: 14px;
color: white;
}
.form-group input,
.form-group select {
padding: 12px;
font-size: 15px;
border: 2px solid rgba(255,255,255,0.3);
border-radius: 6px;
background: rgba(255,255,255,0.95);
transition: all 0.3s ease;
font-family: 'Courier New', monospace;
}
.form-group input:focus,
.form-group select:focus {
outline: none;
border-color: #4CAF50;
background: white;
box-shadow: 0 0 0 3px rgba(76,175,80,0.15);
}
.form-group select {
cursor: pointer;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
/* ── Buttons ── */
.btn {
padding: 12px 24px;
font-size: 15px;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.btn-primary {
background: #4CAF50;
color: white;
}
.btn-primary:hover {
background: #45a049;
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0,0,0,0.25);
}
.btn-primary:active {
background: #3d8b40;
transform: translateY(0);
}
.btn-secondary {
background: rgba(255,255,255,0.2);
color: white;
border: 2px solid rgba(255,255,255,0.4);
}
.btn-secondary:hover {
background: rgba(255,255,255,0.3);
transform: translateY(-1px);
}
.btn-danger {
background: #f44336;
color: white;
}
.btn-danger:hover {
background: #d32f2f;
transform: translateY(-1px);
}
.btn-sm {
padding: 6px 14px;
font-size: 13px;
}
.submit-btn {
padding: 14px 35px;
font-size: 16px;
background: #4CAF50;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
margin-top: 15px;
width: 100%;
}
.submit-btn:hover {
background: #45a049;
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0,0,0,0.3);
}
.submit-btn:active {
background: #3d8b40;
transform: translateY(0);
}
/* ── Result sections ── */
.result-section {
background: white;
border: 1px solid #e0e0e0;
border-radius: 8px;
margin-top: 20px;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
transition: all 0.3s ease;
display: none;
}
.result-section.show {
display: block;
}
.result-section:hover {
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.section-header {
background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
color: white;
padding: 15px 20px;
}
.section-header h3 {
margin: 0;
font-size: 18px;
display: flex;
align-items: center;
gap: 10px;
}
.section-header.blue {
background: linear-gradient(135deg, #2196F3 0%, #1976D2 100%);
}
.section-header.orange {
background: linear-gradient(135deg, #FF9800 0%, #F57C00 100%);
}
.section-header.purple {
background: linear-gradient(135deg, #9C27B0 0%, #7B1FA2 100%);
}
.section-content {
padding: 20px;
background: #fafafa;
}
.data-row {
display: flex;
margin: 10px 0;
padding: 10px;
background: white;
border-radius: 4px;
border-left: 3px solid #4CAF50;
align-items: flex-start;
}
.data-label {
font-weight: bold;
color: #555;
min-width: 200px;
flex-shrink: 0;
font-size: 14px;
padding-top: 1px;
}
.data-value {
color: #333;
font-family: 'Courier New', monospace;
word-break: break-word;
font-size: 15px;
}
.data-value.highlight {
color: #1976D2;
font-weight: bold;
font-size: 18px;
}
/* ── Help text ── */
.help-text {
margin-top: 15px;
font-size: 14px;
color: rgba(255,255,255,0.9);
background: rgba(0,0,0,0.1);
padding: 10px 15px;
border-radius: 4px;
}
.help-text strong {
color: white;
}
/* ── Topology canvas area ── */
#cy-container {
width: 100%;
height: 380px;
background: #1a2332;
border-radius: 8px;
border: 2px solid #2d3d52;
position: relative;
overflow: hidden;
}
#cy {
width: 100%;
height: 100%;
}
/* ── Switch list ── */
.switch-list,
.link-list {
list-style: none;
margin: 0;
padding: 0;
}
.switch-item,
.link-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 12px;
background: white;
border-radius: 5px;
margin-bottom: 6px;
border-left: 4px solid #2196F3;
font-size: 14px;
flex-wrap: wrap;
}
.switch-item .sw-id {
font-family: 'Courier New', monospace;
font-weight: bold;
color: #1976D2;
min-width: 40px;
}
.switch-item .sw-name {
flex: 1;
font-weight: 600;
}
.switch-item .sw-priority,
.switch-item .sw-mac {
font-family: 'Courier New', monospace;
font-size: 13px;
color: #666;
}
.link-item {
border-left-color: #FF9800;
}
.link-item .lk-label {
flex: 1;
font-family: 'Courier New', monospace;
font-size: 13px;
}
.tag {
display: inline-block;
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: bold;
}
.tag-root { background: #FFF3CD; color: #856404; }
.tag-fwd { background: #D4EDDA; color: #155724; }
.tag-blk { background: #F8D7DA; color: #721C24; }
.tag-disc { background: #D1ECF1; color: #0C5460; }
.tag-lrn { background: #CCE5FF; color: #004085; }
.tag-edge { background: #E2D9F3; color: #491F8A; }
/* ── Port state table ── */
.port-table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
margin-top: 8px;
}
.port-table th {
background: #E3F2FD;
color: #1565C0;
padding: 8px 12px;
text-align: left;
font-weight: 600;
border-bottom: 2px solid #BBDEFB;
}
.port-table td {
padding: 7px 12px;
border-bottom: 1px solid #f0f0f0;
font-family: 'Courier New', monospace;
font-size: 13px;
}
.port-table tr:last-child td {
border-bottom: none;
}
.port-table tr:nth-child(even) td {
background: #f8f9fa;
}
/* ── Timeline ── */
.timeline {
position: relative;
padding-left: 28px;
}
.timeline::before {
content: '';
position: absolute;
left: 8px;
top: 0;
bottom: 0;
width: 2px;
background: #BBDEFB;
}
.timeline-event {
position: relative;
margin-bottom: 12px;
background: white;
border-radius: 6px;
padding: 10px 14px;
border: 1px solid #e8e8e8;
font-size: 13px;
}
.timeline-event::before {
content: '';
position: absolute;
left: -24px;
top: 12px;
width: 10px;
height: 10px;
border-radius: 50%;
background: #2196F3;
border: 2px solid white;
box-shadow: 0 0 0 2px #2196F3;
}
.timeline-event.root-election::before { background: #FFC107; box-shadow: 0 0 0 2px #FFC107; }
.timeline-event.port-change::before { background: #4CAF50; box-shadow: 0 0 0 2px #4CAF50; }
.timeline-event.port-blocked::before { background: #f44336; box-shadow: 0 0 0 2px #f44336; }
.timeline-event.convergence::before { background: #9C27B0; box-shadow: 0 0 0 2px #9C27B0; }
.timeline-event .te-time {
font-weight: bold;
color: #1976D2;
font-family: 'Courier New', monospace;
margin-right: 8px;
}
.timeline-event .te-desc {
color: #333;
}
/* ── BPDU Inspector ── */
.bpdu-panel {
background: #0d1117;
border-radius: 6px;
padding: 15px;
font-family: 'Courier New', monospace;
font-size: 13px;
color: #79c0ff;
max-height: 300px;
overflow-y: auto;
}
.bpdu-row {
display: flex;
gap: 8px;
margin-bottom: 3px;
line-height: 1.5;
}
.bpdu-field {
color: #d2a8ff;
min-width: 180px;
flex-shrink: 0;
}
.bpdu-val {
color: #a5f3fc;
}
.bpdu-val.root { color: #fbbf24; }
.bpdu-val.bridge { color: #86efac; }
/* ── Scenario selector ── */
.scenario-bar {
display: flex;
gap: 10px;
align-items: center;
flex-wrap: wrap;
margin-top: 10px;
}
.scenario-bar label {
font-weight: 600;
font-size: 14px;
color: white;
}
.scenario-bar select {
padding: 8px 12px;
border: 2px solid rgba(255,255,255,0.3);
border-radius: 6px;
background: rgba(255,255,255,0.95);
font-size: 14px;
cursor: pointer;
flex: 1;
min-width: 200px;
}
/* ── Legend ── */
.legend {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 8px;
font-size: 13px;
}
.legend-item {
display: flex;
align-items: center;
gap: 5px;
}
.legend-dot {
width: 12px;
height: 12px;
border-radius: 50%;
flex-shrink: 0;
}
/* ── Inline error / info messages ── */
.msg {
padding: 10px 14px;
border-radius: 6px;
font-size: 14px;
margin-top: 8px;
}
.msg-error {
background: #FFEBEE;
color: #C62828;
border-left: 4px solid #f44336;
}
.msg-info {
background: #E3F2FD;
color: #1565C0;
border-left: 4px solid #2196F3;
}
.msg-success {
background: #E8F5E9;
color: #1B5E20;
border-left: 4px solid #4CAF50;
}
/* ── STP concept panel ── */
.concept-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.concept-card {
background: white;
border-radius: 6px;
padding: 14px;
border-top: 3px solid #2196F3;
font-size: 13px;
}
.concept-card h4 {
margin: 0 0 6px 0;
color: #1976D2;
font-size: 14px;
}
.concept-card p {
margin: 0;
color: #555;
line-height: 1.5;
}
.concept-card.rstp {
border-top-color: #9C27B0;
}
.concept-card.rstp h4 {
color: #7B1FA2;
}
/* ── Mode toggle ── */
.mode-toggle {
display: flex;
gap: 0;
margin-top: 15px;
background: rgba(0,0,0,0.15);
border-radius: 6px;
padding: 4px;
}
.mode-btn {
flex: 1;
padding: 8px 16px;
border: none;
background: transparent;
color: rgba(255,255,255,0.7);
cursor: pointer;
border-radius: 4px;
font-size: 14px;
font-weight: 600;
transition: all 0.2s ease;
}
.mode-btn.active {
background: white;
color: #1976D2;
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
}
/* ── Pulse animation for root bridge ── */
@keyframes rootPulse {
0% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.7); }
70% { box-shadow: 0 0 0 10px rgba(255, 193, 7, 0); }
100% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0); }
}
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
.fade-in-up {
animation: fadeInUp 0.35s ease both;
}
/* ── Responsive ── */
@media (max-width: 768px) {
.form-grid {
grid-template-columns: 1fr;
}
.data-row {
flex-direction: column;
}
.data-label {
margin-bottom: 4px;
min-width: auto;
}
.concept-grid {
grid-template-columns: 1fr;
}
#cy-container {
height: 280px;
}
.port-table {
font-size: 12px;
}
.port-table th,
.port-table td {
padding: 6px 8px;
}
.mode-btn {
padding: 8px 8px;
font-size: 13px;
}
.scenario-bar {
flex-direction: column;
align-items: stretch;
}
.scenario-bar label {
display: block;
}
}
@media (max-width: 480px) {
.calc-header {
padding: 18px;
}
.calc-header h2 {
font-size: 22px;
}
.switch-item,
.link-item {
flex-wrap: wrap;
}
}
═════════════════════════════ ══════════════════════════════
KEPALA / KONFIGURASI
═════════════════════════════ ══════════════════════════════
/.calc-header
═════════════════════════════ ══════════════════════════════
DAFTAR TOPOLOGI SAAT INI
═════════════════════════════ ══════════════════════════════
Beralih
- Belum ada tombol yang ditambahkan.
Tautan
- Belum ada tautan yang ditambahkan.
═════════════════════════════ ══════════════════════════════
VISUALISASI TOPOLOGI
═════════════════════════════ ══════════════════════════════
Legenda
Memblokir / Membuang Port
Garis putus-putus = tautan diblokir
═════════════════════════════ ══════════════════════════════
PEMILU JEMBATAN AKAR
═════════════════════════════ ══════════════════════════════
═════════════════════════════ ══════════════════════════════
NEGARA PELABUHAN
═════════════════════════════ ══════════════════════════════
═════════════════════════════ ══════════════════════════════
INSPEKTUR BPDU
═════════════════════════════ ══════════════════════════════
═════════════════════════════ ══════════════════════════════
WAKTU KONVERGENSI
═════════════════════════════ ══════════════════════════════
═════════════════════════════ ══════════════════════════════
KONSEP STP
═════════════════════════════ ══════════════════════════════
Pemilihan Jembatan Akar
Switch dengan Bridge ID terendah menang. Bridge ID = Prioritas (default 32768) + alamat MAC. Ikatan pada prioritas mengarah ke MAC yang secara numerik lebih rendah. Hanya satu jembatan akar per VLAN.
Pelabuhan Akar
Setiap switch non-root memilih satu Root Port — port dengan biaya jalur kumulatif terendah ke root bridge. Ikatan diputus oleh ID Bridge tetangga, lalu ID port.
Pelabuhan yang Ditunjuk
Setiap segmen jaringan memiliki tepat satu Port yang Ditunjuk — port yang menawarkan jalur terbaik (biaya terendah) ke root. Semua port di jembatan akar telah ditetapkan.
Port yang Diblokir / Membuang
Port apa pun yang bukan merupakan Root Port atau Designated Port akan diblokir (STP) atau dibuang (RSTP). Ini memutus loop Layer-2 sambil mempertahankan topologi bebas loop.
Port Tepi RSTP (802.1w).
Port tepi RSTP terhubung ke host akhir, bukan switch lainnya. Mereka segera bertransisi ke Penerusan (tidak ada penundaan mendengarkan/belajar), setara dengan fitur PortFast STP.
Kecepatan Konvergensi RSTP
Konvergensi STP membutuhkan waktu 30–50 detik (Mendengarkan 15 detik + Belajar 15 detik per port). RSTP menggunakan jabat tangan Proposal/Perjanjian, yang menyatu dalam waktu kurang dari 2 detik per hop.
Biaya Jalur (IEEE 802.1D)
10 Gbps = 2, 1 Gbps = 4, 100 Mbps = 19, 10 Mbps = 100, 1,5 Mbps = 1000. Biaya lebih rendah = jalur pilihan. Jembatan akar mengiklankan biaya 0; setiap hop menambahkan biaya portnya.
BPDU (Unit Data Protokol Jembatan)
BPDU membawa: Root Bridge ID, Root Path Cost, Sender Bridge ID, Port ID, dan timer. Root bridge berasal dari BPDU setiap Hello Time (2 detik). Jembatan lain meneruskannya ke Pelabuhan yang Ditunjuk.
/.calc-container
CDN Cytoscape.js