psnc commited on
Commit
98214c6
·
verified ·
1 Parent(s): c5844bb

Sync from GitHub Actions

Browse files
CLAUDE.md CHANGED
@@ -53,6 +53,7 @@ nbeats/
53
  ├── static/ # 모델별 UI (index=N-BEATS, prophet, arima,
54
  │ │ # xgboost, ets, lightgbm, theta,
55
  │ ├── hybrid.html # 하이브리드 앙상블, timesfm)
 
56
  │ └── ...
57
  ├── timesfm-frontend/ # TimesFM 전용 Next.js 앱 (Vercel 배포)
58
  ├── test_data/ # 테스트용 CSV 데이터셋
 
53
  ├── static/ # 모델별 UI (index=N-BEATS, prophet, arima,
54
  │ │ # xgboost, ets, lightgbm, theta,
55
  │ ├── hybrid.html # 하이브리드 앙상블, timesfm)
56
+ │ ├── nav.js # 공유 모델 드롭다운 내비 (모델 추가 시 MODELS 배열만 수정)
57
  │ └── ...
58
  ├── timesfm-frontend/ # TimesFM 전용 Next.js 앱 (Vercel 배포)
59
  ├── test_data/ # 테스트용 CSV 데이터셋
docs/superpowers/specs/2026-07-11-model-nav-dropdown-design.md ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 모델 선택 드롭다운 내비게이션 설계
2
+
3
+ 날짜: 2026-07-11
4
+ 상태: 승인됨 (대화에서 설계 승인 후 기록)
5
+
6
+ ## 문제
7
+
8
+ 상단 내비가 9개 모델 링크를 한 줄에 담고 `overflow-x: auto`로 처리해,
9
+ 화면이 좁으면 옆으로 밀어서(슬라이드) 모델을 찾아야 한다. 사용자가 확인한
10
+ 불편: (1) 가로 스크롤로 목록이 잘림, (2) 현재 어떤 모델 페이지인지 파악 어려움.
11
+ 또한 동일 내비 마크업+CSS가 9개 HTML에 복붙되어 드리프트가 발생했다
12
+ (예: index.html은 `.tag-classic` CSS 누락).
13
+
14
+ ## 결정: 드롭다운 + 공유 스크립트 추출
15
+
16
+ 검토한 대안: 드롭다운(채택) / flex-wrap 줄바꿈 내비 / 하이브리드(반응형 전환).
17
+ 드롭다운이 두 불편을 모두 해결하고 폭과 무관하게 동작해 채택.
18
+
19
+ ### 구조
20
+
21
+ - **`static/nav.js` 신규** — 내비 전체(스타일 주입 + 마크업 생성 + 이벤트)를 담당.
22
+ - 모델 메타데이터(이름·경로·태그·태그 색)는 파일 내 `MODELS` 배열 하나로 관리.
23
+ - `location.pathname`으로 현재 모델을 판별해 버튼 라벨과 목록 강조에 반영.
24
+ - 페이지의 CSS 변수(`--surface`, `--accent` 등)를 사용해 페이지별 테마 유지
25
+ (변수 부재 대비 폴백 값 포함).
26
+ - **9개 HTML 페이지** — 기존 `<nav class="top-nav">` 블록과 내비 전용 CSS
27
+ (`.top-nav`, `.nav-*`, `.model-tag`, `.tag-*`)를 제거하고
28
+ `<script defer src="/static/nav.js"></script>` 한 줄로 대체.
29
+ - 새 모델 추가 시 `MODELS` 배열에 항목 하나만 추가.
30
+
31
+ ### 동작
32
+
33
+ - 내비 바: 브랜드("시계열 예측") + 현재 모델명·태그가 표시된 드롭다운 버튼(▾)
34
+ - 버튼 클릭 → 세로 목록(9개, 현재 모델 강조, 카테고리 태그) 토글
35
+ - 닫기: 항목 선택(페이지 이동), 바깥 클릭, Esc(버튼으로 포커스 복귀)
36
+ - 접근성: `aria-haspopup`/`aria-expanded`, 항목은 일반 `<a>`라 Tab/Enter 동작
37
+ - 모바일: 동일 컴포넌트 (가로 스크롤 원천 제거)
38
+
39
+ ### 범위 제외
40
+
41
+ - `timesfm-frontend/`(Vercel Next.js)는 별개 앱 — 미변경
42
+ - JS 로드 실패 폴백(noscript) — 로컬 정적 파일이라 생략 (YAGNI)
43
+
44
+ ## 테스트
45
+
46
+ - `tests/test_api_contract.py`에 추가: `/static/nav.js` 200 + 모델 목록 포함,
47
+ 9개 페이지가 nav.js를 로드하고 인라인 `<nav class="top-nav">`가 없음
48
+ - 실동작: 로컬 기동 후 브라우저에서 드롭다운 열기 → 모델 전환 → 현재 모델
49
+ 표시 확인 (`.claude/skills/verify` 레시피)
static/arima.html CHANGED
@@ -28,21 +28,6 @@
28
  * { box-sizing: border-box; margin: 0; padding: 0; }
29
  body { font-family: 'Outfit', sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; line-height: 1.6; }
30
 
