Public Source Viewer
비나래아카이브 개발자 포털
실제 서비스 구조를 살펴볼 수 있는 공개용 코드 뷰어입니다. 인증, 세션, 외부 연동, 토큰, 관리자 식별 등 보안상 민감한 구현은 파일 단위 또는 줄 단위로 검열됩니다.
view/hinana/main.ejs
공개 가능
1
<!DOCTYPE html>
2
<html lang="ko" xmlns="http://www.w3.org/1999/xhtml">
3
<head>
4
<meta charset="utf-8" />
5
<meta name="color-scheme" content="light dark">
6
<meta name="viewport" content="width=device-width, initial-scale=1">
7
<link rel="manifest" href="/manifest.json">
8
<meta name="theme-color" content="<%= (typeof theme !== 'undefined' && theme === 'dark') ? '#05070b' : '#f8fbff' %>">
9
<meta name="apple-mobile-web-app-title" content="비나래 아카이브">
10
<meta name="description" content="비나래 아카이브의 첫 화면이에요. 기록, 블로그, 라운지, 갤러리로 바로 이동할 수 있어요.">
11
<meta property="og:type" content="website">
12
<meta property="og:site_name" content="비나래 아카이브">
13
<meta property="og:title" content="비나래 아카이브">
14
<meta property="og:description" content="기록과 작은 기능들이 모여 있는 비나래의 개인 아카이브예요.">
15
<meta property="og:url" content="https://hinana.moe/">
16
<meta property="og:image" content="https://hinana.moe/image/10.png">
17
<meta name="twitter:card" content="summary_large_image">
18
<meta name="twitter:title" content="비나래 아카이브">
19
<meta name="twitter:description" content="기록과 작은 기능들이 모여 있는 비나래의 개인 아카이브예요.">
20
<meta name="twitter:image" content="https://hinana.moe/image/10.png">
21
<title>비나래 아카이브</title>
22
23
<link rel="stylesheet" href="/vendors/bootstrap/css/bootstrap.min.css" />
24
<script src="/vendors/bootstrap/js/bootstrap.min.js"></script>
25
<link rel="stylesheet" href="/css/hinana.css" type="text/css">
26
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css">
27
28
<style>
29
:root {
30
--font: 'Noto Sans KR', system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
31
--bg-main: #f8fbff;
32
--bg-soft: rgba(255, 255, 255, 0.76);
33
--bg-solid: #ffffff;
34
--text-primary: #101828;
35
--text-secondary: #586879;
36
--accent: #1298e8;
37
--mint: #12b981;
38
--rose: #f05272;
39
--gold: #c99735;
40
--border: rgba(15, 23, 42, 0.12);
41
--shadow: 0 22px 70px rgba(15, 23, 42, 0.14);
42
--header: rgba(248, 251, 255, 0.82);
43
}
44
body.dark-mode {
45
--bg-main: #05070b;
46
--bg-soft: rgba(13, 17, 23, 0.76);
47
--bg-solid: #0c1119;
48
--text-primary: #f6f8fb;
49
--text-secondary: #9aa8ba;
50
--accent: #38bdf8;
51
--mint: #34d399;
52
--rose: #fb7185;
53
--gold: #f4c96e;
54
--border: rgba(226, 232, 240, 0.14);
55
--shadow: 0 22px 80px rgba(0, 0, 0, 0.46);
56
--header: rgba(5, 7, 11, 0.82);
57
}
58
* { box-sizing: border-box; }
59
html { scroll-behavior: smooth; }
60
html, body {
61
margin: 0; min-height: 100%;
62
font-family: var(--font);
63
background: var(--bg-main);
64
color: var(--text-primary);
65
overflow-x: hidden;
66
}
67
a { color: inherit; text-decoration: none; }
68
69
.global-header {
70
height: 62px;
71
position: fixed; top: 0; left: 0; right: 0; z-index: 50;
72
display: flex; align-items: center; justify-content: space-between; gap: 18px;
73
padding: 0 22px;
74
background: var(--header);
75
border-bottom: 1px solid var(--border);
76
backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
77
}
78
.header-logo { height: 28px; width: auto; mix-blend-mode: multiply; }
79
body.dark-mode .header-logo { mix-blend-mode: screen; }
80
.header-nav {
81
position: absolute; left: 50%; transform: translateX(-50%);
82
display: flex; align-items: center; gap: 18px;
83
}
84
.nav-link {
85
color: var(--text-secondary); font-weight: 850; font-size: 0.92rem;
86
white-space: nowrap; transition: color 0.18s, transform 0.18s;
87
}
88
.nav-link:hover { color: var(--text-primary); transform: translateY(-1px); }
89
.header-actions { display: flex; align-items: center; gap: 9px; z-index: 2; }
90
.round-btn {
91
width: 36px; height: 36px; border-radius: 50%;
92
border: 1px solid var(--border); background: var(--bg-soft);
93
color: var(--text-secondary);
94
display: inline-flex; align-items: center; justify-content: center;
95
cursor: pointer; transition: transform 0.18s, color 0.18s, border-color 0.18s;
96
}
97
.round-btn:hover { color: var(--accent); border-color: rgba(18,152,232,0.45); transform: translateY(-1px); }
98
.login-pill {
99
min-height: 36px; display: inline-flex; align-items: center; gap: 7px;
100
padding: 0 13px; border-radius: 999px; border: 1px solid var(--border);
101
background: var(--bg-soft); color: var(--text-primary); font-size: 0.82rem; font-weight: 900;
102
}
103
.home-mode-switch {
104
position: fixed;
105
top: 78px;
106
right: 18px;
107
z-index: 45;
108
min-height: 38px;
109
display: inline-flex;
110
align-items: center;
111
gap: 8px;
112
padding: 0 13px;
113
border-radius: 999px;
114
border: 1px solid var(--border);
115
background: var(--bg-soft);
116
color: var(--text-primary);
117
font-size: 0.82rem;
118
font-weight: 950;
119
box-shadow: var(--shadow);
120
backdrop-filter: blur(16px);
121
-webkit-backdrop-filter: blur(16px);
122
transition: transform 0.18s, border-color 0.18s, color 0.18s;
123
}
124
.home-mode-switch:hover {
125
transform: translateY(-2px);
126
border-color: rgba(18,152,232,0.48);
127
color: var(--accent);
128
}
129
130
.home-shell { position: relative; isolation: isolate; }
131
#home-canvas {
132
position: fixed; inset: 0; z-index: -1; width: 100%; height: 100%;
133
pointer-events: none; opacity: 0.75;
134
}
135
.hero {
136
min-height: 100svh;
137
position: relative;
138
display: flex; align-items: flex-end;
139
padding: 100px 0 34px;
140
overflow: hidden;
141
}
142
.hero::before {
143
content: "";
144
position: absolute; inset: 0;
145
background:
146
linear-gradient(90deg, rgba(5,7,11,0.62), rgba(5,7,11,0.16) 48%, rgba(5,7,11,0.48)),
147
linear-gradient(180deg, rgba(5,7,11,0.2), rgba(5,7,11,0.2) 50%, var(--bg-main) 100%),
148
url('/image/10.png') center / cover no-repeat;
149
transform: scale(1.03);
150
z-index: -3;
151
}
152
body:not(.dark-mode) .hero::before {
153
background:
154
linear-gradient(90deg, rgba(255,255,255,0.76), rgba(255,255,255,0.2) 48%, rgba(255,255,255,0.72)),
155
linear-gradient(180deg, rgba(248,251,255,0.05), rgba(248,251,255,0.18) 54%, var(--bg-main) 100%),
156
url('/image/10.png') center / cover no-repeat;
157
}
158
.hero::after {
159
content: "";
160
position: absolute; inset: 0; z-index: -2;
161
background:
162
linear-gradient(90deg, rgba(18,152,232,0.11) 1px, transparent 1px),
163
linear-gradient(180deg, rgba(18,185,129,0.08) 1px, transparent 1px);
164
background-size: 48px 48px;
165
mask-image: linear-gradient(180deg, rgba(0,0,0,0.74), transparent 86%);
166
}
167
.hero-inner {
168
width: min(1220px, calc(100% - 36px));
169
margin: 0 auto;
170
display: grid; grid-template-columns: minmax(0, 1fr) minmax(310px, 450px);
171
gap: clamp(24px, 5vw, 70px);
172
align-items: end;
173
position: relative;
174
z-index: 2;
175
}
176
.hero-copy { padding-bottom: clamp(18px, 4vw, 46px); }
177
.eyebrow {
178
display: inline-flex; align-items: center; gap: 8px;
179
color: var(--gold); font-size: 0.78rem; font-weight: 950;
180
letter-spacing: 0.16em; text-transform: uppercase;
181
margin-bottom: 16px;
182
}
183
.signal {
184
width: 9px; height: 9px; border-radius: 3px; background: var(--mint);
185
box-shadow: 0 0 0 6px rgba(18,185,129,0.16);
186
animation: pulse 1.7s ease-in-out infinite;
187
}
188
.hero-title {
189
margin: 0;
190
max-width: 860px;
191
font-size: clamp(3.1rem, 9.6vw, 8.8rem);
192
line-height: 0.9;
193
letter-spacing: 0;
194
font-weight: 950;
195
}
196
.hero-title span { display: block; }
197
.hero-title .accent {
198
color: transparent;
199
background: linear-gradient(90deg, var(--accent), var(--mint), var(--gold), var(--rose));
200
background-size: 240% 100%;
201
-webkit-background-clip: text; background-clip: text;
202
animation: sweep 7s ease-in-out infinite;
203
}
204
.hero-lead {
205
max-width: 670px;
206
margin: 24px 0 0;
207
color: var(--text-secondary);
208
font-size: clamp(1rem, 1.8vw, 1.2rem);
209
line-height: 1.82;
210
}
211
.hero-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 28px; }
212
.action-btn {
213
min-height: 44px; display: inline-flex; align-items: center; gap: 8px;
214
padding: 0 16px; border-radius: 999px;
215
border: 1px solid var(--border); background: var(--bg-soft);
216
color: var(--text-primary); font-weight: 900; font-size: 0.9rem;
217
backdrop-filter: blur(14px);
218
box-shadow: 0 1px 0 rgba(255,255,255,0.45) inset;
219
transition: transform 0.18s, border-color 0.18s, color 0.18s;
220
}
221
.action-btn.primary { background: #0f1419; color: #fff; border-color: #0f1419; }
222
body.dark-mode .action-btn.primary { background: #f6f8fb; color: #101828; border-color: #f6f8fb; }
223
.action-btn:hover { transform: translateY(-2px); border-color: rgba(18,152,232,0.48); color: var(--accent); }
224
225
.hero-panel {
226
position: relative;
227
min-height: 520px;
228
display: grid; place-items: end center;
229
}
230
.character {
231
position: relative; z-index: 2;
232
width: min(420px, 92vw);
233
filter: drop-shadow(0 30px 35px rgba(0,0,0,0.28));
234
animation: float 5.6s ease-in-out infinite;
235
}
236
.orbit-card {
237
position: absolute; z-index: 3;
238
display: inline-flex; align-items: center; gap: 8px;
239
padding: 9px 11px; border-radius: 8px;
240
border: 1px solid var(--border); background: var(--bg-soft);
241
color: var(--text-primary); font-weight: 950; font-size: 0.78rem;
242
box-shadow: var(--shadow);
243
backdrop-filter: blur(18px);
244
}
245
.orbit-a { top: 17%; left: 0; animation: chip 4.2s ease-in-out infinite; }
246
.orbit-b { top: 43%; right: 2%; animation: chip 4.8s ease-in-out infinite reverse; }
247
.orbit-c { bottom: 5%; left: 11%; animation: chip 5.2s ease-in-out infinite; }
248
.memory-float {
249
position: absolute;
250
z-index: 1;
251
width: var(--memory-size);
252
height: var(--memory-size);
253
left: var(--memory-left);
254
top: var(--memory-top);
255
background: var(--memory-image) center / cover no-repeat;
256
border-radius: 8px;
257
opacity: 0.34;
258
filter: blur(2px) saturate(1.05);
259
box-shadow: 0 18px 44px rgba(15, 23, 42, 0.18);
260
transform: rotate(var(--memory-rotate));
261
animation: memoryDrift var(--memory-duration) ease-in-out infinite;
262
pointer-events: none;
263
}
264
.scan-frame {
265
position: absolute; inset: 12% 6% 5%;
266
border: 1px solid rgba(56,189,248,0.42);
267
border-radius: 8px;
268
overflow: hidden; z-index: 1;
269
}
270
.scan-frame::before {
271
content: ""; position: absolute; left: 0; right: 0; top: -25%;
272
height: 22%; background: linear-gradient(180deg, transparent, rgba(56,189,248,0.28), transparent);
273
animation: scan 4.7s linear infinite;
274
}
275
276
.quick-launch {
277
width: min(1220px, calc(100% - 36px));
278
margin: -26px auto 0;
279
position: relative; z-index: 5;
280
display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
281
gap: 12px;
282
}
283
.launch-card {
284
min-height: 130px; padding: 18px;
285
border: 1px solid var(--border); border-radius: 8px;
286
background: var(--bg-soft);
287
box-shadow: var(--shadow);
288
backdrop-filter: blur(18px);
289
display: flex; flex-direction: column; justify-content: space-between;
290
overflow: hidden;
291
transition: transform 0.18s, border-color 0.18s, background 0.18s;
292
}
293
.launch-card:hover { transform: translateY(-4px); border-color: rgba(18,152,232,0.46); background: var(--bg-solid); }
294
.launch-top { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
295
.launch-icon {
296
width: 38px; height: 38px; border-radius: 8px;
297
display: inline-flex; align-items: center; justify-content: center;
298
background: rgba(18,152,232,0.12); color: var(--accent); font-size: 1.2rem;
299
}
300
.launch-arrow { color: var(--text-secondary); }
301
.launch-title { margin-top: 20px; font-size: 1.12rem; font-weight: 950; }
302
.launch-desc { margin-top: 5px; color: var(--text-secondary); font-size: 0.8rem; line-height: 1.55; }
303
304
.section {
305
width: min(1220px, calc(100% - 36px));
306
margin: 0 auto;
307
padding: clamp(64px, 9vw, 110px) 0;
308
}
309
.section-head {
310
display: flex; align-items: end; justify-content: space-between; gap: 24px;
311
margin-bottom: 24px;
312
}
313
.section-title {
314
margin: 0;
315
font-size: clamp(1.9rem, 4vw, 3.3rem);
316
font-weight: 950;
317
letter-spacing: 0;
318
}
319
.section-copy { margin: 0; max-width: 560px; color: var(--text-secondary); line-height: 1.75; }
320
.feature-grid {
321
display: grid; grid-template-columns: 1.15fr 0.85fr; gap: 14px;
322
}
323
.feature-panel {
324
min-height: 360px;
325
border: 1px solid var(--border); border-radius: 8px;
326
background: var(--bg-soft);
327
box-shadow: 0 1px 0 rgba(255,255,255,0.42) inset;
328
backdrop-filter: blur(18px);
329
overflow: hidden;
330
}
331
.feature-image {
332
position: relative;
333
background: url('/image/9.png') center / cover no-repeat;
334
}
335
.feature-image::after {
336
content: ""; position: absolute; inset: 0;
337
background: linear-gradient(180deg, transparent 38%, rgba(0,0,0,0.62));
338
}
339
.feature-caption {
340
position: absolute; left: 22px; right: 22px; bottom: 20px; z-index: 2;
341
color: #fff;
342
}
343
.feature-caption h3 { margin: 0; font-size: 1.65rem; font-weight: 950; }
344
.feature-caption p { margin: 8px 0 0; color: rgba(255,255,255,0.8); line-height: 1.65; }
345
.feature-list { padding: 18px; display: grid; gap: 10px; }
346
.mini-link {
347
padding: 14px; border-radius: 8px; border: 1px solid var(--border);
348
background: var(--bg-solid);
349
display: flex; align-items: center; justify-content: space-between; gap: 12px;
350
transition: transform 0.18s, color 0.18s;
351
}
352
.mini-link:hover { transform: translateX(3px); color: var(--accent); }
353
.mini-title { font-weight: 950; }
354
.mini-desc { margin-top: 3px; color: var(--text-secondary); font-size: 0.78rem; }
355
356
.site-footer {
357
width: min(1220px, calc(100% - 36px));
358
margin: 0 auto 38px; padding-top: 26px;
359
border-top: 1px solid var(--border);
360
color: var(--text-secondary);
361
display: flex; align-items: center; justify-content: space-between; gap: 20px;
362
font-size: 0.78rem;
363
}
364
.footer-brand { display: flex; align-items: center; gap: 12px; min-width: 0; }
365
.footer-logo { width: 44px; height: 44px; object-fit: contain; opacity: 0.74; mix-blend-mode: multiply; }
366
body.dark-mode .footer-logo { mix-blend-mode: screen; opacity: 0.9; }
367
.footer-title { color: var(--text-primary); font-weight: 950; font-size: 0.9rem; }
368
.footer-links { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; justify-content: flex-end; }
369
.footer-links a { color: var(--text-secondary); font-weight: 850; }
370
.footer-links a:hover { color: var(--accent); }
371
.footer-copy { margin-top: 4px; text-align: right; font-size: 0.72rem; }
372
.footer-version {
373
display: inline-flex; align-items: center; min-height: 20px;
374
padding: 0 8px; margin-right: 8px; border: 1px solid var(--line);
375
border-radius: 999px; color: var(--accent); font-weight: 850;
376
background: var(--bg-panel);
377
}
378
379
.reveal { opacity: 0; transform: translateY(22px); transition: opacity 0.7s ease, transform 0.7s ease; }
380
.reveal.is-visible { opacity: 1; transform: translateY(0); }
381
382
.classic-home {
383
min-height: calc(100svh - 62px);
384
padding: 112px 18px 56px;
385
display: flex;
386
flex-direction: column;
387
align-items: center;
388
justify-content: center;
389
position: relative;
390
overflow: hidden;
391
background:
392
linear-gradient(180deg, rgba(248,251,255,0.86), var(--bg-main)),
393
radial-gradient(circle at 50% 20%, rgba(18,152,232,0.14), transparent 34%);
394
}
395
body.dark-mode .classic-home {
396
background:
397
linear-gradient(180deg, rgba(5,7,11,0.82), var(--bg-main)),
398
radial-gradient(circle at 50% 20%, rgba(56,189,248,0.13), transparent 34%);
399
}
400
.classic-title {
401
margin: 0 0 46px;
402
color: var(--text-primary);
403
font-size: clamp(2.2rem, 6vw, 4.8rem);
404
line-height: 1;
405
font-weight: 950;
406
letter-spacing: 0;
407
text-align: center;
408
}
409
.classic-books {
410
display: flex;
411
justify-content: center;
412
align-items: center;
413
gap: clamp(18px, 4vw, 46px);
414
flex-wrap: wrap;
415
position: relative;
416
z-index: 2;
417
}
418
.classic-book {
419
position: relative;
420
width: 220px;
421
height: 300px;
422
border-radius: 3px 8px 8px 3px;
423
overflow: hidden;
424
display: flex;
425
align-items: center;
426
justify-content: center;
427
background: var(--bg-solid);
428
box-shadow: 12px 18px 38px rgba(15, 23, 42, 0.2);
429
transform: rotate(var(--book-tilt, 0deg));
430
transition: transform 0.22s ease, box-shadow 0.22s ease;
431
}
432
.classic-book:hover {
433
transform: rotate(var(--book-tilt, 0deg)) translateY(-7px) scale(1.03);
434
box-shadow: 16px 26px 54px rgba(15, 23, 42, 0.25);
435
}
436
.classic-book::before {
437
content: "";
438
position: absolute;
439
top: 0; left: 0;
440
width: 28px; height: 100%;
441
background: linear-gradient(180deg, #9f2f2f 0 11%, #f0ca55 11% 15%, #9f2f2f 15% 22%, #f0ca55 22% 26%, #9f2f2f 26% 100%);
442
z-index: 3;
443
}
444
.classic-book::after {
445
content: "";
446
position: absolute;
447
left: 28px; right: 0; bottom: 0;
448
height: 17px;
449
background: repeating-linear-gradient(90deg, #e7e7e7 0 1px, #fafafa 1px 3px);
450
border-top: 1px solid rgba(0,0,0,0.16);
451
z-index: 3;
452
}
453
.classic-book-image {
454
position: absolute; inset: 0;
455
width: 100%; height: 100%;
456
object-fit: cover;
457
filter: saturate(1.04) contrast(1.02);
458
transform: scale(1.04);
459
}
460
.classic-book-shade {
461
position: absolute; inset: 0;
462
background: linear-gradient(180deg, rgba(0,0,0,0.18), rgba(0,0,0,0.52));
463
z-index: 1;
464
}
465
.classic-book-title {
466
position: relative;
467
z-index: 4;
468
color: #fff;
469
text-align: center;
470
font-size: 1.42rem;
471
font-weight: 950;
472
line-height: 1.25;
473
padding: 28px;
474
text-shadow: 0 2px 12px rgba(0,0,0,0.72);
475
}
476
.classic-leaf {
477
position: absolute;
478
top: -180px;
479
width: var(--leaf-size);
480
height: var(--leaf-size);
481
background: var(--leaf-image) center / contain no-repeat;
482
filter: blur(4px);
483
opacity: 0;
484
animation: classicFall var(--leaf-duration) linear infinite;
485
animation-delay: var(--leaf-delay);
486
pointer-events: none;
487
}
488
489
@keyframes sweep { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } }
490
@keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(0.72); } }
491
@keyframes float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-13px); } }
492
@keyframes chip { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-9px); } }
493
@keyframes scan { from { transform: translateY(-10%); } to { transform: translateY(610%); } }
494
@keyframes memoryDrift {
495
0%, 100% { transform: translate3d(0, 0, 0) rotate(var(--memory-rotate)); }
496
50% { transform: translate3d(0, -18px, 0) rotate(calc(var(--memory-rotate) + 4deg)); }
497
}
498
@keyframes classicFall {
499
0% { transform: translate3d(0, -18vh, 0) rotate(-10deg); opacity: 0; }
500
12% { opacity: 0.65; }
501
88% { opacity: 0.42; }
502
100% { transform: translate3d(8vw, 124vh, 0) rotate(14deg); opacity: 0; }
503
}
504
505
@media (max-width: 980px) {
506
.global-header { position: sticky; flex-wrap: wrap; height: auto; min-height: 62px; padding: 10px 16px; }
507
.header-nav {
508
position: static; transform: none; order: 3; width: 100%;
509
justify-content: center; overflow-x: auto; padding-top: 10px;
510
border-top: 1px solid var(--border);
511
}
512
.hero { min-height: auto; padding-top: 36px; }
513
.hero-inner { grid-template-columns: 1fr; }
514
.hero-panel { order: -1; min-height: 430px; }
515
.character { width: min(330px, 84vw); }
516
.quick-launch { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 18px; }
517
.section-head { display: block; }
518
.section-copy { margin-top: 10px; }
519
.feature-grid { grid-template-columns: 1fr; }
520
.home-mode-switch { top: auto; right: 14px; bottom: 14px; }
521
}
522
@media (max-width: 620px) {
523
.hero-inner, .quick-launch, .section, .site-footer { width: min(100% - 24px, 1220px); }
524
.hero-title { font-size: clamp(3rem, 18vw, 5rem); }
525
.hero-panel { min-height: 360px; }
526
.orbit-card { font-size: 0.68rem; padding: 7px 8px; }
527
.orbit-a { left: 3%; top: 10%; }
528
.orbit-b { right: 1%; top: 40%; }
529
.orbit-c { left: 7%; bottom: 1%; }
530
.memory-float { opacity: 0.2; filter: blur(3px) saturate(1.02); }
531
.quick-launch { grid-template-columns: 1fr; }
532
.launch-card { min-height: 112px; }
533
.feature-panel { min-height: 300px; }
534
.classic-book { width: 180px; height: 250px; }
535
.site-footer { flex-direction: column; align-items: flex-start; }
536
.footer-links { justify-content: flex-start; }
537
.footer-copy { text-align: left; }
538
}
539
@media (prefers-reduced-motion: reduce) {
540
*, *::before, *::after {
541
animation-duration: 0.01ms !important;
542
animation-iteration-count: 1 !important;
543
scroll-behavior: auto !important;
544
transition-duration: 0.01ms !important;
545
}
546
#home-canvas { display: none; }
547
}
548
</style>
549
</head>
550
<body class="<%= (typeof theme !== 'undefined' && theme === 'dark') ? 'dark-mode' : '' %>">
551
<header class="global-header">
552
<a href="/hinana/index" aria-label="비나래 아카이브">
553
<img src="/image/<%= (typeof theme !== 'undefined' && theme === 'dark') ? 'archive1.png' : 'archive.png' %>" alt="비나래 아카이브" class="header-logo">
554
</a>
555
<nav class="header-nav">
556
<a class="nav-link" href="/hinana/index">Archive</a>
557
<a class="nav-link" href="/hinana/info">Info</a>
558
<a class="nav-link" href="/hinana/blog">Blog</a>
559
<a class="nav-link" href="/hinana/lounge">Lounge</a>
560
</nav>
561
<div class="header-actions">
562
<% if(username) { %>
563
<a class="login-pill" href="/hinana/userInfo"><i class="bi bi-person-circle"></i><%= username %></a>
564
<% } else { %>
565
<a class="login-pill" href="/login"><i class="bi bi-box-arrow-in-right"></i>Login</a>
566
<% } %>
567
<form action="/toggle-theme" method="POST" style="margin:0;">
568
<button type="submit" class="round-btn" title="테마 변경">
569
<i class="bi <%= (typeof theme !== 'undefined' && theme==='dark') ? 'bi-moon-stars-fill':'bi-sun-fill' %>"></i>
570
</button>
571
</form>
572
</div>
573
</header>
574
575
<% if (typeof classicHome !== 'undefined' && classicHome) { %>
576
<a class="home-mode-switch" href="/"><i class="bi bi-stars"></i>새 현관 보기</a>
577
<% } else { %>
578
<a class="home-mode-switch" href="/classic"><i class="bi bi-clock-history"></i>이전 현관 보기</a>
579
<% } %>
580
581
<% if (typeof classicHome !== 'undefined' && classicHome) { %>
582
<main class="classic-home">
583
<h1 class="classic-title">환영합니다!</h1>
584
<div class="classic-books" aria-label="이전 현관">
585
<a class="classic-book" href="/hinana/index" style="--book-tilt:-4deg;">
586
<img class="classic-book-image" src="/image/1.png" alt="">
587
<span class="classic-book-shade"></span>
588
<span class="classic-book-title">비나래 아카이브</span>
589
</a>
590
<a class="classic-book" href="/hinana/info" style="--book-tilt:2deg;">
591
<img class="classic-book-image" src="/image/2.png" alt="">
592
<span class="classic-book-shade"></span>
593
<span class="classic-book-title">비나래씨는요?</span>
594
</a>
595
<a class="classic-book" href="/hinana/lounge" style="--book-tilt:4deg;">
596
<img class="classic-book-image" src="/image/3.png" alt="">
597
<span class="classic-book-shade"></span>
598
<span class="classic-book-title">비나래 라운지</span>
599
</a>
600
</div>
601
<span class="classic-leaf" style="left:8vw; --leaf-size:220px; --leaf-duration:21s; --leaf-delay:-2s; --leaf-image:url('/image/4.png');"></span>
602
<span class="classic-leaf" style="left:28vw; --leaf-size:250px; --leaf-duration:24s; --leaf-delay:-7s; --leaf-image:url('/image/5.png');"></span>
603
<span class="classic-leaf" style="left:48vw; --leaf-size:210px; --leaf-duration:19s; --leaf-delay:-11s; --leaf-image:url('/image/6.png');"></span>
604
<span class="classic-leaf" style="left:68vw; --leaf-size:240px; --leaf-duration:23s; --leaf-delay:-4s; --leaf-image:url('/image/7.png');"></span>
605
<span class="classic-leaf" style="left:88vw; --leaf-size:280px; --leaf-duration:26s; --leaf-delay:-9s; --leaf-image:url('/image/8.png');"></span>
606
</main>
607
<% } else { %>
608
<main class="home-shell">
609
<canvas id="home-canvas" aria-hidden="true"></canvas>
610
611
<section class="hero">
612
<span class="memory-float" style="--memory-image:url('/image/1.png'); --memory-size:180px; --memory-left:7%; --memory-top:16%; --memory-rotate:-8deg; --memory-duration:7s;"></span>
613
<span class="memory-float" style="--memory-image:url('/image/2.png'); --memory-size:150px; --memory-left:78%; --memory-top:12%; --memory-rotate:9deg; --memory-duration:8s;"></span>
614
<span class="memory-float" style="--memory-image:url('/image/3.png'); --memory-size:210px; --memory-left:66%; --memory-top:66%; --memory-rotate:-5deg; --memory-duration:9s;"></span>
615
<span class="memory-float" style="--memory-image:url('/image/4.png'); --memory-size:130px; --memory-left:18%; --memory-top:70%; --memory-rotate:11deg; --memory-duration:7.5s;"></span>
616
<span class="memory-float" style="--memory-image:url('/image/5.png'); --memory-size:160px; --memory-left:44%; --memory-top:22%; --memory-rotate:4deg; --memory-duration:8.6s;"></span>
617
<div class="hero-inner">
618
<div class="hero-copy reveal">
619
<div class="eyebrow"><span class="signal"></span> Hinana Memory Gateway</div>
620
<h1 class="hero-title">
621
<span>비나래</span>
622
<span class="accent">아카이브</span>
623
</h1>
624
<p class="hero-lead">
625
기록, 블로그, 라운지, 작은 실험들이 한곳에 모여 있어요.
626
오늘은 어디부터 열어볼까요?
627
</p>
628
<div class="hero-actions">
629
<a class="action-btn primary" href="/hinana/index"><i class="bi bi-archive"></i> Archive 열기</a>
630
<a class="action-btn" href="/hinana/info"><i class="bi bi-person-lines-fill"></i> 비나래 소개</a>
631
<a class="action-btn" href="/hinana/blog"><i class="bi bi-journal-text"></i> Blog</a>
632
</div>
633
</div>
634
635
<div class="hero-panel reveal" data-tilt-area>
636
<div class="scan-frame"></div>
637
<img src="/image/hinana_train.png" alt="비나래 아카이브" class="character" data-tilt-layer="0.12">
638
<div class="orbit-card orbit-a"><i class="bi bi-broadcast-pin"></i> archive online</div>
639
<div class="orbit-card orbit-b"><i class="bi bi-stars"></i> fresh signal</div>
640
<div class="orbit-card orbit-c"><i class="bi bi-gem"></i> memory engine</div>
641
</div>
642
</div>
643
</section>
644
645
<section class="quick-launch" aria-label="빠른 이동">
646
<a class="launch-card reveal" href="/hinana/index">
647
<div class="launch-top">
648
<span class="launch-icon"><i class="bi bi-archive"></i></span>
649
<i class="bi bi-arrow-up-right launch-arrow"></i>
650
</div>
651
<div>
652
<div class="launch-title">Archive</div>
653
<div class="launch-desc">짧은 기록과 댓글이 쌓이는 메인 아카이브예요.</div>
654
</div>
655
</a>
656
<a class="launch-card reveal" href="/hinana/blog">
657
<div class="launch-top">
658
<span class="launch-icon"><i class="bi bi-journal-richtext"></i></span>
659
<i class="bi bi-arrow-up-right launch-arrow"></i>
660
</div>
661
<div>
662
<div class="launch-title">Blog</div>
663
<div class="launch-desc">조금 더 긴 글과 도서관 같은 기록을 볼 수 있어요.</div>
664
</div>
665
</a>
666
<a class="launch-card reveal" href="/hinana/lounge">
667
<div class="launch-top">
668
<span class="launch-icon"><i class="bi bi-moon-stars"></i></span>
669
<i class="bi bi-arrow-up-right launch-arrow"></i>
670
</div>
671
<div>
672
<div class="launch-title">Lounge</div>
673
<div class="launch-desc">라운지와 갤러리 쪽으로 이어지는 공간이에요.</div>
674
</div>
675
</a>
676
<a class="launch-card reveal" href="/hinana/gallery">
677
<div class="launch-top">
678
<span class="launch-icon"><i class="bi bi-images"></i></span>
679
<i class="bi bi-arrow-up-right launch-arrow"></i>
680
</div>
681
<div>
682
<div class="launch-title">Gallery</div>
683
<div class="launch-desc">이미지와 라운지 쪽 시각 자료를 둘러볼 수 있어요.</div>
684
</div>
685
</a>
686
</section>
687
688
<section class="section">
689
<div class="section-head reveal">
690
<h2 class="section-title">아카이브 지도</h2>
691
<p class="section-copy">
692
글과 이미지, 작은 도구들이 각자의 방처럼 이어져 있어요.
693
마음이 가는 문을 열고 천천히 둘러보세요.
694
</p>
695
</div>
696
<div class="feature-grid">
697
<a class="feature-panel feature-image reveal" href="/hinana/gallery">
698
<div class="feature-caption">
699
<h3>Gallery</h3>
700
<p>이미지와 브랜드 자산, 라운지 계열의 시각 자료를 둘러볼 수 있어요.</p>
701
</div>
702
</a>
703
<div class="feature-panel feature-list reveal">
704
<a class="mini-link" href="/hinana/archive-search">
705
<div>
706
<div class="mini-title">Archive Search</div>
707
<div class="mini-desc">블로그와 일본어 라운지 기록을 한 번에 검색해요.</div>
708
</div>
709
<i class="bi bi-arrow-right"></i>
710
</a>
711
<a class="mini-link" href="/hinana/developer">
712
<div>
713
<div class="mini-title">Developer</div>
714
<div class="mini-desc">검열된 공개용 코드 뷰어로 프로젝트 구조를 볼 수 있어요.</div>
715
</div>
716
<i class="bi bi-arrow-right"></i>
717
</a>
718
<a class="mini-link" href="/hinana/subway">
719
<div>
720
<div class="mini-title">Subway</div>
721
<div class="mini-desc">지하철 도착/열차 추적 도구로 이동해요.</div>
722
</div>
723
<i class="bi bi-arrow-right"></i>
724
</a>
725
<a class="mini-link" href="/hinana/tetris">
726
<div>
727
<div class="mini-title">Tetris</div>
728
<div class="mini-desc">가볍게 점수를 쌓고 책갈피를 받을 수 있어요.</div>
729
</div>
730
<i class="bi bi-arrow-right"></i>
731
</a>
732
<a class="mini-link" href="/hinana/train">
733
<div>
734
<div class="mini-title">Train</div>
735
<div class="mini-desc">기차 화면을 꾸미는 작은 장난감이에요.</div>
736
</div>
737
<i class="bi bi-arrow-right"></i>
738
</a>
739
</div>
740
</div>
741
</section>
742
</main>
743
<% } %>
744
745
<footer class="site-footer">
746
<div class="footer-brand">
747
<img src="/image/sign.png" alt="비나래" class="footer-logo">
748
<div>
749
<div class="footer-title">비나래 아카이브</div>
750
<div>Archive My Memory</div>
751
</div>
752
</div>
753
<div>
754
<div class="footer-links">
755
<a href="/hinana/index">Archive</a>
756
<a href="/hinana/info">Info</a>
757
<a href="/hinana/blog">Blog</a>
758
<a href="/hinana/lounge">Lounge</a>
759
<a href="/hinana/developer">Developer</a>
760
<a href="https://x.com/NoctchillHinana" target="_blank" rel="noreferrer">X</a>
761
</div>
762
<div class="footer-copy"><span class="footer-version">Ver. 8.0.3.2-Utsumi Aoba</span>ⓒ 2024~2026. 비나래 | hinana.moe</div>
763
</div>
764
</footer>
765
766
<script>
767
if ("serviceWorker" in navigator) { navigator.serviceWorker.register("/sw.js"); }
768
769
(function() {
770
var reduceMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
771
var revealEls = document.querySelectorAll('.reveal');
772
if ('IntersectionObserver' in window) {
773
var observer = new IntersectionObserver(function(entries) {
774
entries.forEach(function(entry) {
775
if (entry.isIntersecting) {
776
entry.target.classList.add('is-visible');
777
observer.unobserve(entry.target);
778
}
779
});
780
}, { threshold: 0.14 });
781
revealEls.forEach(function(el) { observer.observe(el); });
782
} else {
783
revealEls.forEach(function(el) { el.classList.add('is-visible'); });
784
}
785
786
var tiltArea = document.querySelector('[data-tilt-area]');
787
if (tiltArea && !reduceMotion) {
788
tiltArea.addEventListener('mousemove', function(event) {
789
var rect = tiltArea.getBoundingClientRect();
790
var x = (event.clientX - rect.left) / rect.width - 0.5;
791
var y = (event.clientY - rect.top) / rect.height - 0.5;
792
tiltArea.querySelectorAll('[data-tilt-layer]').forEach(function(layer) {
793
var depth = Number(layer.getAttribute('data-tilt-layer') || 0);
794
layer.style.transform = 'translate3d(' + (x * depth * 110) + 'px,' + (y * depth * 110) + 'px,0)';
795
});
796
});
797
tiltArea.addEventListener('mouseleave', function() {
798
tiltArea.querySelectorAll('[data-tilt-layer]').forEach(function(layer) {
799
layer.style.transform = '';
800
});
801
});
802
}
803
804
var canvas = document.getElementById('home-canvas');
805
if (!canvas || reduceMotion) return;
806
var ctx = canvas.getContext('2d');
807
var particles = [];
808
var w = 0;
809
var h = 0;
810
var dpr = Math.min(window.devicePixelRatio || 1, 2);
811
812
function resize() {
813
w = window.innerWidth;
814
h = window.innerHeight;
815
canvas.width = Math.floor(w * dpr);
816
canvas.height = Math.floor(h * dpr);
817
canvas.style.width = w + 'px';
818
canvas.style.height = h + 'px';
819
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
820
var count = Math.min(72, Math.max(28, Math.floor(w * h / 21000)));
821
particles = Array.from({ length: count }, function() {
822
return {
823
x: Math.random() * w,
824
y: Math.random() * h,
825
vx: (Math.random() - 0.5) * 0.25,
826
vy: (Math.random() - 0.5) * 0.25,
827
size: Math.random() * 2 + 1,
828
hue: Math.random() > 0.5 ? 198 : 157
829
};
830
});
831
}
832
833
function draw() {
834
ctx.clearRect(0, 0, w, h);
835
particles.forEach(function(p, index) {
836
p.x += p.vx;
837
p.y += p.vy;
838
if (p.x < -12) p.x = w + 12;
839
if (p.x > w + 12) p.x = -12;
840
if (p.y < -12) p.y = h + 12;
841
if (p.y > h + 12) p.y = -12;
842
ctx.fillStyle = 'hsla(' + p.hue + ', 86%, 56%, 0.42)';
843
ctx.fillRect(p.x, p.y, p.size, p.size);
844
845
for (var j = index + 1; j < particles.length; j++) {
846
var q = particles[j];
847
var dx = p.x - q.x;
848
var dy = p.y - q.y;
849
var dist = Math.sqrt(dx * dx + dy * dy);
850
if (dist < 88) {
851
ctx.strokeStyle = 'rgba(18, 152, 232,' + (0.11 * (1 - dist / 88)) + ')';
852
ctx.lineWidth = 1;
853
ctx.beginPath();
854
ctx.moveTo(p.x, p.y);
855
ctx.lineTo(q.x, q.y);
856
ctx.stroke();
857
}
858
}
859
});
860
requestAnimationFrame(draw);
861
}
862
863
resize();
864
window.addEventListener('resize', resize);
865
draw();
866
})();
867
</script>
868
</body>
869
</html>
870