Răsfoiți Sursa

Added desktop style

Jonas van Leeuwen 8 ore în urmă
părinte
comite
eff7ad5de3
5 a modificat fișierele cu 700 adăugiri și 91 ștergeri
  1. 358 70
      assets/css/styles.css
  2. BIN
      assets/images/ctftime.png
  3. BIN
      assets/images/tuxforge.png
  4. 218 0
      assets/js/script.js
  5. 124 21
      index.html

+ 358 - 70
assets/css/styles.css

@@ -1,116 +1,404 @@
 :root {
-  --bg-main: #213248;
-  --bg-card: #000;
-  --color-primary: #4074cf;
+  --bg-main:    #213248;
+  --bg-card:    #000;
+  --color-primary:       #4074cf;
   --color-primary-hover: #5585d8;
-  --color-text: #e2ecf8;
+  --color-text:  #e2ecf8;
   --color-muted: #8aaecb;
   --border-soft: #4074cf33;
-}
+  --adwaita-top:    #444;
+  --adwaita-bottom: #393939;
+  --adwaita-border: #1e1e1e;
+  --adwaita-label:  rgba(255,255,255,0.87);
+  --wm-close-bg:    #f05454;
+  --wm-close-bdr:   #c94040;
+  --wm-grey-bg:     #c9c9c9;
+  --wm-grey-bdr:    #a0a0a0;
+  --tb-h:        44px;
+  --tb-bg:       rgba(15,22,35,0.97);
+  --tb-border:   rgba(64,116,207,0.18);
+  --tb-text:     #c8ddf0;
+  --sm-bg:       rgba(18,28,46,0.97);
+  --sm-border:   rgba(64,116,207,0.25);
+  --sm-w-left:   220px;
+  --sm-w-right:  180px;
+  --sm-h:        420px;
 
-* {
-  box-sizing: border-box;
-  margin: 0;
-  padding: 0;
+  --handle-size: 6px;
+  --corner-size: 12px;
 }
 
+*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
+button { font-family: inherit; border: none; background: none; cursor: pointer; color: inherit; }
+a { color: inherit; text-decoration: none; }
+
 body {
   background-color: var(--bg-main);
   background-image: url(/assets/images/background.png);
   color: var(--color-text);
   font-family: 'Share Tech Mono', monospace;
   height: 100vh;
-  display: flex;
-  align-items: center;
-  justify-content: center;
+  overflow: hidden;
 }
 