31
- .top-nav { background: var(--surface); border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 100; }
32
- .nav-inner { max-width: 1060px; margin: 0 auto; padding: 0 2rem; display: flex; align-items: center; gap: 2rem; height: 56px; overflow-x: auto; }
33
- .nav-brand { font-weight: 700; font-size: 1rem; color: var(--text); text-decoration: none; letter-spacing: -0.01em; white-space: nowrap; }
34
- .nav-links { display: flex; gap: 0.25rem; list-style: none; }
35
- .nav-links a { display: flex; align-items: center; gap: 0.4rem; padding: 0.5rem 0.75rem; border-radius: 8px; text-decoration: none; font-size: 0.82rem; font-weight: 500; color: var(--text-muted); transition: all 0.15s; white-space: nowrap; }
36
- .nav-links a:hover { color: var(--text); background: var(--surface-hover); }
37
- .nav-links a.active { color: var(--accent); background: rgba(6, 182, 212, 0.1); }
38
- .nav-links .model-tag { font-size: 0.6rem; padding: 0.1rem 0.35rem; border-radius: 4px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; }
39
- .tag-dl { background: rgba(99, 102, 241, 0.15); color: #a5b4fc; }
40
- .tag-stat { background: rgba(34, 197, 94, 0.15); color: #86efac; }
41
- .tag-ml { background: rgba(245, 158, 11, 0.15); color: #fcd34d; }
42
- .tag-classic { background: rgba(6, 182, 212, 0.15); color: #67e8f9; }
43
- .tag-ets { background: rgba(16, 185, 129, 0.15); color: #6ee7b7; }
44
- .tag-theta { background: rgba(168, 85, 247, 0.15); color: #c4b5fd; }
45
-
46
  .container { max-width: 1060px; margin: 0 auto; padding: 2rem; }
47
  header { margin-bottom: 2rem; padding-bottom: 1.25rem; border-bottom: 1px solid var(--border); }
48
  h1 { font-size: 1.75rem; font-weight: 700; letter-spacing: -0.02em; background: linear-gradient(135deg, #fff 0%, #67e8f9 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
@@ -115,24 +100,9 @@
115
  .disabled-fields input, .disabled-fields select { opacity: 0.35; }
116
  #manualOrderRow input:disabled { cursor: not-allowed; background: var(--surface); }
117
  </style>
 
118
  </head>
119
  <body>
120
- <nav class="top-nav">
121
- <div class="nav-inner">
122
- <a href="/" class="nav-brand">시계열 예측</a>
123
- <ul class="nav-links">
124
- <li><a href="/">N-BEATS <span class="model-tag tag-dl">DL</span></a></li>
125
- <li><a href="/prophet">Prophet <span class="model-tag tag-stat">Stat</span></a></li>
126
- <li><a href="/xgboost">XGBoost <span class="model-tag tag-ml">ML</span></a></li>
127
- <li><a href="/lightgbm">LightGBM <span class="model-tag tag-ml">ML</span></a></li>
128
- <li><a href="/arima" class="active">ARIMA <span class="model-tag tag-classic">Classical</span></a></li>
129
- <li><a href="/theta">Theta <span class="model-tag tag-theta">Classical</span></a></li>
130
- <li><a href="/ets">ETS <span class="model-tag tag-ets">Smoothing</span></a></li>
131
- <li><a href="/hybrid">Hybrid <span class="model-tag" style="background: rgba(236, 72, 153, 0.15); color: #f472b6;">Ensemble</span></a></li>
132
- <li><a href="/timesfm">TimesFM <span class="model-tag" style="background: rgba(56, 189, 248, 0.15); color: #7dd3fc;">FM</span></a></li>
133
- </ul>
134
- </div>
135
- </nav>
136
 
137
  <div class="container">
138
  <header>
 
28
  * { box-sizing: border-box; margin: 0; padding: 0; }
29
  body { font-family: 'Outfit', sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; line-height: 1.6; }
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  .container { max-width: 1060px; margin: 0 auto; padding: 2rem; }
32
  header { margin-bottom: 2rem; padding-bottom: 1.25rem; border-bottom: 1px solid var(--border); }
33
  h1 { font-size: 1.75rem; font-weight: 700; letter-spacing: -0.02em; background: linear-gradient(135deg, #fff 0%, #67e8f9 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
 
100
  .disabled-fields input, .disabled-fields select { opacity: 0.35; }
101
  #manualOrderRow input:disabled { cursor: not-allowed; background: var(--surface); }
102
  </style>
103
+ <script defer src="/static/nav.js"></script>
104
  </head>
105
  <body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
 
107
  <div class="container">
108
  <header>
static/ets.html CHANGED
@@ -28,21 +28,6 @@
28
  * { box-sizing: border-box; margin: 0; padding: 0; }
29
  body { font-family: 'Outfit', sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; line-height: 1.6; }
30
 
31
- .top-nav { background: var(--surface); border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 100; }
32
- .nav-inner { max-width: 1060px; margin: 0 auto; padding: 0 2rem; display: flex; align-items: center; gap: 2rem; height: 56px; overflow-x: auto; }
33
- .nav-brand { font-weight: 700; font-size: 1rem; color: var(--text); text-decoration: none; letter-spacing: -0.01em; white-space: nowrap; }
34
- .nav-links { display: flex; gap: 0.25rem; list-style: none; }
35
- .nav-links a { display: flex; align-items: center; gap: 0.4rem; padding: 0.5rem 0.75rem; border-radius: 8px; text-decoration: none; font-size: 0.82rem; font-weight: 500; color: var(--text-muted); transition: all 0.15s; white-space: nowrap; }
36
- .nav-links a:hover { color: var(--text); background: var(--surface-hover); }
37
- .nav-links a.active { color: var(--accent); background: rgba(16, 185, 129, 0.1); }
38
- .nav-links .model-tag { font-size: 0.6rem; padding: 0.1rem 0.35rem; border-radius: 4px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; }
39
- .tag-dl { background: rgba(99, 102, 241, 0.15); color: #a5b4fc; }
40
- .tag-stat { background: rgba(34, 197, 94, 0.15); color: #86efac; }
41
- .tag-ml { background: rgba(245, 158, 11, 0.15); color: #fcd34d; }
42
- .tag-classic { background: rgba(6, 182, 212, 0.15); color: #67e8f9; }
43
- .tag-ets { background: rgba(16, 185, 129, 0.15); color: #6ee7b7; }
44
- .tag-theta { background: rgba(168, 85, 247, 0.15); color: #c4b5fd; }
45
-
46
  .container { max-width: 1060px; margin: 0 auto; padding: 2rem; }
47
  header { margin-bottom: 2rem; padding-bottom: 1.25rem; border-bottom: 1px solid var(--border); }
48
  h1 { font-size: 1.75rem; font-weight: 700; letter-spacing: -0.02em; background: linear-gradient(135deg, #fff 0%, #6ee7b7 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
@@ -108,24 +93,9 @@
108
  .spinner { display: inline-block; width: 1em; height: 1em; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: spin 0.6s linear infinite; vertical-align: -0.15em; margin-right: 0.4em; }
109
  @keyframes spin { to { transform: rotate(360deg); } }
110
  </style>
 
111
  </head>
112
  <body>
113
- <nav class="top-nav">
114
- <div class="nav-inner">
115
- <a href="/" class="nav-brand">시계열 예측</a>
116
- <ul class="nav-links">
117
- <li><a href="/">N-BEATS <span class="model-tag tag-dl">DL</span></a></li>
118
- <li><a href="/prophet">Prophet <span class="model-tag tag-stat">Stat</span></a></li>
119
- <li><a href="/xgboost">XGBoost <span class="model-tag tag-ml">ML</span></a></li>
120
- <li><a href="/lightgbm">LightGBM <span class="model-tag tag-ml">ML</span></a></li>
121
- <li><a href="/arima">ARIMA <span class="model-tag tag-classic">Classical</span></a></li>
122
- <li><a href="/theta">Theta <span class="model-tag tag-theta">Classical</span></a></li>
123
- <li><a href="/ets" class="active">ETS <span class="model-tag tag-ets">Smoothing</span></a></li>
124
- <li><a href="/hybrid">Hybrid <span class="model-tag" style="background: rgba(236, 72, 153, 0.15); color: #f472b6;">Ensemble</span></a></li>
125
- <li><a href="/timesfm">TimesFM <span class="model-tag" style="background: rgba(56, 189, 248, 0.15); color: #7dd3fc;">FM</span></a></li>
126
- </ul>
127
- </div>
128
- </nav>
129
 
130
  <div class="container">
131
  <header>
 
28
  * { box-sizing: border-box; margin: 0; padding: 0; }
29
  body { font-family: 'Outfit', sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; line-height: 1.6; }
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  .container { max-width: 1060px; margin: 0 auto; padding: 2rem; }
32
  header { margin-bottom: 2rem; padding-bottom: 1.25rem; border-bottom: 1px solid var(--border); }
33
  h1 { font-size: 1.75rem; font-weight: 700; letter-spacing: -0.02em; background: linear-gradient(135deg, #fff 0%, #6ee7b7 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
 
93
  .spinner { display: inline-block; width: 1em; height: 1em; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: spin 0.6s linear infinite; vertical-align: -0.15em; margin-right: 0.4em; }
94
  @keyframes spin { to { transform: rotate(360deg); } }
95
  </style>
96
+ <script defer src="/static/nav.js"></script>
97
  </head>
98
  <body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
  <div class="container">
101
  <header>
static/hybrid.html CHANGED
@@ -44,103 +44,6 @@
44
  line-height: 1.6;
45
  }
46
 
47
- .top-nav {
48
- background: var(--surface);
49
- border-bottom: 1px solid var(--border);
50
- position: sticky;
51
- top: 0;
52
- z-index: 100;
53
- }
54
-
55
- .nav-inner {
56
- max-width: 1060px;
57
- margin: 0 auto;
58
- padding: 0 2rem;
59
- display: flex;
60
- align-items: center;
61
- gap: 2rem;
62
- height: 56px;
63
- overflow-x: auto;
64
- }
65
-
66
- .nav-brand {
67
- font-weight: 700;
68
- font-size: 1rem;
69
- color: var(--text);
70
- text-decoration: none;
71
- letter-spacing: -0.01em;
72
- white-space: nowrap;
73
- }
74
-
75
- .nav-links {
76
- display: flex;
77
- gap: 0.25rem;
78
- list-style: none;
79
- }
80
-
81
- .nav-links a {
82
- display: flex;
83
- align-items: center;
84
- gap: 0.4rem;
85
- padding: 0.5rem 0.75rem;
86
- border-radius: 8px;
87
- text-decoration: none;
88
- font-size: 0.82rem;
89
- font-weight: 500;
90
- color: var(--text-muted);
91
- transition: all 0.15s;
92
- white-space: nowrap;
93
- }
94
-
95
- .nav-links a:hover {
96
- color: var(--text);
97
- background: var(--surface-hover);
98
- }
99
-
100
- .nav-links a.active {
101
- color: var(--accent);
102
- background: rgba(99, 102, 241, 0.1);
103
- }
104
-
105
- .nav-links .model-tag {
106
- font-size: 0.6rem;
107
- padding: 0.1rem 0.35rem;
108
- border-radius: 4px;
109
- font-weight: 600;
110
- text-transform: uppercase;
111
- letter-spacing: 0.03em;
112
- }
113
-
114
- .tag-dl {
115
- background: rgba(99, 102, 241, 0.15);
116
- color: #a5b4fc;
117
- }
118
-
119
- .tag-stat {
120
- background: rgba(34, 197, 94, 0.15);
121
- color: #86efac;
122
- }
123
-
124
- .tag-ml {
125
- background: rgba(245, 158, 11, 0.15);
126
- color: #fcd34d;
127
- }
128
-
129
- .tag-classic {
130
- background: rgba(6, 182, 212, 0.15);
131
- color: #67e8f9;
132
- }
133
-
134
- .tag-ets {
135
- background: rgba(16, 185, 129, 0.15);
136
- color: #6ee7b7;
137
- }
138
-
139
- .tag-theta {
140
- background: rgba(168, 85, 247, 0.15);
141
- color: #c4b5fd;
142
- }
143
-
144
  .tag-hybrid {
145
  background: rgba(99, 102, 241, 0.2);
146
  color: #a5b4fc;
@@ -536,25 +439,10 @@
536
  text-align: left;
537
  }
538
  </style>
 
539
  </head>
540
 
541
  <body>
542
- <nav class="top-nav">
543
- <div class="nav-inner">
544
- <a href="/" class="nav-brand">시계열 예측</a>
545
- <ul class="nav-links">
546
- <li><a href="/">N-BEATS <span class="model-tag tag-dl">DL</span></a></li>
547
- <li><a href="/prophet">Prophet <span class="model-tag tag-stat">Stat</span></a></li>
548
- <li><a href="/xgboost">XGBoost <span class="model-tag tag-ml">ML</span></a></li>
549
- <li><a href="/lightgbm">LightGBM <span class="model-tag tag-ml">ML</span></a></li>
550
- <li><a href="/arima">ARIMA <span class="model-tag tag-classic">Classical</span></a></li>
551
- <li><a href="/theta">Theta <span class="model-tag tag-theta">Classical</span></a></li>
552
- <li><a href="/ets">ETS <span class="model-tag tag-ets">Smoothing</span></a></li>
553
- <li><a href="/hybrid" class="active">하이브리드 <span class="model-tag tag-hybrid">Hybrid</span></a></li>
554
- <li><a href="/timesfm">TimesFM <span class="model-tag" style="background: rgba(56, 189, 248, 0.15); color: #7dd3fc;">FM</span></a></li>
555
- </ul>
556
- </div>
557
- </nav>
558
 
559
  <div class="container">
560
  <header>
 
44
  line-height: 1.6;
45
  }
46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  .tag-hybrid {
48
  background: rgba(99, 102, 241, 0.2);
49
  color: #a5b4fc;
 
439
  text-align: left;
440
  }
441
  </style>
442
+ <script defer src="/static/nav.js"></script>
443
  </head>
444
 
445
  <body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
446
 
447
  <div class="container">
448
  <header>
static/index.html CHANGED
@@ -37,70 +37,6 @@
37
  }
38
 
39
  /* ---- 네비게이션 ---- */
40
- .top-nav {
41
- background: var(--surface);
42
- border-bottom: 1px solid var(--border);
43
- position: sticky;
44
- top: 0;
45
- z-index: 100;
46
- }
47
- .nav-inner {
48
- max-width: 1060px;
49
- margin: 0 auto;
50
- padding: 0 2rem;
51
- display: flex;
52
- align-items: center;
53
- gap: 2rem;
54
- height: 56px;
55
- overflow-x: auto;
56
- }
57
- .nav-brand {
58
- font-weight: 700;
59
- font-size: 1rem;
60
- color: var(--text);
61
- text-decoration: none;
62
- letter-spacing: -0.01em;
63
- white-space: nowrap;
64
- }
65
- .nav-links {
66
- display: flex;
67
- gap: 0.25rem;
68
- list-style: none;
69
- }
70
- .nav-links a {
71
- display: flex;
72
- align-items: center;
73
- gap: 0.4rem;
74
- padding: 0.5rem 0.75rem;
75
- border-radius: 8px;
76
- text-decoration: none;
77
- font-size: 0.82rem;
78
- font-weight: 500;
79
- white-space: nowrap;
80
- color: var(--text-muted);
81
- transition: all 0.15s;
82
- }
83
- .nav-links a:hover {
84
- color: var(--text);
85
- background: var(--surface-hover);
86
- }
87
- .nav-links a.active {
88
- color: var(--accent);
89
- background: rgba(99, 102, 241, 0.1);
90
- }
91
- .nav-links .model-tag {
92
- font-size: 0.6rem;
93
- padding: 0.1rem 0.35rem;
94
- border-radius: 4px;
95
- font-weight: 600;
96
- text-transform: uppercase;
97
- letter-spacing: 0.03em;
98
- }
99
- .tag-dl { background: rgba(99, 102, 241, 0.15); color: #a5b4fc; }
100
- .tag-ets { background: rgba(16, 185, 129, 0.15); color: #6ee7b7; }
101
- .tag-theta { background: rgba(168, 85, 247, 0.15); color: #c4b5fd; }
102
- .tag-stat { background: rgba(34, 197, 94, 0.15); color: #86efac; }
103
- .tag-ml { background: rgba(245, 158, 11, 0.15); color: #fcd34d; }
104
 
105
  .container {
106
  max-width: 1060px;
@@ -388,24 +324,9 @@
388
  }
389
  @keyframes spin { to { transform: rotate(360deg); } }
390
  </style>
 
391
  </head>
392
  <body>
393
- <nav class="top-nav">
394
- <div class="nav-inner">
395
- <a href="/" class="nav-brand">시계열 예측</a>
396
- <ul class="nav-links">
397
- <li><a href="/" class="active">N-BEATS <span class="model-tag tag-dl">DL</span></a></li>
398
- <li><a href="/prophet">Prophet <span class="model-tag tag-stat">Stat</span></a></li>
399
- <li><a href="/xgboost">XGBoost <span class="model-tag tag-ml">ML</span></a></li>
400
- <li><a href="/lightgbm">LightGBM <span class="model-tag tag-ml">ML</span></a></li>
401
- <li><a href="/arima">ARIMA <span class="model-tag tag-classic">Classical</span></a></li>
402
- <li><a href="/theta">Theta <span class="model-tag tag-theta">Classical</span></a></li>
403
- <li><a href="/ets">ETS <span class="model-tag tag-ets">Smoothing</span></a></li>
404
- <li><a href="/hybrid">Hybrid <span class="model-tag" style="background: rgba(236, 72, 153, 0.15); color: #f472b6;">Ensemble</span></a></li>
405
- <li><a href="/timesfm">TimesFM <span class="model-tag" style="background: rgba(56, 189, 248, 0.15); color: #7dd3fc;">FM</span></a></li>
406
- </ul>
407
- </div>
408
- </nav>
409
 
410
  <div class="container">
411
  <header>
 
37
  }
38
 
39
  /* ---- 네비게이션 ---- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
  .container {
42
  max-width: 1060px;
 
324
  }
325
  @keyframes spin { to { transform: rotate(360deg); } }
326
  </style>
327
+ <script defer src="/static/nav.js"></script>
328
  </head>
329
  <body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
 
331
  <div class="container">
332
  <header>
static/lightgbm.html CHANGED
@@ -28,21 +28,6 @@
28
  * { box-sizing: border-box; margin: 0; padding: 0; }
29
  body { font-family: 'Outfit', sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; line-height: 1.6; }
30
 
31
- .top-nav { background: var(--surface); border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 100; }
32
- .nav-inner { max-width: 1060px; margin: 0 auto; padding: 0 2rem; display: flex; align-items: center; gap: 2rem; height: 56px; overflow-x: auto; }
33
- .nav-brand { font-weight: 700; font-size: 1rem; color: var(--text); text-decoration: none; letter-spacing: -0.01em; white-space: nowrap; }
34
- .nav-links { display: flex; gap: 0.25rem; list-style: none; }
35
- .nav-links a { display: flex; align-items: center; gap: 0.4rem; padding: 0.5rem 0.75rem; border-radius: 8px; text-decoration: none; font-size: 0.82rem; font-weight: 500; color: var(--text-muted); transition: all 0.15s; white-space: nowrap; }
36
- .nav-links a:hover { color: var(--text); background: var(--surface-hover); }
37
- .nav-links a.active { color: var(--accent); background: rgba(132, 204, 22, 0.1); }
38
- .nav-links .model-tag { font-size: 0.6rem; padding: 0.1rem 0.35rem; border-radius: 4px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; }
39
- .tag-dl { background: rgba(99, 102, 241, 0.15); color: #a5b4fc; }
40
- .tag-stat { background: rgba(34, 197, 94, 0.15); color: #86efac; }
41
- .tag-ml { background: rgba(245, 158, 11, 0.15); color: #fcd34d; }
42
- .tag-classic { background: rgba(6, 182, 212, 0.15); color: #67e8f9; }
43
- .tag-ets { background: rgba(16, 185, 129, 0.15); color: #6ee7b7; }
44
- .tag-theta { background: rgba(168, 85, 247, 0.15); color: #c4b5fd; }
45
-
46
  .container { max-width: 1060px; margin: 0 auto; padding: 2rem; }
47
  header { margin-bottom: 2rem; padding-bottom: 1.25rem; border-bottom: 1px solid var(--border); }
48
  h1 { font-size: 1.75rem; font-weight: 700; letter-spacing: -0.02em; background: linear-gradient(135deg, #fff 0%, #bef264 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
@@ -106,24 +91,9 @@
106
  .spinner { display: inline-block; width: 1em; height: 1em; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: spin 0.6s linear infinite; vertical-align: -0.15em; margin-right: 0.4em; }
107
  @keyframes spin { to { transform: rotate(360deg); } }
108
  </style>
 
109
  </head>
110
  <body>
111
- <nav class="top-nav">
112
- <div class="nav-inner">
113
- <a href="/" class="nav-brand">시계열 예측</a>
114
- <ul class="nav-links">
115
- <li><a href="/">N-BEATS <span class="model-tag tag-dl">DL</span></a></li>
116
- <li><a href="/prophet">Prophet <span class="model-tag tag-stat">Stat</span></a></li>
117
- <li><a href="/xgboost">XGBoost <span class="model-tag tag-ml">ML</span></a></li>
118
- <li><a href="/lightgbm" class="active">LightGBM <span class="model-tag tag-ml">ML</span></a></li>
119
- <li><a href="/arima">ARIMA <span class="model-tag tag-classic">Classical</span></a></li>
120
- <li><a href="/theta">Theta <span class="model-tag tag-theta">Classical</span></a></li>
121
- <li><a href="/ets">ETS <span class="model-tag tag-ets">Smoothing</span></a></li>
122
- <li><a href="/hybrid">Hybrid <span class="model-tag" style="background: rgba(236, 72, 153, 0.15); color: #f472b6;">Ensemble</span></a></li>
123
- <li><a href="/timesfm">TimesFM <span class="model-tag" style="background: rgba(56, 189, 248, 0.15); color: #7dd3fc;">FM</span></a></li>
124
- </ul>
125
- </div>
126
- </nav>
127
 
128
  <div class="container">
129
  <header>
 
28
  * { box-sizing: border-box; margin: 0; padding: 0; }
29
  body { font-family: 'Outfit', sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; line-height: 1.6; }
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  .container { max-width: 1060px; margin: 0 auto; padding: 2rem; }
32
  header { margin-bottom: 2rem; padding-bottom: 1.25rem; border-bottom: 1px solid var(--border); }
33
  h1 { font-size: 1.75rem; font-weight: 700; letter-spacing: -0.02em; background: linear-gradient(135deg, #fff 0%, #bef264 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
 
91
  .spinner { display: inline-block; width: 1em; height: 1em; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: spin 0.6s linear infinite; vertical-align: -0.15em; margin-right: 0.4em; }
92
  @keyframes spin { to { transform: rotate(360deg); } }
93
  </style>
94
+ <script defer src="/static/nav.js"></script>
95
  </head>
96
  <body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
  <div class="container">
99
  <header>
static/nav.js ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * 공유 모델 내비게이션 (드롭다운)
3
+ *
4
+ * 모든 static 페이지가 <script defer src="/static/nav.js">로 로드한다.
5
+ * 모델 추가 시 MODELS 배열에 항목 하나만 추가하면 전 페이지에 반영된다.
6
+ * 색상은 페이지의 CSS 변수(--surface 등)를 사용해 페이지별 테마를 따른다.
7
+ */
8
+ (function () {
9
+ "use strict";
10
+
11
+ var MODELS = [
12
+ { path: "/", name: "N-BEATS", tag: "DL", bg: "rgba(99, 102, 241, 0.15)", color: "#a5b4fc" },
13
+ { path: "/prophet", name: "Prophet", tag: "Stat", bg: "rgba(34, 197, 94, 0.15)", color: "#86efac" },
14
+ { path: "/xgboost", name: "XGBoost", tag: "ML", bg: "rgba(245, 158, 11, 0.15)", color: "#fcd34d" },
15
+ { path: "/lightgbm", name: "LightGBM", tag: "ML", bg: "rgba(245, 158, 11, 0.15)", color: "#fcd34d" },
16
+ { path: "/arima", name: "ARIMA", tag: "Classical", bg: "rgba(6, 182, 212, 0.15)", color: "#67e8f9" },
17
+ { path: "/theta", name: "Theta", tag: "Classical", bg: "rgba(168, 85, 247, 0.15)", color: "#c4b5fd" },
18
+ { path: "/ets", name: "ETS", tag: "Smoothing", bg: "rgba(16, 185, 129, 0.15)", color: "#6ee7b7" },
19
+ { path: "/hybrid", name: "Hybrid", tag: "Ensemble", bg: "rgba(236, 72, 153, 0.15)", color: "#f472b6" },
20
+ { path: "/timesfm", name: "TimesFM", tag: "FM", bg: "rgba(56, 189, 248, 0.15)", color: "#7dd3fc" }
21
+ ];
22
+
23
+ var CSS = [
24
+ '.top-nav { background: var(--surface, #17171c); border-bottom: 1px solid var(--border, #2a2a33); position: sticky; top: 0; z-index: 100; }',
25
+ '.nav-inner { max-width: 1060px; margin: 0 auto; padding: 0 2rem; display: flex; align-items: center; gap: 1.25rem; height: 56px; }',
26
+ '.nav-brand { font-weight: 700; font-size: 1rem; color: var(--text, #e8e8ed); text-decoration: none; letter-spacing: -0.01em; white-space: nowrap; }',
27
+ '.model-dd { position: relative; }',
28
+ '.model-dd-btn { display: flex; align-items: center; gap: 0.5rem; padding: 0.45rem 0.85rem; border-radius: 8px; border: 1px solid var(--border, #2a2a33); background: transparent; color: var(--text, #e8e8ed); font-size: 0.85rem; font-weight: 600; font-family: inherit; cursor: pointer; transition: border-color 0.15s; }',
29
+ '.model-dd-btn:hover { border-color: var(--accent, #6366f1); }',
30
+ '.model-dd-caret { font-size: 0.65rem; color: var(--text-muted, #9a9aa5); transition: transform 0.15s; }',
31
+ '.model-dd.open .model-dd-caret { transform: rotate(180deg); }',
32
+ '.model-dd-list { position: absolute; top: calc(100% + 6px); left: 0; min-width: 230px; margin: 0; padding: 0.4rem; list-style: none; background: var(--surface, #17171c); border: 1px solid var(--border, #2a2a33); border-radius: 10px; box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45); z-index: 101; }',
33
+ '.model-dd-list a { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.5rem 0.75rem; border-radius: 7px; text-decoration: none; font-size: 0.85rem; font-weight: 500; color: var(--text-muted, #9a9aa5); }',
34
+ '.model-dd-list a:hover { color: var(--text, #e8e8ed); background: var(--surface-hover, rgba(255, 255, 255, 0.05)); }',
35
+ '.model-dd-list a.active { color: var(--accent, #6366f1); background: rgba(99, 102, 241, 0.1); }',
36
+ '.model-tag { font-size: 0.6rem; padding: 0.12rem 0.4rem; border-radius: 4px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; }'
37
+ ].join('\n');
38
+
39
+ function tagHtml(model) {
40
+ return '<span class="model-tag" style="background:' + model.bg + ';color:' + model.color + ';">' + model.tag + '</span>';
41
+ }
42
+
43
+ function normalizePath(p) {
44
+ return p.length > 1 && p.slice(-1) === '/' ? p.slice(0, -1) : p;
45
+ }
46
+
47
+ var currentPath = normalizePath(window.location.pathname);
48
+ var current = MODELS.filter(function (m) { return m.path === currentPath; })[0] || MODELS[0];
49
+
50
+ var items = MODELS.map(function (m) {
51
+ var active = m.path === current.path ? ' class="active"' : '';
52
+ return '<li><a href="' + m.path + '"' + active + '><span>' + m.name + '</span>' + tagHtml(m) + '</a></li>';
53
+ }).join('');
54
+
55
+ var style = document.createElement('style');
56
+ style.textContent = CSS;
57
+ document.head.appendChild(style);
58
+
59
+ var nav = document.createElement('nav');
60
+ nav.className = 'top-nav';
61
+ nav.innerHTML =
62
+ '<div class="nav-inner">' +
63
+ '<a href="/" class="nav-brand">시계열 예측</a>' +
64
+ '<div class="model-dd">' +
65
+ '<button type="button" class="model-dd-btn" aria-haspopup="true" aria-expanded="false">' +
66
+ '<span>' + current.name + '</span>' + tagHtml(current) +
67
+ '<span class="model-dd-caret">▾</span>' +
68
+ '</button>' +
69
+ '<ul class="model-dd-list" hidden>' + items + '</ul>' +
70
+ '</div>' +
71
+ '</div>';
72
+ document.body.insertAdjacentElement('afterbegin', nav);
73
+
74
+ var dd = nav.querySelector('.model-dd');
75
+ var btn = nav.querySelector('.model-dd-btn');
76
+ var list = nav.querySelector('.model-dd-list');
77
+
78
+ function setOpen(open) {
79
+ list.hidden = !open;
80
+ dd.classList.toggle('open', open);
81
+ btn.setAttribute('aria-expanded', String(open));
82
+ }
83
+
84
+ btn.addEventListener('click', function () {
85
+ setOpen(list.hidden);
86
+ });
87
+
88
+ document.addEventListener('click', function (e) {
89
+ if (!list.hidden && !dd.contains(e.target)) setOpen(false);
90
+ });
91
+
92
+ document.addEventListener('keydown', function (e) {
93
+ if (e.key === 'Escape' && !list.hidden) {
94
+ setOpen(false);
95
+ btn.focus();
96
+ }
97
+ });
98
+ })();
static/prophet.html CHANGED
@@ -28,21 +28,6 @@
28
  * { box-sizing: border-box; margin: 0; padding: 0; }
29
  body { font-family: 'Outfit', sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; line-height: 1.6; }
30
 
31
- .top-nav { background: var(--surface); border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 100; }
32
- .nav-inner { max-width: 1060px; margin: 0 auto; padding: 0 2rem; display: flex; align-items: center; gap: 2rem; height: 56px; overflow-x: auto; }
33
- .nav-brand { font-weight: 700; font-size: 1rem; color: var(--text); text-decoration: none; letter-spacing: -0.01em; white-space: nowrap; }
34
- .nav-links { display: flex; gap: 0.25rem; list-style: none; }
35
- .nav-links a { display: flex; align-items: center; gap: 0.4rem; padding: 0.5rem 0.75rem; border-radius: 8px; text-decoration: none; font-size: 0.82rem; font-weight: 500; color: var(--text-muted); transition: all 0.15s; white-space: nowrap; }
36
- .nav-links a:hover { color: var(--text); background: var(--surface-hover); }
37
- .nav-links a.active { color: var(--accent); background: rgba(34, 197, 94, 0.1); }
38
- .nav-links .model-tag { font-size: 0.6rem; padding: 0.1rem 0.35rem; border-radius: 4px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; }
39
- .tag-dl { background: rgba(99, 102, 241, 0.15); color: #a5b4fc; }
40
- .tag-stat { background: rgba(34, 197, 94, 0.15); color: #86efac; }
41
- .tag-ml { background: rgba(245, 158, 11, 0.15); color: #fcd34d; }
42
- .tag-classic { background: rgba(6, 182, 212, 0.15); color: #67e8f9; }
43
- .tag-ets { background: rgba(16, 185, 129, 0.15); color: #6ee7b7; }
44
- .tag-theta { background: rgba(168, 85, 247, 0.15); color: #c4b5fd; }
45
-
46
  .container { max-width: 1060px; margin: 0 auto; padding: 2rem; }
47
  header { margin-bottom: 2rem; padding-bottom: 1.25rem; border-bottom: 1px solid var(--border); }
48
  h1 { font-size: 1.75rem; font-weight: 700; letter-spacing: -0.02em; background: linear-gradient(135deg, #fff 0%, #86efac 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
@@ -106,24 +91,9 @@
106
  .spinner { display: inline-block; width: 1em; height: 1em; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: spin 0.6s linear infinite; vertical-align: -0.15em; margin-right: 0.4em; }
107
  @keyframes spin { to { transform: rotate(360deg); } }
108
  </style>
 
109
  </head>
110
  <body>
111
- <nav class="top-nav">
112
- <div class="nav-inner">
113
- <a href="/" class="nav-brand">시계열 예측</a>
114
- <ul class="nav-links">
115
- <li><a href="/">N-BEATS <span class="model-tag tag-dl">DL</span></a></li>
116
- <li><a href="/prophet" class="active">Prophet <span class="model-tag tag-stat">Stat</span></a></li>
117
- <li><a href="/xgboost">XGBoost <span class="model-tag tag-ml">ML</span></a></li>
118
- <li><a href="/lightgbm">LightGBM <span class="model-tag tag-ml">ML</span></a></li>
119
- <li><a href="/arima">ARIMA <span class="model-tag tag-classic">Classical</span></a></li>
120
- <li><a href="/theta">Theta <span class="model-tag tag-theta">Classical</span></a></li>
121
- <li><a href="/ets">ETS <span class="model-tag tag-ets">Smoothing</span></a></li>
122
- <li><a href="/hybrid">Hybrid <span class="model-tag" style="background: rgba(236, 72, 153, 0.15); color: #f472b6;">Ensemble</span></a></li>
123
- <li><a href="/timesfm">TimesFM <span class="model-tag" style="background: rgba(56, 189, 248, 0.15); color: #7dd3fc;">FM</span></a></li>
124
- </ul>
125
- </div>
126
- </nav>
127
 
128
  <div class="container">
129
  <header>
 
28
  * { box-sizing: border-box; margin: 0; padding: 0; }
29
  body { font-family: 'Outfit', sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; line-height: 1.6; }
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  .container { max-width: 1060px; margin: 0 auto; padding: 2rem; }
32
  header { margin-bottom: 2rem; padding-bottom: 1.25rem; border-bottom: 1px solid var(--border); }
33
  h1 { font-size: 1.75rem; font-weight: 700; letter-spacing: -0.02em; background: linear-gradient(135deg, #fff 0%, #86efac 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
 
91
  .spinner { display: inline-block; width: 1em; height: 1em; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: spin 0.6s linear infinite; vertical-align: -0.15em; margin-right: 0.4em; }
92
  @keyframes spin { to { transform: rotate(360deg); } }
93
  </style>
94
+ <script defer src="/static/nav.js"></script>
95
  </head>
96
  <body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
  <div class="container">
99
  <header>
static/theta.html CHANGED
@@ -28,21 +28,6 @@
28
  * { box-sizing: border-box; margin: 0; padding: 0; }
29
  body { font-family: 'Outfit', sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; line-height: 1.6; }
30
 
31
- .top-nav { background: var(--surface); border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 100; }
32
- .nav-inner { max-width: 1060px; margin: 0 auto; padding: 0 2rem; display: flex; align-items: center; gap: 2rem; height: 56px; overflow-x: auto; }
33
- .nav-brand { font-weight: 700; font-size: 1rem; color: var(--text); text-decoration: none; letter-spacing: -0.01em; white-space: nowrap; }
34
- .nav-links { display: flex; gap: 0.25rem; list-style: none; }
35
- .nav-links a { display: flex; align-items: center; gap: 0.4rem; padding: 0.5rem 0.75rem; border-radius: 8px; text-decoration: none; font-size: 0.82rem; font-weight: 500; color: var(--text-muted); transition: all 0.15s; white-space: nowrap; }
36
- .nav-links a:hover { color: var(--text); background: var(--surface-hover); }
37
- .nav-links a.active { color: var(--accent); background: rgba(168, 85, 247, 0.1); }
38
- .nav-links .model-tag { font-size: 0.6rem; padding: 0.1rem 0.35rem; border-radius: 4px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; }
39
- .tag-dl { background: rgba(99, 102, 241, 0.15); color: #a5b4fc; }
40
- .tag-stat { background: rgba(34, 197, 94, 0.15); color: #86efac; }
41
- .tag-ml { background: rgba(245, 158, 11, 0.15); color: #fcd34d; }
42
- .tag-classic { background: rgba(6, 182, 212, 0.15); color: #67e8f9; }
43
- .tag-ets { background: rgba(16, 185, 129, 0.15); color: #6ee7b7; }
44
- .tag-theta { background: rgba(168, 85, 247, 0.15); color: #c4b5fd; }
45
-
46
  .container { max-width: 1060px; margin: 0 auto; padding: 2rem; }
47
  header { margin-bottom: 2rem; padding-bottom: 1.25rem; border-bottom: 1px solid var(--border); }
48
  h1 { font-size: 1.75rem; font-weight: 700; letter-spacing: -0.02em; background: linear-gradient(135deg, #fff 0%, #c4b5fd 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
@@ -108,24 +93,9 @@
108
  .spinner { display: inline-block; width: 1em; height: 1em; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: spin 0.6s linear infinite; vertical-align: -0.15em; margin-right: 0.4em; }
109
  @keyframes spin { to { transform: rotate(360deg); } }
110
  </style>
 
111
  </head>
112
  <body>
113
- <nav class="top-nav">
114
- <div class="nav-inner">
115
- <a href="/" class="nav-brand">시계열 예측</a>
116
- <ul class="nav-links">
117
- <li><a href="/">N-BEATS <span class="model-tag tag-dl">DL</span></a></li>
118
- <li><a href="/prophet">Prophet <span class="model-tag tag-stat">Stat</span></a></li>
119
- <li><a href="/xgboost">XGBoost <span class="model-tag tag-ml">ML</span></a></li>
120
- <li><a href="/lightgbm">LightGBM <span class="model-tag tag-ml">ML</span></a></li>
121
- <li><a href="/arima">ARIMA <span class="model-tag tag-classic">Classical</span></a></li>
122
- <li><a href="/theta" class="active">Theta <span class="model-tag tag-theta">Classical</span></a></li>
123
- <li><a href="/ets">ETS <span class="model-tag tag-ets">Smoothing</span></a></li>
124
- <li><a href="/hybrid">Hybrid <span class="model-tag" style="background: rgba(236, 72, 153, 0.15); color: #f472b6;">Ensemble</span></a></li>
125
- <li><a href="/timesfm">TimesFM <span class="model-tag" style="background: rgba(56, 189, 248, 0.15); color: #7dd3fc;">FM</span></a></li>
126
- </ul>
127
- </div>
128
- </nav>
129
 
130
  <div class="container">
131
  <header>
 
28
  * { box-sizing: border-box; margin: 0; padding: 0; }
29
  body { font-family: 'Outfit', sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; line-height: 1.6; }
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  .container { max-width: 1060px; margin: 0 auto; padding: 2rem; }
32
  header { margin-bottom: 2rem; padding-bottom: 1.25rem; border-bottom: 1px solid var(--border); }
33
  h1 { font-size: 1.75rem; font-weight: 700; letter-spacing: -0.02em; background: linear-gradient(135deg, #fff 0%, #c4b5fd 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
 
93
  .spinner { display: inline-block; width: 1em; height: 1em; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: spin 0.6s linear infinite; vertical-align: -0.15em; margin-right: 0.4em; }
94
  @keyframes spin { to { transform: rotate(360deg); } }
95
  </style>
96
+ <script defer src="/static/nav.js"></script>
97
  </head>
98
  <body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
  <div class="container">
101
  <header>
static/timesfm.html CHANGED
@@ -38,70 +38,6 @@
38
  }
39
 
40
  /* ---- 네비게이션 ---- */
41
- .top-nav {
42
- background: var(--surface);
43
- border-bottom: 1px solid var(--border);
44
- position: sticky;
45
- top: 0;
46
- z-index: 100;
47
- }
48
- .nav-inner {
49
- max-width: 1060px;
50
- margin: 0 auto;
51
- padding: 0 2rem;
52
- display: flex;
53
- align-items: center;
54
- gap: 2rem;
55
- height: 56px;
56
- overflow-x: auto;
57
- }
58
- .nav-brand {
59
- font-weight: 700;
60
- font-size: 1rem;
61
- color: var(--text);
62
- text-decoration: none;
63
- letter-spacing: -0.01em;
64
- white-space: nowrap;
65
- }
66
- .nav-links {
67
- display: flex;
68
- gap: 0.25rem;
69
- list-style: none;
70
- }
71
- .nav-links a {
72
- display: flex;
73
- align-items: center;
74
- gap: 0.4rem;
75
- padding: 0.5rem 0.75rem;
76
- border-radius: 8px;
77
- text-decoration: none;
78
- font-size: 0.82rem;
79
- font-weight: 500;
80
- white-space: nowrap;
81
- color: var(--text-muted);
82
- transition: all 0.15s;
83
- }
84
- .nav-links a:hover {
85
- color: var(--text);
86
- background: var(--surface-hover);
87
- }
88
- .nav-links a.active {
89
- color: var(--sky);
90
- background: rgba(56, 189, 248, 0.1);
91
- }
92
- .nav-links .model-tag {
93
- font-size: 0.6rem;
94
- padding: 0.1rem 0.35rem;
95
- border-radius: 4px;
96
- font-weight: 600;
97
- text-transform: uppercase;
98
- letter-spacing: 0.03em;
99
- }
100
- .tag-dl { background: rgba(99, 102, 241, 0.15); color: #a5b4fc; }
101
- .tag-ets { background: rgba(16, 185, 129, 0.15); color: #6ee7b7; }
102
- .tag-theta{ background: rgba(168, 85, 247, 0.15); color: #c4b5fd; }
103
- .tag-stat { background: rgba(34, 197, 94, 0.15); color: #86efac; }
104
- .tag-ml { background: rgba(245, 158, 11, 0.15); color: #fcd34d; }
105
  .tag-fm { background: rgba(56, 189, 248, 0.15); color: #7dd3fc; }
106
 
107
  .container {
@@ -333,26 +269,10 @@
333
  .result-table tr:hover td { background: rgba(255,255,255,0.02); }
334
  .row-future td:first-child { color: var(--sky); }
335
  </style>
 
336
  </head>
337
  <body>
338
 
339
- <nav class="top-nav">
340
- <div class="nav-inner">
341
- <a href="/" class="nav-brand">시계열 예측</a>
342
- <ul class="nav-links">
343
- <li><a href="/">N-BEATS <span class="model-tag tag-dl">DL</span></a></li>
344
- <li><a href="/prophet">Prophet <span class="model-tag tag-stat">Stat</span></a></li>
345
- <li><a href="/xgboost">XGBoost <span class="model-tag tag-ml">ML</span></a></li>
346
- <li><a href="/lightgbm">LightGBM <span class="model-tag tag-ml">ML</span></a></li>
347
- <li><a href="/arima">ARIMA <span class="model-tag tag-stat">Classical</span></a></li>
348
- <li><a href="/theta">Theta <span class="model-tag tag-theta">Classical</span></a></li>
349
- <li><a href="/ets">ETS <span class="model-tag tag-ets">Smoothing</span></a></li>
350
- <li><a href="/hybrid">Hybrid <span class="model-tag" style="background:rgba(236,72,153,0.15);color:#f472b6;">Ensemble</span></a></li>
351
- <li><a href="/timesfm" class="active">TimesFM <span class="model-tag tag-fm">FM</span></a></li>
352
- </ul>
353
- </div>
354
- </nav>
355
-
356
  <div class="container">
357
  <header>
358
  <div>
 
38
  }
39
 
40
  /* ---- 네비게이션 ---- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  .tag-fm { background: rgba(56, 189, 248, 0.15); color: #7dd3fc; }
42
 
43
  .container {
 
269
  .result-table tr:hover td { background: rgba(255,255,255,0.02); }
270
  .row-future td:first-child { color: var(--sky); }
271
  </style>
272
+ <script defer src="/static/nav.js"></script>
273
  </head>
274
  <body>
275
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  <div class="container">
277
  <header>
278
  <div>
static/xgboost.html CHANGED
@@ -28,21 +28,6 @@
28
  * { box-sizing: border-box; margin: 0; padding: 0; }
29
  body { font-family: 'Outfit', sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; line-height: 1.6; }
30
 
31
- .top-nav { background: var(--surface); border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 100; }
32
- .nav-inner { max-width: 1060px; margin: 0 auto; padding: 0 2rem; display: flex; align-items: center; gap: 2rem; height: 56px; overflow-x: auto; }
33
- .nav-brand { font-weight: 700; font-size: 1rem; color: var(--text); text-decoration: none; letter-spacing: -0.01em; white-space: nowrap; }
34
- .nav-links { display: flex; gap: 0.25rem; list-style: none; }
35
- .nav-links a { display: flex; align-items: center; gap: 0.4rem; padding: 0.5rem 0.75rem; border-radius: 8px; text-decoration: none; font-size: 0.82rem; font-weight: 500; color: var(--text-muted); transition: all 0.15s; white-space: nowrap; }
36
- .nav-links a:hover { color: var(--text); background: var(--surface-hover); }
37
- .nav-links a.active { color: var(--accent); background: rgba(245, 158, 11, 0.1); }
38
- .nav-links .model-tag { font-size: 0.6rem; padding: 0.1rem 0.35rem; border-radius: 4px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; }
39
- .tag-dl { background: rgba(99, 102, 241, 0.15); color: #a5b4fc; }
40
- .tag-stat { background: rgba(34, 197, 94, 0.15); color: #86efac; }
41
- .tag-ml { background: rgba(245, 158, 11, 0.15); color: #fcd34d; }
42
- .tag-classic { background: rgba(6, 182, 212, 0.15); color: #67e8f9; }
43
- .tag-ets { background: rgba(16, 185, 129, 0.15); color: #6ee7b7; }
44
- .tag-theta { background: rgba(168, 85, 247, 0.15); color: #c4b5fd; }
45
-
46
  .container { max-width: 1060px; margin: 0 auto; padding: 2rem; }
47
  header { margin-bottom: 2rem; padding-bottom: 1.25rem; border-bottom: 1px solid var(--border); }
48
  h1 { font-size: 1.75rem; font-weight: 700; letter-spacing: -0.02em; background: linear-gradient(135deg, #fff 0%, #fcd34d 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
@@ -106,24 +91,9 @@
106
  .spinner { display: inline-block; width: 1em; height: 1em; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: spin 0.6s linear infinite; vertical-align: -0.15em; margin-right: 0.4em; }
107
  @keyframes spin { to { transform: rotate(360deg); } }
108
  </style>
 
109
  </head>
110
  <body>
111
- <nav class="top-nav">
112
- <div class="nav-inner">
113
- <a href="/" class="nav-brand">시계열 예측</a>
114
- <ul class="nav-links">
115
- <li><a href="/">N-BEATS <span class="model-tag tag-dl">DL</span></a></li>
116
- <li><a href="/prophet">Prophet <span class="model-tag tag-stat">Stat</span></a></li>
117
- <li><a href="/xgboost" class="active">XGBoost <span class="model-tag tag-ml">ML</span></a></li>
118
- <li><a href="/lightgbm">LightGBM <span class="model-tag tag-ml">ML</span></a></li>
119
- <li><a href="/arima">ARIMA <span class="model-tag tag-classic">Classical</span></a></li>
120
- <li><a href="/theta">Theta <span class="model-tag tag-theta">Classical</span></a></li>
121
- <li><a href="/ets">ETS <span class="model-tag tag-ets">Smoothing</span></a></li>
122
- <li><a href="/hybrid">Hybrid <span class="model-tag" style="background: rgba(236, 72, 153, 0.15); color: #f472b6;">Ensemble</span></a></li>
123
- <li><a href="/timesfm">TimesFM <span class="model-tag" style="background: rgba(56, 189, 248, 0.15); color: #7dd3fc;">FM</span></a></li>
124
- </ul>
125
- </div>
126
- </nav>
127
 
128
  <div class="container">
129
  <header>
 
28
  * { box-sizing: border-box; margin: 0; padding: 0; }
29
  body { font-family: 'Outfit', sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; line-height: 1.6; }
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  .container { max-width: 1060px; margin: 0 auto; padding: 2rem; }
32
  header { margin-bottom: 2rem; padding-bottom: 1.25rem; border-bottom: 1px solid var(--border); }
33
  h1 { font-size: 1.75rem; font-weight: 700; letter-spacing: -0.02em; background: linear-gradient(135deg, #fff 0%, #fcd34d 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
 
91
  .spinner { display: inline-block; width: 1em; height: 1em; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: spin 0.6s linear infinite; vertical-align: -0.15em; margin-right: 0.4em; }
92
  @keyframes spin { to { transform: rotate(360deg); } }
93
  </style>
94
+ <script defer src="/static/nav.js"></script>
95
  </head>
96
  <body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
  <div class="container">
99
  <header>