Public Source Viewer
비나래아카이브 개발자 포털
실제 서비스 구조를 살펴볼 수 있는 공개용 코드 뷰어입니다. 인증, 세션, 외부 연동, 토큰, 관리자 식별 등 보안상 민감한 구현은 파일 단위 또는 줄 단위로 검열됩니다.
view/hinana/info.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') ? '#050505' : '#fbfcff' %>">
9
<meta name="apple-mobile-web-app-title" content="비나래 아카이브">
10
<meta name="description" content="비나래를 소개하는 비나래 아카이브의 인터랙티브 프로필 페이지예요.">
11
<meta property="og:type" content="profile">
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/hinana/info">
16
<meta property="og:image" content="https://hinana.moe/image/hinana_normal.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/hinana_normal.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-family: 'Noto Sans KR', system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
31
--bg-main: #fbfcff;
32
--bg-elevated: rgba(255, 255, 255, 0.78);
33
--bg-solid: #ffffff;
34
--bg-soft: #eef6f2;
35
--text-primary: #111827;
36
--text-secondary: #526171;
37
--accent-color: #0ea5e9;
38
--accent-green: #10b981;
39
--accent-rose: #f43f5e;
40
--accent-gold: #c99333;
41
--border-color: rgba(15, 23, 42, 0.11);
42
--shadow-soft: 0 18px 50px rgba(15, 23, 42, 0.12);
43
--shadow-line: 0 1px 0 rgba(255,255,255,0.65) inset;
44
--header-bg: rgba(251, 252, 255, 0.82);
45
}
46
body.dark-mode {
47
--bg-main: #050505;
48
--bg-elevated: rgba(18, 22, 30, 0.78);
49
--bg-solid: #0d1117;
50
--bg-soft: #111827;
51
--text-primary: #f4f7fb;
52
--text-secondary: #9aa7b7;
53
--accent-color: #38bdf8;
54
--accent-green: #34d399;
55
--accent-rose: #fb7185;
56
--accent-gold: #f5c76b;
57
--border-color: rgba(226, 232, 240, 0.13);
58
--shadow-soft: 0 18px 60px rgba(0, 0, 0, 0.48);
59
--shadow-line: 0 1px 0 rgba(255,255,255,0.07) inset;
60
--header-bg: rgba(5, 5, 5, 0.82);
61
}
62
* { box-sizing: border-box; }
63
html { scroll-behavior: smooth; }
64
html, body {
65
margin: 0; min-height: 100%;
66
font-family: var(--font-family);
67
background: var(--bg-main);
68
color: var(--text-primary);
69
}
70
body {
71
overflow-x: hidden;
72
background-image:
73
linear-gradient(90deg, rgba(14,165,233,0.06) 1px, transparent 1px),
74
linear-gradient(180deg, rgba(16,185,129,0.05) 1px, transparent 1px);
75
background-size: 46px 46px;
76
}
77
body.dark-mode {
78
background-image:
79
linear-gradient(90deg, rgba(56,189,248,0.055) 1px, transparent 1px),
80
linear-gradient(180deg, rgba(52,211,153,0.045) 1px, transparent 1px);
81
}
82
a { color: inherit; text-decoration: none; }
83
84
.global-header {
85
height: 62px;
86
background: var(--header-bg);
87
border-bottom: 1px solid var(--border-color);
88
display: flex; align-items: center; justify-content: space-between; gap: 18px;
89
padding: 0 20px;
90
position: sticky; top: 0; z-index: 1000;
91
backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
92
}
93
.header-brand { display: flex; align-items: center; gap: 15px; min-width: 0; }
94
.header-logo { height: 28px; width: auto; mix-blend-mode: multiply; }
95
body.dark-mode .header-logo { mix-blend-mode: screen; }
96
.header-nav {
97
position: absolute; left: 50%; transform: translateX(-50%);
98
display: flex; gap: 20px; align-items: center;
99
}
100
.nav-link {
101
font-weight: 750; font-size: 0.95rem; color: var(--text-secondary);
102
transition: color 0.18s, transform 0.18s;
103
white-space: nowrap;
104
}
105
.nav-link:hover, .nav-link.active { color: var(--text-primary); transform: translateY(-1px); }
106
.nav-divider { opacity: 0.32; color: var(--text-secondary); }
107
.login-link { color: var(--accent-color); }
108
.header-controls { display: flex; align-items: center; gap: 10px; z-index: 5; }
109
.icon-btn {
110
width: 36px; height: 36px; border-radius: 50%;
111
border: 1px solid var(--border-color); background: var(--bg-elevated);
112
color: var(--text-secondary); display: inline-flex; align-items: center; justify-content: center;
113
cursor: pointer; transition: color 0.18s, transform 0.18s, border-color 0.18s;
114
}
115
.icon-btn:hover { color: var(--accent-color); transform: translateY(-1px); border-color: rgba(14,165,233,0.35); }
116
117
.info-page { position: relative; isolation: isolate; }
118
#spark-canvas {
119
position: fixed; inset: 0; width: 100%; height: 100%;
120
z-index: -1; pointer-events: none; opacity: 0.72;
121
}
122
.hero {
123
min-height: calc(100svh - 62px);
124
display: grid; grid-template-columns: minmax(0, 1.02fr) minmax(320px, 0.98fr);
125
align-items: center; gap: clamp(24px, 5vw, 70px);
126
width: min(1180px, calc(100% - 36px));
127
margin: 0 auto;
128
padding: clamp(38px, 7vw, 86px) 0;
129
}
130
.hero-copy { position: relative; z-index: 2; }
131
.eyebrow {
132
display: inline-flex; align-items: center; gap: 8px;
133
color: var(--accent-gold); font-size: 0.78rem; font-weight: 900;
134
letter-spacing: 0.18em; text-transform: uppercase; margin-bottom: 14px;
135
}
136
.signal-dot {
137
width: 9px; height: 9px; border-radius: 2px; background: var(--accent-green);
138
box-shadow: 0 0 0 6px rgba(16,185,129,0.14);
139
animation: signalPulse 1.8s ease-in-out infinite;
140
}
141
.hero-title {
142
margin: 0;
143
font-size: clamp(3.2rem, 10vw, 8.4rem);
144
line-height: 0.9;
145
font-weight: 950;
146
letter-spacing: 0;
147
}
148
.hero-title .line {
149
display: block;
150
text-shadow: 0 1px 0 rgba(255,255,255,0.45), 0 18px 42px rgba(14,165,233,0.18);
151
}
152
body.dark-mode .hero-title .line { text-shadow: 0 0 24px rgba(56,189,248,0.2); }
153
.hero-title .accent {
154
color: transparent;
155
background: linear-gradient(90deg, var(--accent-color), var(--accent-green), var(--accent-gold), var(--accent-rose));
156
background-size: 220% 100%;
157
-webkit-background-clip: text; background-clip: text;
158
animation: titleSweep 7s ease-in-out infinite;
159
}
160
.hero-lead {
161
margin: 24px 0 0;
162
max-width: 640px;
163
color: var(--text-secondary);
164
font-size: clamp(1rem, 2vw, 1.2rem);
165
line-height: 1.8;
166
}
167
.hero-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 28px; }
168
.action-btn {
169
display: inline-flex; align-items: center; gap: 8px;
170
min-height: 42px; padding: 0 15px; border-radius: 999px;
171
border: 1px solid var(--border-color); background: var(--bg-elevated);
172
color: var(--text-primary); font-size: 0.9rem; font-weight: 850;
173
box-shadow: var(--shadow-line);
174
transition: transform 0.18s, border-color 0.18s, color 0.18s;
175
}
176
.action-btn.primary { background: #0f1419; color: #fff; border-color: #0f1419; }
177
body.dark-mode .action-btn.primary { background: #f4f7fb; color: #111827; border-color: #f4f7fb; }
178
.action-btn:hover { transform: translateY(-2px); border-color: rgba(14,165,233,0.45); color: var(--accent-color); }
179
.hero-stats {
180
margin-top: 30px;
181
display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
182
gap: 10px; max-width: 650px;
183
}
184
.stat-tile {
185
padding: 14px; border-radius: 8px;
186
border: 1px solid var(--border-color); background: var(--bg-elevated);
187
box-shadow: var(--shadow-line);
188
}
189
.stat-value { font-size: 1.08rem; font-weight: 950; color: var(--text-primary); }
190
.stat-label { margin-top: 3px; font-size: 0.73rem; color: var(--text-secondary); font-weight: 750; }
191
192
.hero-visual { position: relative; min-height: min(620px, 75svh); display: grid; place-items: center; }
193
.portrait-stage {
194
position: relative;
195
width: min(480px, 86vw);
196
aspect-ratio: 0.82;
197
transform-style: preserve-3d;
198
perspective: 1200px;
199
}
200
.scene-panel {
201
position: absolute; inset: 5% 3% 10% 8%;
202
border-radius: 8px; overflow: hidden;
203
border: 1px solid var(--border-color);
204
box-shadow: var(--shadow-soft);
205
transform: rotate(-4deg) translateZ(-24px);
206
background: var(--bg-soft);
207
}
208
.scene-panel img { width: 100%; height: 100%; object-fit: cover; filter: saturate(1.05) contrast(1.02); }
209
.portrait-card {
210
position: absolute; inset: 0 8% 0 4%;
211
display: grid; place-items: end center;
212
transform: rotate(2.5deg) translateZ(44px);
213
}
214
.portrait-card::before {
215
content: "";
216
position: absolute; inset: 10% 10% 4%;
217
border: 1px solid rgba(14,165,233,0.42);
218
border-radius: 8px;
219
transform: translate(18px, 18px);
220
}
221
.portrait-card img {
222
position: relative; z-index: 2;
223
width: 100%; height: 100%; object-fit: contain; object-position: bottom center;
224
filter: drop-shadow(0 28px 34px rgba(15, 23, 42, 0.22));
225
animation: floatPortrait 5s ease-in-out infinite;
226
}
227
.scanner {
228
position: absolute; inset: 10% 11% 4% 7%;
229
z-index: 3; pointer-events: none; border-radius: 8px; overflow: hidden;
230
mix-blend-mode: screen;
231
}
232
.scanner::before {
233
content: ""; position: absolute; left: 0; right: 0; top: -20%;
234
height: 24%; background: linear-gradient(180deg, transparent, rgba(56,189,248,0.28), transparent);
235
animation: scanLine 4.8s linear infinite;
236
}
237
.floating-chip {
238
position: absolute; z-index: 4;
239
display: inline-flex; align-items: center; gap: 7px;
240
padding: 8px 10px; border-radius: 8px;
241
border: 1px solid var(--border-color); background: var(--bg-elevated);
242
box-shadow: var(--shadow-soft), var(--shadow-line);
243
color: var(--text-primary); font-size: 0.78rem; font-weight: 900;
244
backdrop-filter: blur(14px);
245
}
246
.chip-a { top: 11%; left: 0; animation: floatChip 4.4s ease-in-out infinite; }
247
.chip-b { right: 0; top: 45%; animation: floatChip 5s ease-in-out infinite reverse; }
248
.chip-c { left: 11%; bottom: 4%; animation: floatChip 4.7s ease-in-out infinite; }
249
250
.marquee-band {
251
border-block: 1px solid var(--border-color);
252
background: var(--bg-elevated);
253
overflow: hidden;
254
backdrop-filter: blur(12px);
255
}
256
.marquee-track {
257
display: flex; gap: 28px; width: max-content;
258
padding: 12px 0;
259
color: var(--text-secondary); font-size: 0.78rem; font-weight: 900;
260
letter-spacing: 0.18em; text-transform: uppercase;
261
animation: marquee 28s linear infinite;
262
}
263
.marquee-track span { white-space: nowrap; }
264
265
.section {
266
width: min(1180px, calc(100% - 36px));
267
margin: 0 auto;
268
padding: clamp(54px, 8vw, 94px) 0;
269
}
270
.section-head {
271
display: flex; align-items: flex-end; justify-content: space-between; gap: 22px;
272
margin-bottom: 24px;
273
}
274
.section-title { margin: 0; font-size: clamp(1.8rem, 4vw, 3.1rem); font-weight: 950; letter-spacing: 0; }
275
.section-desc { max-width: 560px; color: var(--text-secondary); line-height: 1.7; margin: 0; }
276
.profile-grid {
277
display: grid; grid-template-columns: repeat(12, 1fr); gap: 14px;
278
}
279
.glass-panel {
280
border: 1px solid var(--border-color);
281
background: var(--bg-elevated);
282
border-radius: 8px;
283
box-shadow: var(--shadow-line);
284
backdrop-filter: blur(18px);
285
overflow: hidden;
286
}
287
.identity-panel { grid-column: span 5; padding: 22px; }
288
.about-panel { grid-column: span 7; padding: 22px; }
289
.wide-panel { grid-column: span 12; padding: 0; }
290
.panel-label {
291
color: var(--accent-color); font-size: 0.72rem; font-weight: 950;
292
letter-spacing: 0.16em; text-transform: uppercase; margin-bottom: 12px;
293
}
294
.identity-name { font-size: clamp(2rem, 5vw, 4.1rem); line-height: 0.95; font-weight: 950; margin: 0 0 18px; }
295
.tag-row { display: flex; flex-wrap: wrap; gap: 8px; }
296
.tag {
297
display: inline-flex; align-items: center; gap: 6px;
298
min-height: 30px; padding: 0 10px; border-radius: 999px;
299
border: 1px solid var(--border-color); background: var(--bg-solid);
300
color: var(--text-secondary); font-size: 0.78rem; font-weight: 800;
301
}
302
.about-text { color: var(--text-secondary); font-size: 1rem; line-height: 1.85; margin: 0; }
303
.link-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); border-top: 1px solid var(--border-color); }
304
.social-link {
305
min-height: 110px; padding: 18px;
306
display: flex; flex-direction: column; justify-content: space-between;
307
border-right: 1px solid var(--border-color);
308
transition: background 0.18s, color 0.18s;
309
}
310
.social-link:last-child { border-right: none; }
311
.social-link:hover { background: rgba(14,165,233,0.08); color: var(--accent-color); }
312
.social-link i { font-size: 1.35rem; }
313
.social-label { font-size: 0.78rem; color: var(--text-secondary); font-weight: 850; }
314
.social-value { font-weight: 950; overflow-wrap: anywhere; }
315
316
.timeline {
317
display: grid; grid-template-columns: 0.9fr 1.1fr; gap: 16px; align-items: stretch;
318
}
319
.motion-frame {
320
min-height: 460px; position: relative; overflow: hidden;
321
background: var(--bg-soft);
322
}
323
.motion-frame img {
324
width: 100%; height: 100%; min-height: 460px; object-fit: cover;
325
transform: scale(1.05);
326
transition: transform 1.2s ease;
327
}
328
.motion-frame.is-visible img { transform: scale(1); }
329
.motion-frame::after {
330
content: ""; position: absolute; inset: 0;
331
background:
332
linear-gradient(180deg, transparent 55%, rgba(0,0,0,0.38)),
333
repeating-linear-gradient(0deg, rgba(255,255,255,0.10) 0 1px, transparent 1px 9px);
334
pointer-events: none;
335
}
336
.timeline-list { display: grid; gap: 12px; }
337
.timeline-item {
338
position: relative; padding: 18px 18px 18px 52px;
339
border: 1px solid var(--border-color); border-radius: 8px;
340
background: var(--bg-elevated); box-shadow: var(--shadow-line);
341
overflow: hidden;
342
}
343
.timeline-item::before {
344
content: ""; position: absolute; left: 20px; top: 20px;
345
width: 14px; height: 14px; border-radius: 4px;
346
background: var(--accent-color); box-shadow: 0 0 0 7px rgba(14,165,233,0.12);
347
}
348
.timeline-item:nth-child(2)::before { background: var(--accent-green); box-shadow: 0 0 0 7px rgba(16,185,129,0.12); }
349
.timeline-item:nth-child(3)::before { background: var(--accent-gold); box-shadow: 0 0 0 7px rgba(201,147,51,0.14); }
350
.timeline-item:nth-child(4)::before { background: var(--accent-rose); box-shadow: 0 0 0 7px rgba(244,63,94,0.12); }
351
.timeline-title { font-weight: 950; margin-bottom: 6px; }
352
.timeline-copy { color: var(--text-secondary); line-height: 1.65; font-size: 0.92rem; }
353
354
.status-strip {
355
display: grid; grid-template-columns: 1.05fr 0.95fr; gap: 16px; align-items: stretch;
356
}
357
.status-card { padding: 22px; }
358
.status-now {
359
display: flex; align-items: center; justify-content: space-between; gap: 14px;
360
padding: 16px; border-radius: 8px; background: var(--bg-solid); border: 1px solid var(--border-color);
361
}
362
.status-heading {
363
display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
364
margin-bottom: 4px;
365
}
366
.status-updated {
367
color: var(--text-secondary);
368
font-size: 0.72rem;
369
font-weight: 800;
370
opacity: 0.78;
371
white-space: nowrap;
372
}
373
.status-text { font-size: clamp(1.2rem, 3vw, 2.3rem); font-weight: 950; }
374
.mini-terminal {
375
min-height: 260px; padding: 18px;
376
background: #0f172a; color: #dbeafe;
377
font-family: Consolas, "SFMono-Regular", Menlo, monospace;
378
font-size: 0.86rem; line-height: 1.7;
379
}
380
.terminal-line { display: block; }
381
.terminal-prompt { color: #38bdf8; }
382
.terminal-green { color: #34d399; }
383
.terminal-gold { color: #facc15; }
384
.cursor {
385
display: inline-block; width: 8px; height: 1em; margin-left: 3px;
386
background: #dbeafe; vertical-align: -2px;
387
animation: blink 1s steps(2, start) infinite;
388
}
389
390
.site-footer {
391
width: min(1180px, calc(100% - 36px));
392
margin: 0 auto 38px; padding-top: 28px;
393
border-top: 1px solid var(--border-color);
394
color: var(--text-secondary);
395
display: flex; align-items: center; justify-content: space-between; gap: 20px;
396
font-size: 0.78rem;
397
}
398
.footer-brand { display: flex; align-items: center; gap: 12px; min-width: 0; }
399
.footer-logo { width: 44px; height: 44px; object-fit: contain; opacity: 0.76; mix-blend-mode: multiply; }
400
body.dark-mode .footer-logo { mix-blend-mode: screen; opacity: 0.88; }
401
.footer-title { color: var(--text-primary); font-weight: 900; font-size: 0.9rem; }
402
.footer-links { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; justify-content: flex-end; }
403
.footer-links a { color: var(--text-secondary); font-weight: 800; }
404
.footer-links a:hover { color: var(--accent-color); }
405
.footer-copy { margin-top: 4px; text-align: right; font-size: 0.72rem; }
406
.footer-version {
407
display: inline-flex; align-items: center; min-height: 20px;
408
padding: 0 8px; margin-right: 8px; border: 1px solid var(--border-color);
409
border-radius: 999px; color: var(--accent-color); font-weight: 850;
410
background: var(--bg-card);
411
}
412
413
.reveal { opacity: 0; transform: translateY(22px); transition: opacity 0.72s ease, transform 0.72s ease; }
414
.reveal.is-visible { opacity: 1; transform: translateY(0); }
415
416
@keyframes titleSweep { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } }
417
@keyframes signalPulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(0.7); } }
418
@keyframes floatPortrait { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-12px); } }
419
@keyframes scanLine { from { transform: translateY(-10%); } to { transform: translateY(520%); } }
420
@keyframes floatChip { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } }
421
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
422
@keyframes blink { 50% { opacity: 0; } }
423
424
@media (max-width: 980px) {
425
.global-header { flex-wrap: wrap; height: auto; min-height: 62px; padding: 10px 16px; }
426
.header-nav {
427
position: static; transform: none; width: 100%; order: 3;
428
justify-content: center; gap: 16px; padding-top: 10px;
429
border-top: 1px solid var(--border-color); overflow-x: auto;
430
}
431
.header-brand { flex: 1; }
432
.hero { grid-template-columns: 1fr; padding-top: 34px; }
433
.hero-visual { min-height: 520px; order: -1; }
434
.hero-stats { grid-template-columns: 1fr; }
435
.section-head { display: block; }
436
.section-desc { margin-top: 10px; }
437
.identity-panel, .about-panel, .wide-panel { grid-column: span 12; }
438
.timeline, .status-strip { grid-template-columns: 1fr; }
439
.link-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
440
.social-link:nth-child(2) { border-right: none; }
441
.social-link:nth-child(1), .social-link:nth-child(2) { border-bottom: 1px solid var(--border-color); }
442
}
443
@media (max-width: 620px) {
444
body { background-size: 32px 32px; }
445
.hero, .section, .site-footer { width: min(100% - 24px, 1180px); }
446
.hero-title { font-size: clamp(3rem, 19vw, 5.2rem); }
447
.hero-visual { min-height: 410px; }
448
.portrait-stage { width: min(360px, 92vw); }
449
.floating-chip { font-size: 0.68rem; padding: 7px 8px; }
450
.chip-a { top: 6%; left: 4%; }
451
.chip-b { right: 2%; top: 42%; }
452
.chip-c { left: 6%; bottom: 1%; }
453
.link-grid { grid-template-columns: 1fr; }
454
.social-link { border-right: none; border-bottom: 1px solid var(--border-color); }
455
.social-link:last-child { border-bottom: none; }
456
.motion-frame, .motion-frame img { min-height: 320px; }
457
.status-now { display: block; }
458
.status-now .action-btn { margin-top: 12px; }
459
.site-footer { flex-direction: column; align-items: flex-start; }
460
.footer-links { justify-content: flex-start; }
461
.footer-copy { text-align: left; }
462
}
463
@media (prefers-reduced-motion: reduce) {
464
*, *::before, *::after {
465
animation-duration: 0.01ms !important;
466
animation-iteration-count: 1 !important;
467
scroll-behavior: auto !important;
468
transition-duration: 0.01ms !important;
469
}
470
#spark-canvas { display: none; }
471
}
472
</style>
473
</head>
474
475
<body class="<%= (typeof theme !== 'undefined' && theme === 'dark') ? 'dark-mode' : '' %>">
476
<header class="global-header">
477
<div class="header-brand">
478
<a href="/hinana/index">
479
<img src="/image/<%= (typeof theme !== 'undefined' && theme === 'dark') ? 'archive1.png' : 'archive.png' %>" alt="비나래 아카이브" class="header-logo">
480
</a>
481
</div>
482
<nav class="header-nav">
483
<a href="/hinana/index" class="nav-link">Archive</a>
484
<a href="/hinana/info" class="nav-link active">Info</a>
485
<a href="/hinana/blog" class="nav-link">Blog</a>
486
<a href="/hinana/lounge" class="nav-link">Lounge</a>
487
<span class="nav-divider">|</span>
488
<% if(username) { %>
489
<a href="/logout?redirect=/hinana/info" class="nav-link text-danger fw-bold">Logout</a>
490
<% } else { %>
491
<a href="/login?redirect=/hinana/info" class="nav-link login-link fw-bold">Login</a>
492
<% } %>
493
</nav>
494
<div class="header-controls">
495
<a href="/hinana/gallery#brand-assets" class="nav-link" style="font-size:0.9rem;">사이트 맵</a>
496
<form action="/toggle-theme" method="POST" class="d-inline">
497
<button type="submit" class="icon-btn" title="테마 변경">
498
<i class="bi <%= (typeof theme !== 'undefined' && theme==='dark') ? 'bi-moon-stars-fill':'bi-sun-fill' %>"></i>
499
</button>
500
</form>
501
</div>
502
</header>
503
504
<main class="info-page">
505
<canvas id="spark-canvas" aria-hidden="true"></canvas>
506
507
<section class="hero">
508
<div class="hero-copy reveal">
509
<div class="eyebrow"><span class="signal-dot"></span> Hinana Archive Identity</div>
510
<h1 class="hero-title">
511
<span class="line">비나래</span>
512
<span class="line accent">예요.</span>
513
</h1>
514
<p class="hero-lead">
515
샤니마스와 이치카와 히나나를 좋아하고, 기록과 작은 기능들을 직접 만들며 쌓아두는 사람이에요.
516
이 페이지는 비나래라는 계정과 취향, 그리고 hinana.moe가 어떤 분위기로 굴러가는지 보여주는 소개 화면이에요.
517
</p>
518
<div class="hero-actions">
519
<a class="action-btn primary" href="https://x.com/NoctchillHinana" target="_blank" rel="noreferrer">
520
<i class="bi bi-twitter-x"></i> X 보기
521
</a>
522
<a class="action-btn" href="/hinana/blog">
523
<i class="bi bi-journal-text"></i> Blog
524
</a>
525
<a class="action-btn" href="/hinana/lounge">
526
<i class="bi bi-stars"></i> Lounge
527
</a>
528
</div>
529
<div class="hero-stats">
530
<div class="stat-tile">
531
<div class="stat-value">hinana.moe</div>
532
<div class="stat-label">개인 아카이브 도메인</div>
533
</div>
534
<div class="stat-tile">
535
<div class="stat-value">fresh signal</div>
536
<div class="stat-label">오늘의 분위기</div>
537
</div>
538
<div class="stat-tile">
539
<div class="stat-value"><%= username ? username : 'Guest' %></div>
540
<div class="stat-label">현재 방문자</div>
541
</div>
542
</div>
543
</div>
544
545
<div class="hero-visual reveal" data-parallax-root>
546
<div class="portrait-stage">
547
<div class="scene-panel" data-parallax="0.08">
548
<img src="/image/scenery10.png" alt="비나래 아카이브 배경">
549
</div>
550
<div class="portrait-card" data-parallax="0.14">
551
<img src="/image/hinana_normal.png" alt="비나래">
552
<div class="scanner"></div>
553
</div>
554
<div class="floating-chip chip-a"><i class="bi bi-broadcast-pin"></i> Archive Online</div>
555
<div class="floating-chip chip-b"><i class="bi bi-controller"></i> VRChat / Rhythm</div>
556
<div class="floating-chip chip-c"><i class="bi bi-gem"></i> Memory Engine</div>
557
</div>
558
</div>
559
</section>
560
561
<div class="marquee-band" aria-hidden="true">
562
<div class="marquee-track">
563
<span>BINARAE ARCHIVE</span><span>NOCTCHILL</span><span>ICHIKAWA HINANA</span><span>VRCHAT</span><span>MEMORY LOG</span><span>PUBLIC SOURCE VIEWER</span>
564
<span>BINARAE ARCHIVE</span><span>NOCTCHILL</span><span>ICHIKAWA HINANA</span><span>VRCHAT</span><span>MEMORY LOG</span><span>PUBLIC SOURCE VIEWER</span>
565
</div>
566
</div>
567
568
<section class="section" id="profile">
569
<div class="section-head reveal">
570
<h2 class="section-title">소개 신호</h2>
571
<p class="section-desc">좋아하는 것, 굴러가는 계정, 그리고 이 사이트가 가진 분위기를 한 화면에 담아봤어요.</p>
572
</div>
573
<div class="profile-grid">
574
<article class="glass-panel identity-panel reveal">
575
<div class="panel-label">Profile</div>
576
<h3 class="identity-name">비나래</h3>
577
<div class="tag-row">
578
<span class="tag"><i class="bi bi-music-note-beamed"></i> 샤니마스</span>
579
<span class="tag"><i class="bi bi-stars"></i> 히나나</span>
580
<span class="tag"><i class="bi bi-vr"></i> VRChat</span>
581
<span class="tag"><i class="bi bi-terminal"></i> 개인 개발</span>
582
<span class="tag"><i class="bi bi-journal-richtext"></i> 기록</span>
583
</div>
584
</article>
585
<article class="glass-panel about-panel reveal">
586
<div class="panel-label">About</div>
587
<p class="about-text">
588
이치카와 히나나를 좋아하는 사람이에요. 좋아하는 것을 그냥 좋아하는 데서 멈추지 않고,
589
작은 페이지와 도구, 기록 시스템으로 남겨두는 쪽을 더 좋아해요.
590
hinana.moe는 그런 취향이 쌓인 개인 아카이브이자, 놀이터이자, 가끔은 실험실이에요.
591
</p>
592
</article>
593
<article class="glass-panel wide-panel reveal">
594
<div class="link-grid">
595
<a class="social-link" href="https://x.com/NoctchillHinana" target="_blank" rel="noreferrer">
596
<i class="bi bi-twitter-x"></i>
597
<div>
598
<div class="social-label">X / Twitter</div>
599
<div class="social-value">@NoctchillHinana</div>
600
</div>
601
</a>
602
<a class="social-link" href="https://vrchat.com/home/user/usr_0082e06a-7f20-46e9-97e5-ef253083ba13" target="_blank" rel="noreferrer">
603
<i class="bi bi-badge-vr"></i>
604
<div>
605
<div class="social-label">VRChat</div>
606
<div class="social-value">morikubo</div>
607
</div>
608
</a>
609
<div class="social-link">
610
<i class="bi bi-discord"></i>
611
<div>
612
<div class="social-label">Discord</div>
613
<div class="social-value">ichikawahinana</div>
614
</div>
615
</div>
616
<a class="social-link" href="mailto:noctchillhinana@gmail.com">
617
<i class="bi bi-envelope"></i>
618
<div>
619
<div class="social-label">Email</div>
620
<div class="social-value">noctchillhinana@gmail.com</div>
621
</div>
622
</a>
623
</div>
624
</article>
625
</div>
626
</section>
627
628
<section class="section">
629
<div class="section-head reveal">
630
<h2 class="section-title">취향 로그</h2>
631
<p class="section-desc">한 사람을 설명하는 건 한 문장보다 반복되는 취향과 습관 쪽에 더 가까워요.</p>
632
</div>
633
<div class="timeline">
634
<div class="glass-panel motion-frame reveal">
635
<img src="/image/scenery11.png" alt="비나래 아카이브 풍경">
636
</div>
637
<div class="timeline-list">
638
<div class="timeline-item reveal">
639
<div class="timeline-title">샤니마스와 히나나</div>
640
<div class="timeline-copy">이 사이트의 이름과 분위기를 만든 가장 큰 취향이에요. 귀엽고 밝은 것, 그런데 어딘가 오래 남는 감각을 좋아해요.</div>
641
</div>
642
<div class="timeline-item reveal">
643
<div class="timeline-title">VRChat</div>
644
<div class="timeline-copy">사람을 만나고, 공간을 돌아다니고, 계정의 분위기를 조금씩 쌓아가는 쪽의 취미예요.</div>
645
</div>
646
<div class="timeline-item reveal">
647
<div class="timeline-title">리듬게임과 여러 게임</div>
648
<div class="timeline-copy">반복해서 손에 익히는 플레이를 좋아해요. 가볍게 켜도 결국 오래 붙잡게 되는 편이에요.</div>
649
</div>
650
<div class="timeline-item reveal">
651
<div class="timeline-title">기록과 기능 만들기</div>
652
<div class="timeline-copy">아카이브, 블로그, 라운지, 모니터링, AI 도구처럼 필요한 것을 직접 만들며 사이트에 붙여두는 걸 좋아해요.</div>
653
</div>
654
</div>
655
</div>
656
</section>
657
658
<%
659
const formatSignalUpdatedAgo = (updatedAt) => {
660
const updatedDate = updatedAt ? new Date(updatedAt) : null;
661
if (!updatedDate || Number.isNaN(updatedDate.getTime()) || updatedDate.getFullYear() <= 2000) return null;
662
663
const diffMs = Date.now() - updatedDate.getTime();
664
const diffMinutes = Math.max(0, Math.floor(diffMs / 60000));
665
if (diffMinutes < 1) return '방금 전';
666
if (diffMinutes < 60) return `${diffMinutes}분 전`;
667
668
const diffHours = Math.floor(diffMinutes / 60);
669
if (diffHours < 24) return `${diffHours}시간 전`;
670
671
const diffDays = Math.floor(diffHours / 24);
672
if (diffDays < 30) return `${diffDays}일 전`;
673
674
const diffMonths = Math.floor(diffDays / 30);
675
if (diffMonths < 12) return `${diffMonths}개월 전`;
676
677
return `${Math.floor(diffMonths / 12)}년 전`;
678
};
679
const binaraeSignalUpdatedAgo = formatSignalUpdatedAgo(typeof binaraeSignal !== 'undefined' ? binaraeSignal.updatedAt : null);
680
%>
681
682
<section class="section">
683
<div class="status-strip">
684
<article class="glass-panel status-card reveal">
685
<div class="panel-label">Fresh Signal</div>
686
<div class="status-now">
687
<div>
688
<div class="status-heading">
689
<div class="social-label">오늘의 신호</div>
690
<% if (binaraeSignalUpdatedAgo) { %>
691
<div class="status-updated">최근 변경 : <%= binaraeSignalUpdatedAgo %></div>
692
<% } %>
693
</div>
694
<div class="status-text"><%= (typeof binaraeSignal !== 'undefined' && binaraeSignal.statusText) ? binaraeSignal.statusText : '파릇하게 기록 중' %></div>
695
</div>
696
<% if(username) { %>
697
<a class="action-btn" href="/hinana/userInfo">
698
<% if (typeof currentUserProfileImage !== 'undefined' && currentUserProfileImage) { %>
699
<img src="<%= currentUserProfileImage %>" alt="" style="width:22px;height:22px;border-radius:50%;object-fit:cover;">
700
<% } else { %>
701
<i class="bi bi-person-circle"></i>
702
<% } %>
703
<%= username %>
704
</a>
705
<% } else { %>
706
<a class="action-btn" href="/login?redirect=/hinana/info"><i class="bi bi-box-arrow-in-right"></i> Login</a>
707
<% } %>
708
</div>
709
<p class="about-text mt-3">
710
말을 걸고 싶으시면 X, Discord, VRChat 어느 쪽이든 괜찮아요.
711
사이트의 기록을 둘러보다가 마음에 드는 부분이 있어도 알려주시면 기뻐요.
712
</p>
713
</article>
714
<article class="glass-panel mini-terminal reveal" aria-label="비나래 터미널">
715
<span class="terminal-line"><span class="terminal-prompt">$</span> whoami</span>
716
<span class="terminal-line terminal-green">binarae / hinana.moe operator</span>
717
<span class="terminal-line"><span class="terminal-prompt">$</span> likes --top</span>
718
<span class="terminal-line">shinycolors, hinana, vrchat, rhythm-games, archives</span>
719
<span class="terminal-line"><span class="terminal-prompt">$</span> <%= (typeof binaraeSignal !== 'undefined' && binaraeSignal.terminalCommand) ? binaraeSignal.terminalCommand : 'mood --fresh' %></span>
720
<span class="terminal-line terminal-gold"><%= (typeof binaraeSignal !== 'undefined' && binaraeSignal.terminalText) ? binaraeSignal.terminalText : 'green signal, memory online' %><span class="cursor"></span></span>
721
<% if (typeof binaraeSignal !== 'undefined' && binaraeSignal.keywords && binaraeSignal.keywords.length) { %>
722
<span class="terminal-line"><span class="terminal-prompt">$</span> keywords</span>
723
<span class="terminal-line"><%= binaraeSignal.keywords.join(' / ') %></span>
724
<% } %>
725
</article>
726
</div>
727
</section>
728
</main>
729
730
<footer class="site-footer">
731
<div class="footer-brand">
732
<img src="/image/sign.png" alt="비나래" class="footer-logo">
733
<div>
734
<div class="footer-title">비나래 아카이브</div>
735
<div>Archive My Memory</div>
736
</div>
737
</div>
738
<div>
739
<div class="footer-links">
740
<a href="/hinana/index">Archive</a>
741
<a href="/hinana/info">Info</a>
742
<a href="/hinana/blog">Blog</a>
743
<a href="/hinana/lounge">Lounge</a>
744
<a href="/hinana/developer">Developer</a>
745
<a href="https://x.com/NoctchillHinana" target="_blank" rel="noreferrer">X</a>
746
</div>
747
<div class="footer-copy"><span class="footer-version">Ver. 8.0.3.2-Utsumi Aoba</span>ⓒ 2024~2026. 비나래 | hinana.moe</div>
748
</div>
749
</footer>
750
751
<script>
752
if ("serviceWorker" in navigator) { navigator.serviceWorker.register("/sw.js"); }
753
754
(function() {
755
var reduceMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
756
var reveals = document.querySelectorAll('.reveal, .motion-frame');
757
if ('IntersectionObserver' in window) {
758
var observer = new IntersectionObserver(function(entries) {
759
entries.forEach(function(entry) {
760
if (entry.isIntersecting) {
761
entry.target.classList.add('is-visible');
762
observer.unobserve(entry.target);
763
}
764
});
765
}, { threshold: 0.16 });
766
reveals.forEach(function(el) { observer.observe(el); });
767
} else {
768
reveals.forEach(function(el) { el.classList.add('is-visible'); });
769
}
770
771
var root = document.querySelector('[data-parallax-root]');
772
if (root && !reduceMotion) {
773
root.addEventListener('mousemove', function(event) {
774
var rect = root.getBoundingClientRect();
775
var x = (event.clientX - rect.left) / rect.width - 0.5;
776
var y = (event.clientY - rect.top) / rect.height - 0.5;
777
root.querySelectorAll('[data-parallax]').forEach(function(layer) {
778
var depth = Number(layer.getAttribute('data-parallax') || 0);
779
layer.style.transform = 'translate3d(' + (x * depth * 120) + 'px,' + (y * depth * 120) + 'px,0) rotate(' + (depth > 0.1 ? 2.5 : -4) + 'deg)';
780
});
781
});
782
root.addEventListener('mouseleave', function() {
783
root.querySelectorAll('[data-parallax]').forEach(function(layer) {
784
layer.style.transform = '';
785
});
786
});
787
}
788
789
var canvas = document.getElementById('spark-canvas');
790
if (!canvas || reduceMotion) return;
791
var ctx = canvas.getContext('2d');
792
var particles = [];
793
var width = 0;
794
var height = 0;
795
var dpr = Math.min(window.devicePixelRatio || 1, 2);
796
797
function resize() {
798
width = window.innerWidth;
799
height = window.innerHeight;
800
canvas.width = Math.floor(width * dpr);
801
canvas.height = Math.floor(height * dpr);
802
canvas.style.width = width + 'px';
803
canvas.style.height = height + 'px';
804
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
805
var count = Math.min(90, Math.max(34, Math.floor(width * height / 18000)));
806
particles = Array.from({ length: count }, function() {
807
return {
808
x: Math.random() * width,
809
y: Math.random() * height,
810
vx: (Math.random() - 0.5) * 0.28,
811
vy: (Math.random() - 0.5) * 0.28,
812
size: Math.random() * 2 + 1,
813
hue: Math.random() > 0.5 ? 198 : 158
814
};
815
});
816
}
817
818
function tick() {
819
ctx.clearRect(0, 0, width, height);
820
particles.forEach(function(p, index) {
821
p.x += p.vx;
822
p.y += p.vy;
823
if (p.x < -20) p.x = width + 20;
824
if (p.x > width + 20) p.x = -20;
825
if (p.y < -20) p.y = height + 20;
826
if (p.y > height + 20) p.y = -20;
827
828
ctx.fillStyle = 'hsla(' + p.hue + ', 85%, 55%, 0.42)';
829
ctx.fillRect(p.x, p.y, p.size, p.size);
830
831
for (var j = index + 1; j < particles.length; j++) {
832
var q = particles[j];
833
var dx = p.x - q.x;
834
var dy = p.y - q.y;
835
var dist = Math.sqrt(dx * dx + dy * dy);
836
if (dist < 92) {
837
ctx.strokeStyle = 'rgba(14, 165, 233,' + (0.12 * (1 - dist / 92)) + ')';
838
ctx.lineWidth = 1;
839
ctx.beginPath();
840
ctx.moveTo(p.x, p.y);
841
ctx.lineTo(q.x, q.y);
842
ctx.stroke();
843
}
844
}
845
});
846
requestAnimationFrame(tick);
847
}
848
849
resize();
850
window.addEventListener('resize', resize);
851
tick();
852
})();
853
</script>
854
</body>
855
</html>
856