diff --git a/index.html b/index.html index 610804d..b4dafbe 100644 --- a/index.html +++ b/index.html @@ -1,105 +1,151 @@ - - - - - SORI STUDIO - - - -
-

sori.studio

-
-
- -

Core Infrastructure

+ + + + + SORI STUDIO + + + + +
+

sori.studio

+
+ +
+ +

Core Infrastructure

-
- -

Public Services

+ +
+ +
+ +

Public Services

-
- -

Personal Archive

+ +
+ +
+ +

Personal Archive

+
- - - + + + + \ No newline at end of file diff --git a/script.js b/script.js index 894f44f..8e61ea8 100644 --- a/script.js +++ b/script.js @@ -4,7 +4,7 @@ async function checkStatus() { cards.forEach(async (card) => { const url = card.getAttribute("data-url"); - const dot = card.querySelector(".bolt.tr"); + const dot = card.querySelector(".card__decor.card__decor--tr"); try { const controller = new AbortController(); @@ -29,13 +29,30 @@ async function checkStatus() { checkStatus(); setInterval(checkStatus, 300000); +// 🔴 테스트 ON +// const TEST_ONLY_SECOND_CARD = true; +// 🟢 테스트 OFF +const TEST_ONLY_SECOND_CARD = false; + // bolt animation function startBoltAction() { - const allBolts = document.querySelectorAll(".bolt, .header-bolt"); + let allBolts = []; + + if (TEST_ONLY_SECOND_CARD) { + // 🔧 2번째 카드만 선택 (0-based index) + const secondCard = document.querySelectorAll(".card")[1]; + if (!secondCard) return; + + allBolts = secondCard.querySelectorAll(".card__decor"); + } else { + // 🔹 전체 카드 대상 + allBolts = document.querySelectorAll(".card__decor"); + } + if (allBolts.length === 0) return; const availableBolts = Array.from(allBolts).filter( - (bolt) => !bolt.classList.contains("bolt-acting") + (bolt) => !bolt.classList.contains("card__decor-acting") ); if (availableBolts.length > 0) { @@ -43,20 +60,17 @@ function startBoltAction() { availableBolts[Math.floor(Math.random() * availableBolts.length)]; const baseRot = - randomBolt.getAttribute("data-rotate") || - randomBolt.style.getPropertyValue("--r") || - "0"; + randomBolt.style.getPropertyValue("--r") || "0deg"; - randomBolt.style.setProperty( - "--r", - baseRot.toString().replace("deg", "") + "deg" - ); + randomBolt.style.setProperty("--r", baseRot); - randomBolt.classList.add("bolt-acting"); + const BOLT_ANIMATION_DURATION = 16000; // CSS와 반드시 일치 + + randomBolt.classList.add("card__decor-acting"); setTimeout(() => { - randomBolt.classList.remove("bolt-acting"); - }, 8100); + randomBolt.classList.remove("card__decor-acting"); + }, BOLT_ANIMATION_DURATION); } const nextInterval = Math.random() * 4000 + 3000; @@ -66,3 +80,30 @@ function startBoltAction() { window.addEventListener("load", () => { setTimeout(startBoltAction, 2000); }); + +// 나사 랜덤 각도 부연 +document.querySelectorAll('.card').forEach((card, cardIndex) => { + card.querySelectorAll('.card__decor').forEach((decor, decorIndex) => { + + // -30 ~ 150 정도가 나사 느낌 제일 안정적 + const angle = Math.floor(Math.random() * 180) - 30; + + decor.style.setProperty('--r', `${angle}deg`); + }); +}); + +window.addEventListener('keydown', (e) => { + const isMac = navigator.platform.toUpperCase().includes('MAC'); + + if ( + isMac && + e.metaKey && + e.shiftKey && + e.code === 'KeyD' && + !['INPUT', 'TEXTAREA'].includes(document.activeElement?.tagName) + ) { + e.preventDefault(); + document.body.classList.toggle('is-nova'); + } +}); + diff --git a/style.css b/style.css index ad8a484..8d42765 100644 --- a/style.css +++ b/style.css @@ -9,6 +9,13 @@ font-style: normal; } +*, +*::before, +*::after { + /* font-family: "Nova" !important; */ +} + + body { margin: 0; padding: 0; @@ -33,7 +40,8 @@ header { } header h1 { - font-family: "Tenor Sans", sans-serif; + /* font-family: "Tenor Sans", sans-serif; */ + font-family: "Nova", "Tenor Sans", sans-serif; font-size: 3.5rem; font-weight: 900; letter-spacing: 4px; @@ -60,12 +68,15 @@ header h1 .dot-point { 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); @@ -76,7 +87,8 @@ header h1 .dot-point { .category-section { width: 95%; max-width: 1440px; - margin: 0 auto 60px; /* 중앙 정렬 유지 */ + margin: 0 auto 60px; + /* 중앙 정렬 유지 */ } /* [2] 섹션 타이틀 (반응형 대응) */ @@ -84,11 +96,13 @@ header h1 .dot-point { font-family: 'Tenor Sans', sans-serif; font-size: 1.25rem; color: #666; + margin: 1rem 0 1.5rem; text-transform: uppercase; letter-spacing: 3px; - padding-left: 15px; + padding-left: 1rem; margin-bottom: 25px; - border-left: 3px solid #00ff88; /* 나사 불빛과 맞춘 포인트 컬러 추천 */ + border-left: 3px solid #00ff88; + /* 나사 불빛과 맞춘 포인트 컬러 추천 */ display: flex; align-items: center; } @@ -107,7 +121,7 @@ header h1 .dot-point { grid-template-columns: repeat(3, 1fr); gap: 1.5rem; /* padding 제거 (부모인 category-section에서 폭 제어) */ - width: 100%; + width: 100%; } /* --- 반응형 구간 설정 --- */ @@ -115,8 +129,10 @@ header h1 .dot-point { /* 1. 태블릿 (1200px 이하) */ @media (max-width: 1200px) { .category-section { - max-width: 900px; /* 2열 카드 폭에 맞춤 */ + max-width: 900px; + /* 2열 카드 폭에 맞춤 */ } + .container { grid-template-columns: repeat(2, 1fr); } @@ -125,17 +141,22 @@ header h1 .dot-point { /* 2. 모바일 (768px 이하) */ @media (max-width: 768px) { .category-section { - max-width: 500px; /* 1열 카드 폭에 맞춤 */ + max-width: 500px; + /* 1열 카드 폭에 맞춤 */ margin-bottom: 40px; } + .container { grid-template-columns: 1fr; gap: 1.25rem; } + .section-title { - font-size: 1rem; /* 모바일에서 타이틀 살짝 축소 */ + font-size: 1rem; + /* 모바일에서 타이틀 살짝 축소 */ letter-spacing: 2px; } + header h1 { font-size: 2.5rem; } @@ -168,7 +189,7 @@ header h1 .dot-point { border-color: #333333; } -.bolt { +.card__decor { position: absolute; width: 1rem; height: 1rem; @@ -177,51 +198,62 @@ header h1 .dot-point { border: 1px solid #111; } -.tl { +.card__decor--tl { top: 1rem; left: 1rem; } -.tr { + +.card__decor--tr { top: 1rem; right: 1rem; } -.bl { + +.card__decor--bl { bottom: 1rem; left: 1rem; } -.br { + +.card__decor--br { bottom: 1rem; right: 1rem; } -.bolt::before { +.card__decor::before { content: ""; position: absolute; top: 50%; left: 50%; + width: 64%; height: 1.5px; - background: #222; border-radius: 4px; + + background: #222; + + /* 핵심: 회전값은 CSS 변수 */ transform: translate(-50%, -50%) rotate(var(--r, 0deg)); - transition: background 0.3s; + transition: transform 0.3s ease, background 0.3s ease; } -.bolt.bolt-acting::before { +/* 작동 상태 */ +.card__decor.card__decor-acting::before { transform: translate(-50%, -50%) rotate(calc(var(--r, 0deg) + 45deg)); background: #000; } -.bolt[data-rotate="45"]::before { +.card__decor[data-rotate="45"]::before { transform: translate(-50%, -50%) rotate(45deg); } -.bolt[data-rotate="160"]::before { + +.card__decor[data-rotate="160"]::before { transform: translate(-50%, -50%) rotate(160deg); } -.bolt[data-rotate="-20"]::before { + +.card__decor[data-rotate="-20"]::before { transform: translate(-50%, -50%) rotate(-20deg); } -.bolt[data-rotate="100"]::before { + +.card__decor[data-rotate="100"]::before { transform: translate(-50%, -50%) rotate(100deg); } @@ -263,7 +295,49 @@ header h1 .dot-point { font-weight: 700; } -.card-content { +.card__header { + width: 100%; + min-width: 0; + + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + overflow: hidden; +} + +.card__title { + max-width: 100%; + min-width: 0; + + font-family: "Nova"; + font-size: 3rem; + line-height: 1; + -webkit-text-stroke: 1px rgba(255, 255, 255, 0.5); + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-align: center; +} + +.card__subtitle { + max-width: 100%; + min-width: 0; + + font-family: "Pretendard", sans-serif; + font-size: 12px; + font-weight: 700; + color: #eaeaea; + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-align: center; +} + +.card__body { flex-grow: 1; display: flex; flex-direction: column; @@ -279,7 +353,7 @@ header h1 .dot-point { width: 100%; } -.url-display { +.card__meta--url { margin: 1.25rem 0 0.25rem; font-size: 1.125rem; font-weight: 700; @@ -296,7 +370,7 @@ header h1 .dot-point { font-family: "Pretendard", sans-serif; } -.desc { +.card__desc { margin: 0.25rem 0 0; color: #888888; font-size: 0.85rem; @@ -305,6 +379,10 @@ header h1 .dot-point { text-align: center; } +.card__desc[data-lang="en"] { + display: none; +} + .status-dot { position: absolute; top: 1rem; @@ -333,10 +411,12 @@ header h1 .dot-point { 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); @@ -349,58 +429,154 @@ header h1 .dot-point { } } -@keyframes bolt-full-cycle { +@keyframes card__decor-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; +@keyframes bolt-spin-return { + + /* 시작: 기본 각도 */ + 0% { + transform: translate(-50%, -50%) rotate(var(--r)); + } + + /* 오른쪽으로 3바퀴 */ + 60% { + transform: translate(-50%, -50%) rotate(calc(var(--r) + 1080deg)); + /* 360 * 3 */ + } + + /* 잠깐 정지 */ + 70% { + transform: translate(-50%, -50%) rotate(calc(var(--r) + 1080deg)); + } + + /* 왼쪽으로 복귀 */ + 100% { + transform: translate(-50%, -50%) rotate(var(--r)); + } +} + +.card__decor-acting::before { + animation: bolt-spin-return 16s cubic-bezier(0.65, 0, 0.35, 1) forwards; } -/* 1. 전체 스크롤바 영역 (폭 설정) */ ::-webkit-scrollbar { - width: 8px; /* 스크롤바 두께를 얇게 조절 */ - height: 8px; /* 가로 스크롤바 대비 */ + width: 8px; + height: 8px; } -/* 2. 스크롤바 배경 (트랙) */ ::-webkit-scrollbar-track { - background: #050505; /* 바디 배경색과 맞춰서 일체감 부여 */ + background: #050505; } -/* 3. 스크롤 바 (움직이는 부분) */ ::-webkit-scrollbar-thumb { - background: linear-gradient(to bottom, #333, #222); /* 나사 머리 같은 금속 느낌 */ + background: linear-gradient(to bottom, #333, #222); border-radius: 10px; - border: 2px solid #050505; /* 트랙과 분리되어 보이는 효과 (공간감) */ + border: 2px solid #050505; } -/* 4. 마우스 호버 시 (사용자가 잡았을 때) */ ::-webkit-scrollbar-thumb:hover { - background: #444; /* 살짝 밝아지며 반응 */ + background: #444; border-color: #050505; } /* 5. (선택사항) 스크롤바가 작동할 때 나사 불빛과 맞춘 포인트 */ ::-webkit-scrollbar-thumb:active { - background: #00ff88; /* 나사 LED 컬러로 포인트 */ + background: #00ff88; + /* 나사 LED 컬러로 포인트 */ box-shadow: 0 0 10px #00ff88; } -/* Firefox용 설정 */ * { scrollbar-width: thin; scrollbar-color: #333 #050505; +} + +.is-nova *, +.is-nova *::before, +.is-nova *::after { + font-family: "Nova", sans-serif !important; +} +.is-nova .section-title { + font-size: 1.75rem; + margin: 1rem 0 1.5rem; +} + +.is-nova .card__subtitle { + /* color: transparent; */ + /* position: relative; */ + display: none; +} + +.is-nova .card__title::after { + content: ""; + display: block; + width: 100%; + height: 1rem; + border-radius: 0.25rem; + opacity: 0.25; + + /* Glass base */ + background: linear-gradient(120deg, + rgba(255, 255, 255, 0.12), + rgba(255, 255, 255, 0.25), + rgba(255, 255, 255, 0.12)); + + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); + + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.25), + 0 4px 12px rgba(0, 0, 0, 0.08); + + background-size: 200% 100%; + animation: glass-skeleton 1.6s ease-in-out infinite; +} + +.is-nova .card__meta--url { + font-size: 1.5rem; + margin-bottom: 0; +} +.is-nova .card__desc[data-lang="ko"] { + display: none; +} + +.is-nova .card__desc[data-lang="ko"] { + display: none; +} + +.is-nova .card__desc[data-lang="en"] { + font-size: 1.25rem; + display: block; + margin: 0; +} + +@keyframes glass-skeleton { + 0% { + background-position: 0% 0; + } + + 50% { + background-position: 100% 0; + } + + 100% { + background-position: 0% 0; + } } \ No newline at end of file