-.container {
-  max-width: 560px;
-  width: 100%;
-  padding: 1.5rem;
-  text-align: center;
+.terminal {
+  position: fixed;
+  width: 560px;
+  min-width: 320px;
+  min-height: 160px;
+  background: var(--bg-card);
+  border: 1px solid var(--border-soft);
+  border-radius: 8px;
+  box-shadow: 0 8px 32px rgba(0,0,0,0.6);
+  overflow: visible;
+  transition: box-shadow 0.15s;
 }
 
-.subtitle {
-  color: var(--color-primary);
-  font-size: 0.75rem;
-  letter-spacing: 0.2em;
-  margin-bottom: 0.5rem;
+.terminal.maximized { border-radius: 0; box-shadow: none; }
+
+.terminal.flash { animation: wflash 0.3s ease; }
+@keyframes wflash {
+  0%,100% { box-shadow: 0 8px 32px rgba(0,0,0,0.6); }
+  50%      { box-shadow: 0 0 0 3px var(--color-primary); }
 }
 
-.title {
-  font-size: 3.5rem;
-  font-weight: 700;
-  margin-bottom: 1.5rem;
-  line-height: 1;
-  color: #ffffff;
+.terminal-titlebar {
+  display: flex;
+  align-items: center;
+  height: 38px;
+  padding: 0 10px;
+  background: linear-gradient(to bottom, var(--adwaita-top), var(--adwaita-bottom));
+  border-bottom: 1px solid var(--adwaita-border);
+  border-radius: 8px 8px 0 0;
+  cursor: grab;
+  user-select: none;
+  position: relative;
 }
+.terminal-titlebar:active { cursor: grabbing; }
+.terminal.maximized .terminal-titlebar { border-radius: 0; }
 
-.terminal {
-  background: var(--bg-card);
-  border: 1px solid var(--border-soft);
-  border-radius: 6px;
-  padding: 20px;
-  text-align: left;
-  margin-bottom: 1.5rem;
+.titlebar-label {
+  position: absolute;
+  left: 50%; transform: translateX(-50%);
+  font-size: 0.78rem;
+  color: var(--adwaita-label);
+  letter-spacing: 0.04em;
+  pointer-events: none;
+  white-space: nowrap;
 }
 
-.terminal-header {
-  color: var(--color-primary);
-  font-size: 0.8rem;
-  margin-bottom: 12px;
+.titlebar-controls { display: flex; align-items: center; gap: 6px; margin-left: auto; }
+
+.wm-btn {
+  width: 18px; height: 18px;
+  border-radius: 50%;
+  display: flex; align-items: center; justify-content: center;
+  transition: filter 0.12s;
 }
+.wm-btn:hover { filter: brightness(1.2); }
+.wm-btn svg { opacity: 0; transition: opacity 0.12s; display: block; }
+.terminal-titlebar:hover .wm-btn svg { opacity: 1; }
+
+.wm-close    { background: var(--wm-close-bg); box-shadow: inset 0 0 0 0.5px var(--wm-close-bdr); }
+.wm-minimize { background: var(--wm-grey-bg);  box-shadow: inset 0 0 0 0.5px var(--wm-grey-bdr); }
+.wm-maximize { background: var(--wm-grey-bg);  box-shadow: inset 0 0 0 0.5px var(--wm-grey-bdr); }
 
 .terminal-body {
+  padding: 16px 20px;
   font-size: 0.82rem;
   line-height: 2;
   color: var(--color-muted);
+  height: calc(100% - 38px);
+  overflow: hidden;
+  border-radius: 0 0 8px 8px;
+}
+.prompt  { color: var(--color-primary); }
+.output  { padding-left: 1rem; display: inline-block; }
+.cursor  {
+  display: inline-block; width: 9px; height: 1em;
+  background: white; vertical-align: middle; margin-left: 2px;
+  animation: blink 1s step-end infinite;
 }
+@keyframes blink { 50% { opacity: 0; } }
 
-.prompt {
-  color: var(--color-primary);
+.resize-handle { position: absolute; z-index: 10; }
+.resize-n  { top: calc(-1 * var(--handle-size)/2); left: var(--corner-size); right: var(--corner-size); height: var(--handle-size); cursor: n-resize; }
+.resize-s  { bottom: calc(-1 * var(--handle-size)/2); left: var(--corner-size); right: var(--corner-size); height: var(--handle-size); cursor: s-resize; }
+.resize-w  { left: calc(-1 * var(--handle-size)/2); top: var(--corner-size); bottom: var(--corner-size); width: var(--handle-size); cursor: w-resize; }
+.resize-e  { right: calc(-1 * var(--handle-size)/2); top: var(--corner-size); bottom: var(--corner-size); width: var(--handle-size); cursor: e-resize; }
+.resize-nw { top: calc(-1 * var(--corner-size)/2); left: calc(-1 * var(--corner-size)/2); width: var(--corner-size); height: var(--corner-size); cursor: nw-resize; }
+.resize-ne { top: calc(-1 * var(--corner-size)/2); right: calc(-1 * var(--corner-size)/2); width: var(--corner-size); height: var(--corner-size); cursor: ne-resize; }
+.resize-sw { bottom: calc(-1 * var(--corner-size)/2); left: calc(-1 * var(--corner-size)/2); width: var(--corner-size); height: var(--corner-size); cursor: sw-resize; }
+.resize-se { bottom: calc(-1 * var(--corner-size)/2); right: calc(-1 * var(--corner-size)/2); width: var(--corner-size); height: var(--corner-size); cursor: se-resize; }
+
+.taskbar {
+  position: fixed;
+  bottom: 0; left: 0; right: 0;
+  height: var(--tb-h);
+  background: var(--tb-bg);
+  border-top: 1px solid var(--tb-border);
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  padding: 0 8px;
+  z-index: 500;
+  backdrop-filter: blur(12px);
+  -webkit-backdrop-filter: blur(12px);
 }
 
-.output {
-  padding-left: 1rem;
-  display: inline-block;
+.tb-start {
+  width: 36px; height: 36px;
+  border-radius: 6px;
+  display: flex; align-items: center; justify-content: center;
+  transition: background 0.15s;
+  flex-shrink: 0;
 }
+.tb-start:hover   { background: rgba(255,255,255,0.08); }
+.tb-start.active  { background: rgba(64,116,207,0.25); }
 
-.cursor {
-  display: inline-block;
-  width: 9px;
-  height: 1em;
-  background: white;
-  vertical-align: middle;
-  margin-left: 2px;
-  animation: blink 1s step-end infinite;
+.tb-sep {
+  width: 1px; height: 22px;
+  background: rgba(255,255,255,0.1);
+  margin: 0 4px;
+  flex-shrink: 0;
 }
 
-@keyframes blink {
-  50% {
-    opacity: 0;
-  }
+.tb-windows {
+  display: flex;
+  align-items: center;
+  gap: 2px;
+  flex: 1;
+  overflow: hidden;
 }
 
-.buttons {
+.tb-window-item {
+  position: relative;
   display: flex;
-  justify-content: center;
-  gap: 1rem;
+  align-items: center;
+  gap: 6px;
+  padding: 4px 10px;
+  height: 34px;
+  border-radius: 5px;
+  font-size: 0.72rem;
+  color: var(--tb-text);
+  transition: background 0.12s;
+  max-width: 160px;
+  overflow: hidden;
 }
+.tb-window-item:hover { background: rgba(255,255,255,0.07); }
 
-.btn {
-  background: var(--color-primary);
-  color: #ffffff;
-  padding: 10px 24px;
-  font-family: 'Share Tech Mono', monospace;
-  font-size: 0.85rem;
-  text-decoration: none;
+.tb-win-icon {
+  width: 22px; height: 22px;
+  background: #111;
   border-radius: 4px;
-  display: inline-block;
-  transition: background 0.2s ease;
+  display: flex; align-items: center; justify-content: center;
+  flex-shrink: 0;
+  border: 1px solid rgba(255,255,255,0.1);
+}
+
+.tb-win-name {
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+}
+
+.tb-win-indicator {
+  position: absolute;
+  bottom: 3px; left: 50%;
+  transform: translateX(-50%);
+  width: 4px; height: 4px;
+  border-radius: 50%;
+  background: var(--color-primary);
+  opacity: 0;
+  transition: opacity 0.15s;
+}
+
+.tb-item-active .tb-win-indicator    { opacity: 1; }
+.tb-item-minimized .tb-window-item   { opacity: 0.55; }
+.tb-item-closed .tb-win-indicator    { opacity: 0; }
+
+.tb-item-active  .tb-window-item,
+.tb-window-item.tb-item-active { background: rgba(64,116,207,0.18); }
+
+.tb-right {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  margin-left: auto;
+  flex-shrink: 0;
+}
+
+.tb-clock {
+  display: flex;
+  flex-direction: column;
+  align-items: flex-end;
+  line-height: 1.2;
+  padding: 4px 8px;
+  border-radius: 5px;
+  cursor: default;
+  transition: background 0.12s;
+}
+.tb-clock:hover { background: rgba(255,255,255,0.06); }
+
+#tb-time { font-size: 0.82rem; color: #ddeeff; }
+#tb-date { font-size: 0.62rem; color: var(--color-muted); }
+
+.startmenu {
+  position: fixed;
+  bottom: var(--tb-h);
+  left: 8px;
+  width: calc(var(--sm-w-left) + var(--sm-w-right));
+  height: var(--sm-h);
+  background: var(--sm-bg);
+  border: 1px solid var(--sm-border);
+  border-radius: 10px 10px 0 0;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+  z-index: 600;
+  backdrop-filter: blur(20px);
+  -webkit-backdrop-filter: blur(20px);
+  box-shadow: 0 -4px 32px rgba(0,0,0,0.6);
+  opacity: 0;
+  transform: translateY(16px);
+  pointer-events: none;
+  transition: opacity 0.18s ease, transform 0.18s ease;
+}
+.startmenu.open {
+  opacity: 1;
+  transform: translateY(0);
+  pointer-events: all;
+}
+
+.startmenu-inner {
+  display: flex;
+  flex: 1;
+  overflow: hidden;
+}
+
+.sm-left {
+  width: var(--sm-w-left);
+  padding: 20px 16px 12px;
+  border-right: 1px solid rgba(255,255,255,0.06);
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+}
+
+.sm-section-label {
+  font-size: 0.62rem;
+  letter-spacing: 0.12em;
+  color: var(--color-muted);
+  text-transform: uppercase;
+  margin-bottom: 4px;
+}
+
+.sm-pinned {
+  display: flex;
+  flex-direction: column;
+  gap: 2px;
+}
+
+.sm-app {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  padding: 8px 10px;
+  border-radius: 6px;
+  font-size: 0.8rem;
+  color: var(--color-text);
+  transition: background 0.12s;
+  text-align: left;
+  width: 100%;
+}
+.sm-app:hover { background: rgba(255,255,255,0.07); }
+
+.sm-app-icon {
+  width: 36px; height: 36px;
+  border-radius: 8px;
+  display: flex; align-items: center; justify-content: center;
+  flex-shrink: 0;
+}
+
+.sm-icon-img  { background: transparent; border: none; padding: 0; overflow: hidden; border-radius: 8px; }
+.sm-icon-img img { width: 36px; height: 36px; display: block; object-fit: cover; border-radius: 8px; }
+.sm-icon-term  { background: #111; border: 1px solid rgba(255,255,255,0.1); }
+
+.sm-right {
+  flex: 1;
+  padding: 20px 16px 12px;
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+  background: rgba(0,0,0,0.15);
+}
+
+.sm-info {
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+}
+
+.sm-info-row {
+  display: flex;
+  justify-content: space-between;
+  font-size: 0.72rem;
+  gap: 8px;
+}
+.sm-info-key { color: var(--color-muted); }
+.sm-info-val { color: var(--color-text);  text-align: right; }
+
+.sm-divider {
+  height: 1px;
+  background: rgba(255,255,255,0.07);
+  margin: 4px 0;
+}
+
+.sm-link {
+  display: block;
+  font-size: 0.72rem;
+  color: var(--color-primary);
+  padding: 4px 0;
+  transition: color 0.12s;
+}
+.sm-link:hover { color: var(--color-primary-hover); }
+
+/* ── Footer bar ──────────────────────────────────────── */
+.sm-footer {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  height: 48px;
+  padding: 0 16px;
+  background: rgba(0,0,0,0.25);
+  border-top: 1px solid rgba(255,255,255,0.06);
+  flex-shrink: 0;
+}
+
+.sm-user {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  font-size: 0.78rem;
+  color: var(--color-text);
+}
+
+.sm-avatar {
+  width: 28px; height: 28px;
+  border-radius: 50%;
+  background: var(--color-primary);
+  display: flex; align-items: center; justify-content: center;
+  font-size: 0.65rem;
+  font-weight: 700;
+  letter-spacing: 0.02em;
+  color: #fff;
 }
 
-.btn:hover {
-  background: var(--color-primary-hover);
+.sm-power {
+  width: 28px; height: 28px;
+  border-radius: 5px;
+  display: flex; align-items: center; justify-content: center;
+  color: var(--tb-text);
+  transition: background 0.12s;
 }
+.sm-power:hover { background: rgba(240,84,84,0.2); }

BIN
assets/images/ctftime.png


BIN
assets/images/tuxforge.png


+ 218 - 0
assets/js/script.js

@@ -0,0 +1,218 @@
+    const terminal     = document.getElementById('terminal');
+    const titlebar     = document.getElementById('titlebar');
+    const startBtn     = document.getElementById('tb-start');
+    const startMenu    = document.getElementById('startmenu');
+    const tbWinItem    = document.getElementById('tb-terminal-item');
+    const smTermLaunch = document.getElementById('sm-terminal-launch');
+
+    const MIN_W = 320, MIN_H = 160;
+    let winState   = 'normal'; 
+    let savedRect  = null;
+    let menuOpen   = false;
+
+    function saveRect() {
+      savedRect = {
+        left:   terminal.offsetLeft,
+        top:    terminal.offsetTop,
+        width:  terminal.offsetWidth,
+        height: terminal.offsetHeight,
+      };
+    }
+
+    function setWinState(s) {
+      winState = s;
+      updateTaskbarItem();
+    }
+
+    function updateTaskbarItem() {
+      const indicator = tbWinItem.querySelector('.tb-win-indicator');
+      const closed = winState === 'closed';
+      tbWinItem.classList.toggle('tb-item-active',    winState === 'normal' || winState === 'maximized');
+      tbWinItem.classList.toggle('tb-item-minimized', winState === 'minimized');
+      tbWinItem.classList.toggle('tb-item-closed',    closed);
+      indicator.style.opacity = closed ? '0' : '1';
+    }
+
+    function restoreWindow() {
+      terminal.style.display = '';
+      terminal.classList.remove('maximized');
+      if (savedRect) {
+        terminal.style.left   = savedRect.left   + 'px';
+        terminal.style.top    = savedRect.top    + 'px';
+        terminal.style.width  = savedRect.width  + 'px';
+        terminal.style.height = savedRect.height + 'px';
+      } else {
+        centerWindow();
+      }
+      setWinState('normal');
+    }
+
+    function centerWindow() {
+      terminal.style.left   = Math.round(window.innerWidth  / 2 - terminal.offsetWidth  / 2) + 'px';
+      terminal.style.top    = Math.round((window.innerHeight - 44) / 2 - terminal.offsetHeight / 2) + 'px';
+    }
+
+    centerWindow();
+    updateTaskbarItem();
+
+    function toggleMenu(e) {
+      e.stopPropagation();
+      menuOpen = !menuOpen;
+      startMenu.classList.toggle('open', menuOpen);
+      startMenu.setAttribute('aria-hidden', String(!menuOpen));
+      startBtn.classList.toggle('active', menuOpen);
+    }
+
+    startBtn.addEventListener('click', toggleMenu);
+
+    document.addEventListener('click', (e) => {
+      if (menuOpen && !startMenu.contains(e.target) && e.target !== startBtn) {
+        menuOpen = false;
+        startMenu.classList.remove('open');
+        startMenu.setAttribute('aria-hidden', 'true');
+        startBtn.classList.remove('active');
+      }
+    });
+
+    startMenu.querySelectorAll('a').forEach(a => {
+      a.addEventListener('click', () => {
+        menuOpen = false;
+        startMenu.classList.remove('open');
+        startBtn.classList.remove('active');
+      });
+    });
+
+    smTermLaunch.addEventListener('click', () => {
+      menuOpen = false;
+      startMenu.classList.remove('open');
+      startBtn.classList.remove('active');
+      if (winState === 'closed' || winState === 'minimized') {
+        restoreWindow();
+      } else if (winState === 'normal' || winState === 'maximized') {
+        terminal.classList.add('flash');
+        setTimeout(() => terminal.classList.remove('flash'), 300);
+      }
+    });
+
+    tbWinItem.addEventListener('click', () => {
+      if (winState === 'closed') {
+        restoreWindow();
+      } else if (winState === 'minimized') {
+        restoreWindow();
+      } else if (winState === 'normal' || winState === 'maximized') {
+        saveRect();
+        terminal.style.display = 'none';
+        setWinState('minimized');
+      }
+    });
+
+    document.getElementById('btn-close').addEventListener('click', () => {
+      saveRect();
+      terminal.style.display = 'none';
+      setWinState('closed');
+    });
+
+    document.getElementById('btn-minimize').addEventListener('click', () => {
+      if (winState === 'minimized') return;
+      if (winState !== 'maximized') saveRect();
+      terminal.style.display = 'none';
+      setWinState('minimized');
+    });
+
+    document.getElementById('btn-maximize').addEventListener('click', toggleMaximize);
+    titlebar.addEventListener('dblclick', (e) => {
+      if (e.target.closest('.wm-btn')) return;
+      toggleMaximize();
+    });
+
+    function toggleMaximize() {
+      if (winState === 'maximized') {
+        winState = 'normal';
+        terminal.classList.remove('maximized');
+        if (savedRect) {
+          terminal.style.left   = savedRect.left   + 'px';
+          terminal.style.top    = savedRect.top    + 'px';
+          terminal.style.width  = savedRect.width  + 'px';
+          terminal.style.height = savedRect.height + 'px';
+        }
+        setWinState('normal');
+      } else {
+        saveRect();
+        terminal.classList.add('maximized');
+        terminal.style.left   = '0';
+        terminal.style.top    = '0';
+        terminal.style.width  = '100vw';
+        terminal.style.height = 'calc(100vh - 44px)';
+        setWinState('maximized');
+      }
+    }
+
+    let dragging = false, dragSX, dragSY, dragL, dragT;
+
+    titlebar.addEventListener('mousedown', (e) => {
+      if (e.target.closest('.wm-btn') || winState === 'maximized') return;
+      dragging = true;
+      dragSX = e.clientX; dragSY = e.clientY;
+      dragL  = terminal.offsetLeft; dragT = terminal.offsetTop;
+      titlebar.style.cursor = 'grabbing';
+      e.preventDefault();
+    });
+
+    document.addEventListener('mousemove', (e) => {
+      if (dragging) {
+        terminal.style.left = (dragL + e.clientX - dragSX) + 'px';
+        terminal.style.top  = (dragT + e.clientY - dragSY) + 'px';
+      }
+      if (resizing) doResize(e.clientX, e.clientY);
+    });
+
+    document.addEventListener('mouseup', () => {
+      dragging = false;
+      titlebar.style.cursor = 'grab';
+      stopResize();
+    });
+
+    let resizing = false, resDir = '', resStart = {};
+
+    document.querySelectorAll('.resize-handle').forEach(h => {
+      h.addEventListener('mousedown', (e) => {
+        if (winState === 'maximized') return;
+        resizing = true;
+        resDir   = h.dataset.dir;
+        resStart = { mx: e.clientX, my: e.clientY,
+                     left: terminal.offsetLeft, top: terminal.offsetTop,
+                     width: terminal.offsetWidth, height: terminal.offsetHeight };
+        document.body.style.cursor = getComputedStyle(h).cursor;
+        e.preventDefault();
+      });
+    });
+
+    function doResize(mx, my) {
+      const dx = mx - resStart.mx, dy = my - resStart.my;
+      let { left, top, width, height } = resStart;
+      if (resDir.includes('e')) width  = Math.max(MIN_W, width  + dx);
+      if (resDir.includes('s')) height = Math.max(MIN_H, height + dy);
+      if (resDir.includes('w')) { const nw = Math.max(MIN_W, width-dx);  left  = left + width  - nw;  width  = nw; }
+      if (resDir.includes('n')) { const nh = Math.max(MIN_H, height-dy); top   = top  + height - nh;  height = nh; }
+      terminal.style.left   = left   + 'px';
+      terminal.style.top    = top    + 'px';
+      terminal.style.width  = width  + 'px';
+      terminal.style.height = height + 'px';
+    }
+
+    function stopResize() {
+      if (resizing) { resizing = false; document.body.style.cursor = ''; }
+    }
+
+    function updateClock() {
+      const now = new Date();
+      const hh = String(now.getHours()).padStart(2,'0');
+      const mm = String(now.getMinutes()).padStart(2,'0');
+      const days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
+      const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
+      document.getElementById('tb-time').textContent = `${hh}:${mm}`;
+      document.getElementById('tb-date').textContent =
+        `${days[now.getDay()]} ${String(now.getDate()).padStart(2,'0')} ${months[now.getMonth()]}`;
+    }
+    updateClock();
+    setInterval(updateClock, 10000);

+ 124 - 21
index.html

@@ -9,36 +9,139 @@
 </head>
 
 <body>
-  <main class="container">
 
-    <p class="subtitle">CTF TEAM</p>
-
-    <h1 class="title">Yixboost CTF Team</h1>
+  <section class="terminal" id="terminal">
+    <div class="terminal-titlebar" id="titlebar">
+      <span class="titlebar-label">~ terminal</span>
+      <div class="titlebar-controls">
+        <button class="wm-btn wm-minimize" id="btn-minimize" aria-label="Minimize">
+          <svg viewBox="0 0 16 16" width="10" height="10"><line x1="3" y1="8" x2="13" y2="8" stroke="rgba(0,0,0,0.55)" stroke-width="1.6" stroke-linecap="round"/></svg>
+        </button>
+        <button class="wm-btn wm-maximize" id="btn-maximize" aria-label="Maximize">
+          <svg viewBox="0 0 16 16" width="10" height="10"><rect x="3.5" y="3.5" width="9" height="9" rx="1.5" fill="none" stroke="rgba(0,0,0,0.55)" stroke-width="1.5"/></svg>
+        </button>
+        <button class="wm-btn wm-close" id="btn-close" aria-label="Close">
+          <svg viewBox="0 0 16 16" width="10" height="10">
+            <line x1="3" y1="3" x2="13" y2="13" stroke="rgba(0,0,0,0.55)" stroke-width="1.6" stroke-linecap="round"/>
+            <line x1="13" y1="3" x2="3" y2="13" stroke="rgba(0,0,0,0.55)" stroke-width="1.6" stroke-linecap="round"/>
+          </svg>
+        </button>
+      </div>
+    </div>
+    <div class="terminal-body">
+      <span class="prompt">$</span> whoami<br/>
+      <span class="output">yixboost — ctf team</span><br/>
+      <span class="prompt">$</span> cat members.txt<br/>
+      <span class="output">a bunch of teenagers</span><br/>
+      <span class="prompt">$</span> cat goal.txt<br/>
+      <span class="output">have fun, learn stuff, maybe get a flag</span><br/>
+      <span class="prompt">$</span> <span class="cursor"></span>
+    </div>
+    <div class="resize-handle resize-n"  data-dir="n"></div>
+    <div class="resize-handle resize-s"  data-dir="s"></div>
+    <div class="resize-handle resize-w"  data-dir="w"></div>
+    <div class="resize-handle resize-e"  data-dir="e"></div>
+    <div class="resize-handle resize-nw" data-dir="nw"></div>
+    <div class="resize-handle resize-ne" data-dir="ne"></div>
+    <div class="resize-handle resize-sw" data-dir="sw"></div>
+    <div class="resize-handle resize-se" data-dir="se"></div>
+  </section>
 
-    <section class="terminal">
-      <div class="terminal-header">~ terminal</div>
+  <div class="startmenu" id="startmenu" aria-hidden="true">
+    <div class="startmenu-inner">
 
-      <div class="terminal-body">
-        <span class="prompt">$</span> whoami<br/>
-        <span class="output">yixboost — ctf team</span><br/>
+      <div class="sm-left">
+        <p class="sm-section-label">Pinned</p>
+        <div class="sm-pinned">
+          <a class="sm-app" href="https://link.yixboost.dev/ctftime" target="_blank" id="sm-ctftime">
+            <div class="sm-app-icon sm-icon-img">
+              <img src="assets/images/ctftime.png" alt="CTFtime" width="36" height="36"/>
+            </div>
+            <span>CTFtime</span>
+          </a>
+          <a class="sm-app" href="https://git.tuxworld.nl/yixboost-ctf-team" target="_blank" id="sm-tuxforge">
+            <div class="sm-app-icon sm-icon-img">
+              <img src="assets/images/tuxforge.png" alt="TuxForge" width="36" height="36"/>
+            </div>
+            <span>TuxForge</span>
+          </a>
+          <button class="sm-app" id="sm-terminal-launch">
+            <div class="sm-app-icon sm-icon-term">
+              <svg viewBox="0 0 24 24" width="22" height="22" fill="none" xmlns="http://www.w3.org/2000/svg">
+                <rect x="2" y="3" width="20" height="16" rx="2" stroke="white" stroke-width="1.6"/>
+                <path d="M6 8l4 3.5L6 15" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
+                <line x1="13" y1="15" x2="18" y2="15" stroke="white" stroke-width="1.6" stroke-linecap="round"/>
+              </svg>
+            </div>
+            <span>Terminal</span>
+          </button>
+        </div>
+      </div>
 
-        <span class="prompt">$</span> cat members.txt<br/>
-        <span class="output">a bunch of 15–17 year olds</span><br/>
+      <div class="sm-right">
+        <p class="sm-section-label">About</p>
+        <div class="sm-info">
+          <div class="sm-info-row">
+            <span class="sm-info-key">team</span>
+            <span class="sm-info-val">yixboost</span>
+          </div>
+          <div class="sm-info-row">
+            <span class="sm-info-key">members</span>
+            <span class="sm-info-val">cool teenagers</span>
+          </div>
+          <div class="sm-info-row">
+            <span class="sm-info-key">goal</span>
+            <span class="sm-info-val">get the flag</span>
+          </div>
+        </div>
+        <div class="sm-divider"></div>
+        <p class="sm-section-label">Links</p>
+        <a class="sm-link" href="https://ctftime.org"   target="_blank">→ ctftime.org</a>
+        <a class="sm-link" href="https://git.tuxworld.nl" target="_blank">→ git.tuxworld.nl</a>
+      </div>
+    </div>
 
-        <span class="prompt">$</span> cat goal.txt<br/>
-        <span class="output">have fun, learn stuff, maybe get a flag</span><br/>
+    <div class="sm-footer">
+      <div class="sm-user">
+        <div class="sm-avatar">yx</div>
+        <span>yixboost</span>
+      </div>
+      <button class="sm-power" title="Power off" aria-label="Power">
+        <svg viewBox="0 0 24 24" width="16" height="16" fill="none">
+          <path d="M12 3v9" stroke="white" stroke-width="2" stroke-linecap="round"/>
+          <path d="M7 6.3A8 8 0 1 0 17 6.3" stroke="white" stroke-width="2" stroke-linecap="round"/>
+        </svg>
+      </button>
+    </div>
+  </div>
 
-        <span class="prompt">$</span> <span class="cursor"></span>
+  <nav class="taskbar" id="taskbar">
+    <button class="tb-start" id="tb-start" aria-label="Start menu" title="Start">
+      <img src="assets/images/logo.png" alt="Start" width="26" height="26" style="border-radius:4px;display:block;"/>
+    </button>
+    <div class="tb-windows" id="tb-windows">
+      <button class="tb-window-item" id="tb-terminal-item" title="~ terminal">
+        <div class="tb-win-icon">
+          <svg viewBox="0 0 24 24" width="14" height="14" fill="none">
+            <rect x="2" y="3" width="20" height="16" rx="2" stroke="white" stroke-width="1.8"/>
+            <path d="M6 8l4 3.5L6 15" stroke="white" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
+            <line x1="13" y1="15" x2="18" y2="15" stroke="white" stroke-width="1.8" stroke-linecap="round"/>
+          </svg>
+        </div>
+        <span class="tb-win-name">terminal</span>
+        <div class="tb-win-indicator"></div>
+      </button>
+    </div>
+    <div class="tb-right">
+      <div class="tb-clock" id="tb-clock">
+        <span id="tb-time">00:00</span>
+        <span id="tb-date">Mon 01 Jan</span>
       </div>
-    </section>
+    </div>
 
-    <div class="divider"></div>
+  </nav>
 
-    <div class="buttons">
-      <a href="https://ctftime.org" target="_blank" class="btn">CTFtime</a>
-      <a href="https://git.tuxworld.nl" target="_blank" class="btn">TuxForge</a>
-    </div>
+  <script src="assets/js/script.js"></script>
 
-  </main>
 </body>
 </html>