commit be2d1bfd78e96da0f5e2d9295638023bfaa467d4 Author: zenn Date: Mon Jan 12 00:48:20 2026 +0900 [260112] Initial Build diff --git a/font/NovaBySoristudio-Regular.otf b/font/NovaBySoristudio-Regular.otf new file mode 100644 index 0000000..8142de0 Binary files /dev/null and b/font/NovaBySoristudio-Regular.otf differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..610804d --- /dev/null +++ b/index.html @@ -0,0 +1,105 @@ + + + + + + + SORI STUDIO + + + +
+

sori.studio

+
+ +
+ +

Core Infrastructure

+ +
+ +
+ +

Public Services

+ +
+ +
+ +

Personal Archive

+ + +
+ + + + diff --git a/script.js b/script.js new file mode 100644 index 0000000..894f44f --- /dev/null +++ b/script.js @@ -0,0 +1,68 @@ +// ping +async function checkStatus() { + const cards = document.querySelectorAll(".card"); + + cards.forEach(async (card) => { + const url = card.getAttribute("data-url"); + const dot = card.querySelector(".bolt.tr"); + + try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 5000); + + await fetch(url, { + mode: "no-cors", + cache: "no-cache", + signal: controller.signal, + }); + + dot.classList.remove("status-offline"); + dot.classList.add("status-online"); + clearTimeout(timeoutId); + } catch (e) { + dot.classList.remove("status-online"); + dot.classList.add("status-offline"); + } + }); +} + +checkStatus(); +setInterval(checkStatus, 300000); + +// bolt animation +function startBoltAction() { + const allBolts = document.querySelectorAll(".bolt, .header-bolt"); + if (allBolts.length === 0) return; + + const availableBolts = Array.from(allBolts).filter( + (bolt) => !bolt.classList.contains("bolt-acting") + ); + + if (availableBolts.length > 0) { + const randomBolt = + availableBolts[Math.floor(Math.random() * availableBolts.length)]; + + const baseRot = + randomBolt.getAttribute("data-rotate") || + randomBolt.style.getPropertyValue("--r") || + "0"; + + randomBolt.style.setProperty( + "--r", + baseRot.toString().replace("deg", "") + "deg" + ); + + randomBolt.classList.add("bolt-acting"); + + setTimeout(() => { + randomBolt.classList.remove("bolt-acting"); + }, 8100); + } + + const nextInterval = Math.random() * 4000 + 3000; + setTimeout(startBoltAction, nextInterval); +} + +window.addEventListener("load", () => { + setTimeout(startBoltAction, 2000); +}); diff --git a/style.css b/style.css new file mode 100644 index 0000000..ad8a484 --- /dev/null +++ b/style.css @@ -0,0 +1,406 @@ +@import url("https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.css"); + +@import url("https://fonts.googleapis.com/css2?family=Anton&family=Orbitron:wght@800&family=Staatliches&family=Tenor+Sans&display=swap"); + +@font-face { + font-family: "Nova"; + src: url("./font/NovaBySoristudio-Regular.otf") format("opentype"); + font-weight: normal; + font-style: normal; +} + +body { + margin: 0; + padding: 0; + background-color: #050505; + background: radial-gradient(circle at 50% 50%, #1a1a1a 0%, #050505 100%); + color: #e0e0e0; + font-family: "Pretendard", system-ui, sans-serif; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + word-break: keep-all; +} + +.nova { + font-family: "nova"; +} + +header { + padding: 80px 0 50px; + text-align: center; +} + +header h1 { + font-family: "Tenor Sans", sans-serif; + font-size: 3.5rem; + font-weight: 900; + letter-spacing: 4px; + text-transform: lowercase; + margin: 0; + + background: linear-gradient(to bottom, #ffffff 30%, #a1a1a1 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + + filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5)); +} + +header h1 .dot-point { + background: none; + -webkit-background-clip: initial; + -webkit-text-fill-color: initial; + + color: #00ff88; + display: inline-block; + + text-shadow: 0 0 8px rgba(0, 255, 136, 0.8); + + animation: pulse-dot 2s infinite; + margin: 0 2px; +} +@keyframes pulse-dot { + 0%, + 100% { + opacity: 0.5; + text-shadow: 0 0 8px rgba(0, 255, 136, 0.8); + } + 50% { + opacity: 1; + text-shadow: 0 0 2px rgba(0, 255, 136, 0.2); + } +} + +/* [1] 공통 섹션 레이아웃 */ +.category-section { + width: 95%; + max-width: 1440px; + margin: 0 auto 60px; /* 중앙 정렬 유지 */ +} + +/* [2] 섹션 타이틀 (반응형 대응) */ +.section-title { + font-family: 'Tenor Sans', sans-serif; + font-size: 1.25rem; + color: #666; + text-transform: uppercase; + letter-spacing: 3px; + padding-left: 15px; + margin-bottom: 25px; + border-left: 3px solid #00ff88; /* 나사 불빛과 맞춘 포인트 컬러 추천 */ + display: flex; + align-items: center; +} + +.section-title::after { + content: ""; + flex: 1; + height: 1px; + background: linear-gradient(to right, #333, transparent); + margin-left: 20px; +} + +/* [3] 그리드 컨테이너 (부모 폭에 맞춤) */ +.container { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1.5rem; + /* padding 제거 (부모인 category-section에서 폭 제어) */ + width: 100%; +} + +/* --- 반응형 구간 설정 --- */ + +/* 1. 태블릿 (1200px 이하) */ +@media (max-width: 1200px) { + .category-section { + max-width: 900px; /* 2열 카드 폭에 맞춤 */ + } + .container { + grid-template-columns: repeat(2, 1fr); + } +} + +/* 2. 모바일 (768px 이하) */ +@media (max-width: 768px) { + .category-section { + max-width: 500px; /* 1열 카드 폭에 맞춤 */ + margin-bottom: 40px; + } + .container { + grid-template-columns: 1fr; + gap: 1.25rem; + } + .section-title { + font-size: 1rem; /* 모바일에서 타이틀 살짝 축소 */ + letter-spacing: 2px; + } + header h1 { + font-size: 2.5rem; + } +} + +.card { + position: relative; + display: flex; + flex-direction: column; + gap: 0.5rem; + + background: linear-gradient(145deg, #242424 0%, #111111 100%); + backdrop-filter: blur(40px); + -webkit-backdrop-filter: blur(40px); + + border: 1px solid #222222; + + box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.05); + + border-radius: 24px; + padding: 30px; + text-decoration: none; + color: inherit; + transition: all 0.4s ease; +} + +.card:hover { + transform: translateY(-8px); + background: linear-gradient(145deg, #1a1a1a 0%, #0d0d0d 100%); + border-color: #333333; +} + +.bolt { + position: absolute; + width: 1rem; + height: 1rem; + background: linear-gradient(135deg, #777 0%, #333 100%); + border-radius: 50%; + border: 1px solid #111; +} + +.tl { + top: 1rem; + left: 1rem; +} +.tr { + top: 1rem; + right: 1rem; +} +.bl { + bottom: 1rem; + left: 1rem; +} +.br { + bottom: 1rem; + right: 1rem; +} + +.bolt::before { + content: ""; + position: absolute; + top: 50%; + left: 50%; + width: 64%; + height: 1.5px; + background: #222; + border-radius: 4px; + transform: translate(-50%, -50%) rotate(var(--r, 0deg)); + transition: background 0.3s; +} + +.bolt.bolt-acting::before { + transform: translate(-50%, -50%) rotate(calc(var(--r, 0deg) + 45deg)); + background: #000; +} + +.bolt[data-rotate="45"]::before { + transform: translate(-50%, -50%) rotate(45deg); +} +.bolt[data-rotate="160"]::before { + transform: translate(-50%, -50%) rotate(160deg); +} +.bolt[data-rotate="-20"]::before { + transform: translate(-50%, -50%) rotate(-20deg); +} +.bolt[data-rotate="100"]::before { + transform: translate(-50%, -50%) rotate(100deg); +} + +.card-inner { + display: flex; + width: 100%; + gap: 1rem; + align-items: center; +} + +.card-icon { + flex-shrink: 0; + width: auto; + height: 60px; + border-radius: 15px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + overflow: hidden; + font-family: "Nova"; + + font-size: 36px; + line-height: 1; +} + +.icon-nova { + font-family: "Nova"; + font-size: 3rem; + line-height: 1; + -webkit-text-stroke: 1px rgba(255, 255, 255, 0.5); +} + +.icon-ruby { + font-family: "pretendard", sans-serif; + font-size: 12px; + line-height: 1; + color: #eaeaea; + font-weight: 700; +} + +.card-content { + flex-grow: 1; + display: flex; + flex-direction: column; + justify-content: center; + min-width: 0; +} + +.title-wrapper { + display: flex; + justify-content: center; + align-items: center; + gap: 10px; + width: 100%; +} + +.url-display { + margin: 1.25rem 0 0.25rem; + font-size: 1.125rem; + font-weight: 700; + color: #acacac; + + text-transform: lowercase; + letter-spacing: -0.3px; + text-align: center; + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 1; + font-family: "Pretendard", sans-serif; +} + +.desc { + margin: 0.25rem 0 0; + color: #888888; + font-size: 0.85rem; + font-weight: 400; + color: #666666; + text-align: center; +} + +.status-dot { + position: absolute; + top: 1rem; + right: 1rem; + flex-shrink: 0; + width: 1rem; + height: 1rem; + border-radius: 50%; + transition: background-color 0.5s ease; +} + +.status-online { + background-color: #00ff88; + box-shadow: 0 0 10px #00ff88; + animation: pulse 2s infinite; +} + +.status-offline { + background-color: #ff4444; + box-shadow: 0 0 10px #ff4444; + animation: none; +} + +@keyframes pulse { + 0% { + transform: scale(0.95); + box-shadow: 0 0 0 0 rgba(0, 255, 136, 0.7); + } + 70% { + transform: scale(1); + box-shadow: 0 0 0 10px rgba(0, 255, 136, 0); + } + 100% { + transform: scale(0.95); + box-shadow: 0 0 0 0 rgba(0, 255, 136, 0); + } +} + +@media (max-width: 768px) { + .container { + grid-template-columns: 1fr; + } +} + +@keyframes bolt-full-cycle { + 0% { + transform: translate(-50%, -50%) rotate(var(--r, 0deg)); + } + 15% { + transform: translate(-50%, -50%) rotate(calc(var(--r, 0deg) + 1080deg)); + } + 85% { + transform: translate(-50%, -50%) rotate(calc(var(--r, 0deg) + 1080deg)); + } + 100% { + transform: translate(-50%, -50%) rotate(var(--r, 0deg)); + } +} + +.bolt-acting::before { + animation: bolt-full-cycle 16s cubic-bezier(0.65, 0, 0.35, 1) forwards; +} + + +/* 1. 전체 스크롤바 영역 (폭 설정) */ +::-webkit-scrollbar { + width: 8px; /* 스크롤바 두께를 얇게 조절 */ + height: 8px; /* 가로 스크롤바 대비 */ +} + +/* 2. 스크롤바 배경 (트랙) */ +::-webkit-scrollbar-track { + background: #050505; /* 바디 배경색과 맞춰서 일체감 부여 */ +} + +/* 3. 스크롤 바 (움직이는 부분) */ +::-webkit-scrollbar-thumb { + background: linear-gradient(to bottom, #333, #222); /* 나사 머리 같은 금속 느낌 */ + border-radius: 10px; + border: 2px solid #050505; /* 트랙과 분리되어 보이는 효과 (공간감) */ +} + +/* 4. 마우스 호버 시 (사용자가 잡았을 때) */ +::-webkit-scrollbar-thumb:hover { + background: #444; /* 살짝 밝아지며 반응 */ + border-color: #050505; +} + +/* 5. (선택사항) 스크롤바가 작동할 때 나사 불빛과 맞춘 포인트 */ +::-webkit-scrollbar-thumb:active { + background: #00ff88; /* 나사 LED 컬러로 포인트 */ + box-shadow: 0 0 10px #00ff88; +} + +/* Firefox용 설정 */ +* { + scrollbar-width: thin; + scrollbar-color: #333 #050505; +} \ No newline at end of file