summaryrefslogtreecommitdiff
path: root/assets
diff options
context:
space:
mode:
authorsillylaird <sillylaird@fastmail.ca>2026-02-03 21:27:57 -0500
committersillylaird <sillylaird@fastmail.ca>2026-02-03 21:27:57 -0500
commit720d752748b793a2f5cf3cc14cb75ad86e8919c0 (patch)
tree29120103307cb17e7d6c283cc198ec2484f934cd /assets
First commit
Diffstat (limited to 'assets')
-rw-r--r--assets/css/inline-attrs.css31
-rw-r--r--assets/css/pages/404.css12
-rw-r--r--assets/css/pages/50x.css9
-rw-r--r--assets/css/pages/articles-2023-04-10.css1
-rw-r--r--assets/css/pages/articles-2023-04-10_jp.css1
-rw-r--r--assets/css/pages/articles-2023-04-10_zh.css1
-rw-r--r--assets/css/pages/articles-runescape.css1
-rw-r--r--assets/css/pages/articles-runescape_jp.css1
-rw-r--r--assets/css/pages/articles-runescape_zh.css1
-rw-r--r--assets/css/pages/bookmarks.css61
-rw-r--r--assets/css/pages/changelog.css19
-rw-r--r--assets/css/pages/computers.css29
-rw-r--r--assets/css/pages/guestbook-form.css51
-rw-r--r--assets/css/pages/guestbook_jp.css29
-rw-r--r--assets/css/pages/guestbook_zh.css29
-rw-r--r--assets/css/pages/hitcounter.css7
-rw-r--r--assets/css/pages/hitcounter_jp.css7
-rw-r--r--assets/css/pages/hitcounter_zh.css7
-rw-r--r--assets/css/pages/journal-index.css3
-rw-r--r--assets/css/pages/journal-index_jp.css3
-rw-r--r--assets/css/pages/journal-index_zh.css3
-rw-r--r--assets/css/pages/links.css3
-rw-r--r--assets/css/pages/links_jp.css3
-rw-r--r--assets/css/pages/links_zh.css3
-rw-r--r--assets/css/pages/mstartpage-index.css361
-rw-r--r--assets/css/pages/startpage-test.css86
-rw-r--r--assets/css/pages/test.css52
-rw-r--r--assets/css/pages/test_jp.css52
-rw-r--r--assets/css/pages/test_zh.css52
-rw-r--r--assets/css/pages/vinyls.css14
-rw-r--r--assets/css/site.css891
-rw-r--r--assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7SUc.woff2bin0 -> 18748 bytes
-rw-r--r--assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7.woff2bin0 -> 48256 bytes
-rw-r--r--assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7SUc.woff2bin0 -> 18996 bytes
-rw-r--r--assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7SUc.woff2bin0 -> 85068 bytes
-rw-r--r--assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7SUc.woff2bin0 -> 25960 bytes
-rw-r--r--assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7SUc.woff2bin0 -> 11232 bytes
-rw-r--r--assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7SUc.woff2bin0 -> 10252 bytes
-rw-r--r--assets/js/includes.js32
-rw-r--r--assets/js/pages/404.js5
-rw-r--r--assets/js/pages/50x.js5
-rw-r--r--assets/js/pages/guestbook_jp.js91
-rw-r--r--assets/js/pages/guestbook_zh.js91
-rw-r--r--assets/js/pages/hitcounter.js16
-rw-r--r--assets/js/pages/hitcounter_jp.js16
-rw-r--r--assets/js/pages/hitcounter_zh.js16
-rw-r--r--assets/js/pages/mstartpage-index.js118
-rw-r--r--assets/js/pages/test.js10
-rw-r--r--assets/js/pages/test_jp.js10
-rw-r--r--assets/js/pages/test_zh.js10
-rw-r--r--assets/js/site.js415
51 files changed, 2658 insertions, 0 deletions
diff --git a/assets/css/inline-attrs.css b/assets/css/inline-attrs.css
new file mode 100644
index 0000000..2dce580
--- /dev/null
+++ b/assets/css/inline-attrs.css
@@ -0,0 +1,31 @@
+.s1da9facb4d{margin:0;}
+.s6e250fe32e{border:none;border-top:1px solid #caa;margin:1rem 0;}
+.s2a2536eebb{margin: 15px 0;}
+.sd078564312{border:1px solid #ffd; display:block; margin-bottom:6px;}
+.sd82634530e{background: transparent;
+ border:1px solid transparent;
+ color:#3a2a00;
+ padding:10px;
+ width:100%;
+ border-radius:4px;}
+.sefd85b92cb{display: flex; flex-direction: column; gap: 20px;}
+.s0b3fba4969{border: 1px solid var(--border); border-radius: var(--r);}
+.see3e7269f8{height:700px; border: 1px solid var(--border); border-radius: var(--r);}
+.s9572e08b37{width:150px;opacity:0.7;}
+.se7f7a3d62d{border:1px solid var(--border);border-radius:var(--r);padding:var(--space-md);background:var(--bg);overflow:auto;}
+.s06eeebc88f{max-width: 100%; height: auto;}
+.s386d82bd1c{height:1100px;}
+.s7e8ca45906{font-size: 50px;}
+.s0667eb5585{max-width:160px;border:1px solid var(--border);border-radius:var(--r);background:var(--bg);}
+.sed328f664c{min-height:360px;}
+.s63501a3337{border:1px solid var(--border);border-radius:var(--r);background:var(--bg);}
+.saf481e1471{width:100%;max-width:560px;height:315px;border:1px solid var(--border);border-radius:var(--r);}
+.sb83d4c5326{max-width:720px;margin:0 auto;border:1px solid var(--border);border-radius:var(--r);background:var(--bg);}
+.sd1a2ffa0b4{text-align:center;margin-top:var(--space-md);}
+.sdac4fe6c9b{text-align:center;}
+.sfed215d685{max-width:420px;margin:0 auto;border:1px solid var(--border);border-radius:var(--r);background:var(--bg);}
+.s16a8a8b00c{overflow:auto;}
+.s473e6bbfeb{width:100%;border-collapse:collapse;}
+.sf2e940dbbf{text-align:left;border-bottom:1px solid var(--border);padding:8px;}
+.s6f279abaab{padding:8px;border-bottom:1px dotted var(--border);}
+.s44cd1236df{padding:8px;}
diff --git a/assets/css/pages/404.css b/assets/css/pages/404.css
new file mode 100644
index 0000000..7939cc1
--- /dev/null
+++ b/assets/css/pages/404.css
@@ -0,0 +1,12 @@
+/* Minimal fallback if CSS fails to load */
+ body{margin:0;background:#ffd;color:#000;font-family:system-ui,Segoe UI,Arial,sans-serif;line-height:1.6;}
+ .wrap{max-width:960px;margin:0 auto;padding:1.35rem 1rem 2.5rem;}
+ .card{border:1px solid #caa;border-radius:6px;padding:1rem;background:#ffd;}
+ .top{display:flex;gap:12px;align-items:center;margin-bottom:1rem;}
+ .top img{width:48px;height:48px;border:2px solid #000;border-radius:6px;}
+ .top a:hover img{border-color:#d22;}
+ a{color:#d22;text-decoration:none;transition:0.1s;}
+ a:hover{background:#d22;color:#000 !important;}
+ .muted{color:#333;font-size:.9rem;}
+ .links{margin:1rem 0 0 0;}
+ .links a{display:inline-block;margin:0 .25rem .5rem 0;padding:2px 4px;border-radius:4px;}
diff --git a/assets/css/pages/50x.css b/assets/css/pages/50x.css
new file mode 100644
index 0000000..cc573de
--- /dev/null
+++ b/assets/css/pages/50x.css
@@ -0,0 +1,9 @@
+body{margin:0;background:#ffd;color:#000;font-family:system-ui,Segoe UI,Arial,sans-serif;line-height:1.6;}
+ .wrap{max-width:960px;margin:0 auto;padding:1.35rem 1rem 2.5rem;}
+ .card{border:1px solid #caa;border-radius:6px;padding:1rem;background:#ffd;}
+ .top{display:flex;gap:12px;align-items:center;margin-bottom:1rem;}
+ .top img{width:48px;height:48px;border:2px solid #000;border-radius:6px;}
+ .top a:hover img{border-color:#d22;}
+ a{color:#d22;text-decoration:none;transition:0.1s;}
+ a:hover{background:#d22;color:#000 !important;}
+ .muted{color:#333;font-size:.9rem;}
diff --git a/assets/css/pages/articles-2023-04-10.css b/assets/css/pages/articles-2023-04-10.css
new file mode 100644
index 0000000..1818de4
--- /dev/null
+++ b/assets/css/pages/articles-2023-04-10.css
@@ -0,0 +1 @@
+kbd{ border:1px solid var(--border); border-radius:4px; padding:0 6px; background: #fff; }
diff --git a/assets/css/pages/articles-2023-04-10_jp.css b/assets/css/pages/articles-2023-04-10_jp.css
new file mode 100644
index 0000000..1818de4
--- /dev/null
+++ b/assets/css/pages/articles-2023-04-10_jp.css
@@ -0,0 +1 @@
+kbd{ border:1px solid var(--border); border-radius:4px; padding:0 6px; background: #fff; }
diff --git a/assets/css/pages/articles-2023-04-10_zh.css b/assets/css/pages/articles-2023-04-10_zh.css
new file mode 100644
index 0000000..1818de4
--- /dev/null
+++ b/assets/css/pages/articles-2023-04-10_zh.css
@@ -0,0 +1 @@
+kbd{ border:1px solid var(--border); border-radius:4px; padding:0 6px; background: #fff; }
diff --git a/assets/css/pages/articles-runescape.css b/assets/css/pages/articles-runescape.css
new file mode 100644
index 0000000..1818de4
--- /dev/null
+++ b/assets/css/pages/articles-runescape.css
@@ -0,0 +1 @@
+kbd{ border:1px solid var(--border); border-radius:4px; padding:0 6px; background: #fff; }
diff --git a/assets/css/pages/articles-runescape_jp.css b/assets/css/pages/articles-runescape_jp.css
new file mode 100644
index 0000000..1818de4
--- /dev/null
+++ b/assets/css/pages/articles-runescape_jp.css
@@ -0,0 +1 @@
+kbd{ border:1px solid var(--border); border-radius:4px; padding:0 6px; background: #fff; }
diff --git a/assets/css/pages/articles-runescape_zh.css b/assets/css/pages/articles-runescape_zh.css
new file mode 100644
index 0000000..1818de4
--- /dev/null
+++ b/assets/css/pages/articles-runescape_zh.css
@@ -0,0 +1 @@
+kbd{ border:1px solid var(--border); border-radius:4px; padding:0 6px; background: #fff; }
diff --git a/assets/css/pages/bookmarks.css b/assets/css/pages/bookmarks.css
new file mode 100644
index 0000000..945b771
--- /dev/null
+++ b/assets/css/pages/bookmarks.css
@@ -0,0 +1,61 @@
+.bookmarks-title{
+ margin-bottom: var(--space-md);
+}
+
+.tree{ list-style-type: none; padding-left: 0; margin: 0; }
+
+.tree ul{ list-style-type: none; padding-left: 28px; margin: 5px 0; }
+
+.tree li{ margin: 4px 0; position: relative; }
+
+.tree summary{
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+ padding: 8px 12px;
+ transition: background-color 0.2s ease;
+ font-weight: 600;
+ outline: none;
+ user-select: none;
+ background: var(--bg);
+ border: 1px solid var(--border);
+ border-radius: var(--r);
+}
+
+.tree summary:hover{ background-color: #fdd; border-color: var(--accent); }
+
+.tree summary::-webkit-details-marker{ display: none; }
+
+.tree summary::before{
+ content: ">";
+ color: var(--ink);
+ margin-right: 10px;
+ font-size: 1rem;
+ transition: all 0.2s ease;
+ width: 20px;
+ text-align: left;
+ font-weight: 700;
+}
+
+.tree details[open] > summary::before{ content: ">"; transform: rotate(90deg); }
+
+.tree a{
+ color: var(--ink);
+ text-decoration: none;
+ display: inline-flex;
+ align-items: center;
+ padding: 6px 6px;
+ border-radius: 4px;
+}
+
+.tree a:hover{ background: var(--accent); color: #000 !important; }
+
+
+.tree .navbar-file a{ color: var(--muted); }
+
+.tree .navbar-file a::before{
+ content: "->";
+ color: var(--muted);
+ margin-right: 10px;
+ font-size: 0.9rem;
+}
diff --git a/assets/css/pages/changelog.css b/assets/css/pages/changelog.css
new file mode 100644
index 0000000..78b2ff4
--- /dev/null
+++ b/assets/css/pages/changelog.css
@@ -0,0 +1,19 @@
+.changelog-list{ list-style: none; padding: 0; margin: 0; }
+
+.changelog-entry{
+ display: grid;
+ grid-template-columns: 12ch 1fr;
+ gap: var(--space-lg);
+ padding: var(--space-md) 0;
+ border-bottom: 1px dotted rgba(0,0,0,.25);
+}
+
+.changelog-entry:last-child{ border-bottom: none; }
+
+.changelog-entry time{ font-weight: 700; color: var(--ink); white-space: nowrap; }
+
+.changelog-entry .text{ color: var(--muted); }
+
+@media (max-width: 560px){
+ .changelog-entry{ grid-template-columns: 1fr; }
+}
diff --git a/assets/css/pages/computers.css b/assets/css/pages/computers.css
new file mode 100644
index 0000000..ec7d01f
--- /dev/null
+++ b/assets/css/pages/computers.css
@@ -0,0 +1,29 @@
+.machine{
+ border: 1px solid var(--ink);
+ padding: var(--space-md);
+ margin-top: var(--space-md);
+ background: var(--bg);
+ display: grid;
+ grid-template-columns: 150px auto 150px;
+ gap: var(--space-md);
+ align-items: start;
+}
+
+.machine img{
+ width: 100%;
+ height: auto;
+ border: 1px solid var(--ink);
+}
+
+.machine-info{ font-size: var(--fs-base); }
+
+.machine-title{
+ color: var(--gold);
+ font-weight: 700;
+ font-size: var(--fs-lg);
+ margin-bottom: var(--space-xs);
+}
+
+@media (max-width: 720px){
+ .machine{ grid-template-columns: 1fr; }
+}
diff --git a/assets/css/pages/guestbook-form.css b/assets/css/pages/guestbook-form.css
new file mode 100644
index 0000000..b45feaa
--- /dev/null
+++ b/assets/css/pages/guestbook-form.css
@@ -0,0 +1,51 @@
+/* Pure light theme — NO container, NO black */
+body {
+ background: transparent;
+ margin: 0;
+ padding: 10px;
+ font-family: "Source Code Pro", monospace;
+ color: #3a2a00;
+ overflow-y: auto;
+ overflow-x: hidden;
+ /* Hide scrollbar for Chrome, Safari and Opera */
+ -ms-overflow-style: none; /* IE and Edge */
+ scrollbar-width: none; /* Firefox */
+}
+body::-webkit-scrollbar {
+ width: 0;
+ height: 0;
+}
+
+/* Remove all container styling */
+form {
+ max-width: 900px;
+ margin: 0 auto;
+}
+
+/* Slight spacing between form and messages */
+form {
+ margin-bottom: 20px;
+}
+
+input[type="text"], textarea {
+ border: 1px solid transparent;
+ background: transparent;
+ color: #3a2a00;
+ font: inherit;
+ box-sizing: border-box; /* Include padding in width */
+ margin-bottom: 10px;
+}
+
+textarea {
+ resize: vertical;
+}
+
+button {
+ background: transparent;
+ border: 1px solid transparent;
+ padding: 10px 20px;
+ border-radius: 4px;
+ cursor: pointer;
+ font: inherit;
+ color: #3a2a00;
+}
diff --git a/assets/css/pages/guestbook_jp.css b/assets/css/pages/guestbook_jp.css
new file mode 100644
index 0000000..bd71cb5
--- /dev/null
+++ b/assets/css/pages/guestbook_jp.css
@@ -0,0 +1,29 @@
+.anonymous { color: #0a0; }
+ .error { color: var(--accent); }
+ .captcha { font-weight: 700; font-size: 20px; }
+ form { display: grid; gap: var(--space-sm); max-width: 640px; }
+ label { font-weight: 600; }
+ input[type="text"], textarea {
+ width: 100%;
+ padding: 10px;
+ border: 1px solid var(--border);
+ border-radius: var(--r);
+ background: var(--bg);
+ color: var(--ink);
+ font: inherit;
+ }
+ textarea { min-height: 120px; resize: vertical; }
+ .btn-row { display: flex; gap: var(--space-sm); flex-wrap: wrap; }
+ button {
+ border: 1px solid var(--border);
+ border-radius: var(--r);
+ background: var(--bg);
+ padding: 8px 12px;
+ cursor: pointer;
+ font: inherit;
+ }
+ button:hover { background: #fdd; border-color: var(--accent); }
+ .entry { border-top: 1px dotted rgba(0,0,0,.25); padding-top: var(--space-md); margin-top: var(--space-md); }
+ .entry h4 { margin: 0 0 var(--space-xs) 0; }
+ .entry p { margin: 0; color: var(--muted); }
+ #captcha-canvas { border: 1px solid var(--border); border-radius: var(--r); background: #fff; }
diff --git a/assets/css/pages/guestbook_zh.css b/assets/css/pages/guestbook_zh.css
new file mode 100644
index 0000000..bd71cb5
--- /dev/null
+++ b/assets/css/pages/guestbook_zh.css
@@ -0,0 +1,29 @@
+.anonymous { color: #0a0; }
+ .error { color: var(--accent); }
+ .captcha { font-weight: 700; font-size: 20px; }
+ form { display: grid; gap: var(--space-sm); max-width: 640px; }
+ label { font-weight: 600; }
+ input[type="text"], textarea {
+ width: 100%;
+ padding: 10px;
+ border: 1px solid var(--border);
+ border-radius: var(--r);
+ background: var(--bg);
+ color: var(--ink);
+ font: inherit;
+ }
+ textarea { min-height: 120px; resize: vertical; }
+ .btn-row { display: flex; gap: var(--space-sm); flex-wrap: wrap; }
+ button {
+ border: 1px solid var(--border);
+ border-radius: var(--r);
+ background: var(--bg);
+ padding: 8px 12px;
+ cursor: pointer;
+ font: inherit;
+ }
+ button:hover { background: #fdd; border-color: var(--accent); }
+ .entry { border-top: 1px dotted rgba(0,0,0,.25); padding-top: var(--space-md); margin-top: var(--space-md); }
+ .entry h4 { margin: 0 0 var(--space-xs) 0; }
+ .entry p { margin: 0; color: var(--muted); }
+ #captcha-canvas { border: 1px solid var(--border); border-radius: var(--r); background: #fff; }
diff --git a/assets/css/pages/hitcounter.css b/assets/css/pages/hitcounter.css
new file mode 100644
index 0000000..1c52da1
--- /dev/null
+++ b/assets/css/pages/hitcounter.css
@@ -0,0 +1,7 @@
+.counter {
+ display: grid;
+ gap: var(--space-sm);
+ justify-items: center;
+ text-align: center;
+ }
+ .big { font-size: 3rem; font-weight: 800; color: var(--gold); }
diff --git a/assets/css/pages/hitcounter_jp.css b/assets/css/pages/hitcounter_jp.css
new file mode 100644
index 0000000..1c52da1
--- /dev/null
+++ b/assets/css/pages/hitcounter_jp.css
@@ -0,0 +1,7 @@
+.counter {
+ display: grid;
+ gap: var(--space-sm);
+ justify-items: center;
+ text-align: center;
+ }
+ .big { font-size: 3rem; font-weight: 800; color: var(--gold); }
diff --git a/assets/css/pages/hitcounter_zh.css b/assets/css/pages/hitcounter_zh.css
new file mode 100644
index 0000000..1c52da1
--- /dev/null
+++ b/assets/css/pages/hitcounter_zh.css
@@ -0,0 +1,7 @@
+.counter {
+ display: grid;
+ gap: var(--space-sm);
+ justify-items: center;
+ text-align: center;
+ }
+ .big { font-size: 3rem; font-weight: 800; color: var(--gold); }
diff --git a/assets/css/pages/journal-index.css b/assets/css/pages/journal-index.css
new file mode 100644
index 0000000..02e8c42
--- /dev/null
+++ b/assets/css/pages/journal-index.css
@@ -0,0 +1,3 @@
+.entry { margin: var(--space-md) 0; }
+ .entry time { font-weight: 700; color: var(--ink); }
+ .entry p { margin: var(--space-xs) 0 0 0; color: var(--muted); }
diff --git a/assets/css/pages/journal-index_jp.css b/assets/css/pages/journal-index_jp.css
new file mode 100644
index 0000000..02e8c42
--- /dev/null
+++ b/assets/css/pages/journal-index_jp.css
@@ -0,0 +1,3 @@
+.entry { margin: var(--space-md) 0; }
+ .entry time { font-weight: 700; color: var(--ink); }
+ .entry p { margin: var(--space-xs) 0 0 0; color: var(--muted); }
diff --git a/assets/css/pages/journal-index_zh.css b/assets/css/pages/journal-index_zh.css
new file mode 100644
index 0000000..02e8c42
--- /dev/null
+++ b/assets/css/pages/journal-index_zh.css
@@ -0,0 +1,3 @@
+.entry { margin: var(--space-md) 0; }
+ .entry time { font-weight: 700; color: var(--ink); }
+ .entry p { margin: var(--space-xs) 0 0 0; color: var(--muted); }
diff --git a/assets/css/pages/links.css b/assets/css/pages/links.css
new file mode 100644
index 0000000..fbb3700
--- /dev/null
+++ b/assets/css/pages/links.css
@@ -0,0 +1,3 @@
+.buttons img { image-rendering: auto; }
+ .buttons a { padding: 0; background: transparent; }
+ .buttons a:hover { background: transparent; }
diff --git a/assets/css/pages/links_jp.css b/assets/css/pages/links_jp.css
new file mode 100644
index 0000000..fbb3700
--- /dev/null
+++ b/assets/css/pages/links_jp.css
@@ -0,0 +1,3 @@
+.buttons img { image-rendering: auto; }
+ .buttons a { padding: 0; background: transparent; }
+ .buttons a:hover { background: transparent; }
diff --git a/assets/css/pages/links_zh.css b/assets/css/pages/links_zh.css
new file mode 100644
index 0000000..fbb3700
--- /dev/null
+++ b/assets/css/pages/links_zh.css
@@ -0,0 +1,3 @@
+.buttons img { image-rendering: auto; }
+ .buttons a { padding: 0; background: transparent; }
+ .buttons a:hover { background: transparent; }
diff --git a/assets/css/pages/mstartpage-index.css b/assets/css/pages/mstartpage-index.css
new file mode 100644
index 0000000..67eac23
--- /dev/null
+++ b/assets/css/pages/mstartpage-index.css
@@ -0,0 +1,361 @@
+:root {
+ --main-text-color: #ddd;
+ --border-color: #C8A977;
+ --search-input-color: black;
+ --link-color: #FF0000;
+ --background-color: #111;
+ --menu-background: #222;
+ }
+
+ * {
+ box-sizing: border-box;
+ }
+
+ html, body {
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ background-color: var(--background-color);
+ font-family: 'Source Code Pro', "MS Gothic", IPAMonaGothic, IPAGothic, "BIZ UDGothic", HGGothicM, "Noto Sans Mono CJK JP", "Noto Sans Mono", monospace;
+ font-size: 14px;
+ color: var(--main-text-color);
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.7);
+ overflow-x: hidden;
+ }
+
+ a {
+ color: var(--link-color);
+ background-color: transparent;
+ text-decoration: none;
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
+ }
+
+ a:hover {
+ text-decoration: underline;
+ }
+
+ /* Header and menu */
+ .header {
+ position: sticky;
+ top: 0;
+ z-index: 100;
+ background-color: var(--background-color);
+ padding: 10px;
+ border-bottom: 1px solid var(--border-color);
+ }
+
+ .menu-toggle {
+ display: block;
+ width: 100%;
+ padding: 12px;
+ background-color: black;
+ color: var(--link-color);
+ border: 1px solid var(--link-color);
+ border-radius: 4px;
+ text-align: center;
+ font-weight: bold;
+ margin-bottom: 10px;
+ cursor: pointer;
+ }
+
+ .main-menu {
+ display: none;
+ flex-direction: column;
+ background-color: var(--menu-background);
+ border: 1px solid var(--link-color);
+ border-radius: 4px;
+ padding: 10px;
+ margin-bottom: 15px;
+ }
+
+ .main-menu.show {
+ display: flex;
+ }
+
+ .main-menu a {
+ padding: 10px;
+ border-bottom: 1px solid #333;
+ }
+
+ .main-menu a:last-child {
+ border-bottom: none;
+ }
+
+ /* Search box */
+ .search-container {
+ width: 100%;
+ padding: 10px;
+ background-color: var(--background-color);
+ margin-bottom: 15px;
+ }
+
+ .search-form {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .search-input {
+ color: var(--link-color);
+ background-color: #3A3A3A;
+ border: 1px solid var(--link-color);
+ padding: 12px;
+ margin-bottom: 10px;
+ text-align: center;
+ border-radius: 4px;
+ font-size: 16px; /* Larger for touch */
+ }
+
+ input[type="submit"] {
+ color: var(--link-color);
+ background-color: black;
+ padding: 12px;
+ border: 1px solid var(--link-color);
+ cursor: pointer;
+ border-radius: 4px;
+ font-weight: bold;
+ font-size: 16px;
+ }
+
+ /* Content sections */
+ .content-section {
+ margin-bottom: 20px;
+ padding: 0 10px;
+ }
+
+ .section-title {
+ color: var(--link-color);
+ font-size: 1.2em;
+ border-bottom: 1px solid var(--border-color);
+ padding-bottom: 5px;
+ margin-bottom: 10px;
+ }
+
+ .link-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
+ gap: 10px;
+ }
+
+ .link-item {
+ padding: 10px;
+ background-color: #1a1a1a;
+ border: 1px solid #333;
+ border-radius: 4px;
+ text-align: center;
+ }
+
+ /* Gallery items for mobile */
+ .gallery-container {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
+ gap: 15px;
+ padding: 10px;
+ }
+
+ .gallery-item {
+ border: 2px ridge var(--border-color);
+ border-radius: 4px;
+ overflow: hidden;
+ }
+
+ .gallery-item:hover {
+ border: 2px ridge var(--link-color);
+ }
+
+ .gallery-item img {
+ width: 100%;
+ height: auto;
+ display: block;
+ }
+
+ .gallery-desc {
+ padding: 8px;
+ text-align: center;
+ font-size: 0.9em;
+ color: var(--link-color);
+ background-color: #1a1a1a;
+ }
+
+ /* Date and time */
+ .datetime {
+ padding: 10px;
+ text-align: center;
+ margin: 15px 0;
+ }
+
+ #realtime-date, #realtime-clock {
+ color: var(--link-color);
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
+ margin: 5px 0;
+ }
+
+ /* Footer */
+ .footer {
+ padding: 15px;
+ text-align: center;
+ border-top: 1px solid #333;
+ margin-top: 20px;
+ }
+
+ .footer-links {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 10px;
+ }
+
+ .footer-links a {
+ padding: 5px;
+ }
+
+ /* Collapsible sections */
+ .collapsible {
+ background-color: #1a1a1a;
+ color: var(--link-color);
+ cursor: pointer;
+ padding: 12px;
+ width: 100%;
+ border: 1px solid var(--border-color);
+ text-align: left;
+ outline: none;
+ font-size: 1em;
+ font-weight: bold;
+ margin-bottom: 5px;
+ border-radius: 4px;
+ }
+
+ .active, .collapsible:hover {
+ background-color: #222;
+ }
+
+ .collapsible-content {
+ padding: 0 10px;
+ display: none;
+ overflow: hidden;
+ background-color: #1a1a1a;
+ margin-bottom: 10px;
+ border-radius: 0 0 4px 4px;
+ }
+
+ /* XXX Verification Modal */
+ .xxx-modal {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.9);
+ z-index: 1000;
+ justify-content: center;
+ align-items: center;
+ }
+
+ .xxx-modal-content {
+ background-color: var(--menu-background);
+ padding: 20px;
+ border-radius: 8px;
+ border: 2px solid var(--link-color);
+ text-align: center;
+ max-width: 90%;
+ width: 350px;
+ }
+
+ .xxx-logo {
+ width: 80px;
+ height: 80px;
+ margin: 0 auto 15px;
+ background-color: #333;
+ border-radius: 50%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-size: 24px;
+ color: var(--link-color);
+ border: 2px solid var(--link-color);
+ }
+
+ .xxx-buttons {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 20px;
+ }
+
+ .xxx-button {
+ padding: 10px 20px;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-weight: bold;
+ }
+
+ .xxx-confirm {
+ background-color: #4CAF50;
+ color: white;
+ }
+
+ .xxx-deny {
+ background-color: #f44336;
+ color: white;
+ }
+
+ /* XXX Section */
+ .xxx-section {
+ margin: 20px 0;
+ text-align: center;
+ }
+
+ .xxx-toggle {
+ padding: 12px 20px;
+ background-color: #333;
+ color: var(--link-color);
+ border: 1px solid var(--link-color);
+ border-radius: 4px;
+ cursor: pointer;
+ font-weight: bold;
+ margin-bottom: 15px;
+ }
+
+ .xxx-list {
+ position: fixed;
+ bottom: 20px;
+ right: 20px;
+ background-color: var(--menu-background);
+ border: 1px solid var(--link-color);
+ border-radius: 8px;
+ padding: 10px;
+ max-width: 200px;
+ display: none;
+ z-index: 999;
+ }
+
+ .xxx-list-title {
+ color: var(--link-color);
+ font-weight: bold;
+ margin-bottom: 8px;
+ text-align: center;
+ border-bottom: 1px solid #444;
+ padding-bottom: 5px;
+ }
+
+ .xxx-list-item {
+ margin: 5px 0;
+ text-align: center;
+ }
+
+ /* Responsive adjustments */
+ @media (min-width: 768px) {
+ body {
+ font-size: 16px;
+ max-width: 800px;
+ margin: 0 auto;
+ }
+
+ .link-grid {
+ grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
+ }
+
+ .gallery-container {
+ grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
+ }
+ }
diff --git a/assets/css/pages/startpage-test.css b/assets/css/pages/startpage-test.css
new file mode 100644
index 0000000..14996cb
--- /dev/null
+++ b/assets/css/pages/startpage-test.css
@@ -0,0 +1,86 @@
+body {
+ font-family: 'Inter', sans-serif; /* Using Inter font */
+ margin: 0; /* Remove default body margin */
+ padding: 20px;
+ background-color: #f0f0f0;
+ display: flex;
+ flex-direction: column; /* Arrange content vertically */
+ justify-content: center;
+ align-items: center;
+ min-height: 100vh;
+ }
+
+ h1 {
+ text-align: center;
+ color: #333;
+ margin-bottom: 20px;
+ font-size: 2em; /* Larger heading */
+ }
+
+ .responsive-container {
+ width: 95%; /* Make it wider to accommodate the full page embed */
+ max-width: 1200px; /* Max width for larger screens to prevent it from getting too wide */
+ padding: 10px;
+ box-sizing: border-box;
+ border: 1px solid #ccc;
+ border-radius: 12px; /* Slightly larger rounded corners */
+ box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15); /* More prominent shadow */
+ background-color: white;
+ margin-bottom: 20px;
+ overflow: hidden; /* Ensure content stays within rounded corners */
+ }
+
+ .iframe-wrapper {
+ position: relative;
+ /* Maintain an aspect ratio that suits a full webpage, e.g., 16:9 or taller */
+ /* A good starting point for a full webpage, adjust as needed for optimal viewing */
+ padding-bottom: 120%; /* Height will be 120% of the width, making it taller */
+ height: 0;
+ overflow: hidden;
+ border-radius: 8px; /* Slightly smaller rounded corners for inner frame */
+ }
+
+ .iframe-wrapper iframe {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ border: none;
+ border-radius: 8px;
+ }
+
+ .desc {
+ padding: 15px;
+ text-align: center;
+ font-size: 1.1em;
+ color: #555;
+ line-height: 1.6;
+ }
+
+ .desc a {
+ color: #007bff;
+ text-decoration: none;
+ font-weight: bold;
+ transition: color 0.3s ease; /* Smooth transition for hover effect */
+ }
+
+ .desc a:hover {
+ text-decoration: underline;
+ color: #0056b3; /* Darker blue on hover */
+ }
+
+ /* Responsive adjustments for the container */
+ @media only screen and (max-width: 768px) {
+ .responsive-container {
+ width: 100%; /* Full width on smaller screens */
+ padding: 5px;
+ border-radius: 0; /* No rounded corners on very small screens for edge-to-edge */
+ }
+ .iframe-wrapper {
+ padding-bottom: 150%; /* Make it even taller on mobile if needed */
+ }
+ body {
+ padding: 10px;
+ }
+ }
diff --git a/assets/css/pages/test.css b/assets/css/pages/test.css
new file mode 100644
index 0000000..4eacfa4
--- /dev/null
+++ b/assets/css/pages/test.css
@@ -0,0 +1,52 @@
+html, body { margin: 0; padding: 0; }
+ table.layout { border-collapse: collapse; width: 100%; }
+ td.content { padding: 0; }
+ .logo { float: left; margin-right: 1rem; }
+
+ a {
+ text-decoration: none;
+ background-color: transparent;
+ transition: background-color 0.2s, color 0.2s;
+ padding: 4px;
+ border-radius: 4px;
+ }
+ a:hover { background-color: red; color: white; }
+ a.no-hover-box:hover { background-color: transparent; color: red; }
+
+ .sidebar {
+ position: fixed;
+ top: 0;
+ right: 0;
+ width: 250px;
+ max-height: 100vh;
+ overflow-y: auto;
+ background-color: #ffd;
+ transform: translateX(100%);
+ transition: transform 0.3s ease-in-out;
+ }
+ .sidebar.visible { transform: translateX(0); }
+
+ .links-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 1.5rem;
+ margin-bottom: 1rem;
+ }
+ .links-container ul {
+ flex: 1 1 200px;
+ list-style: none;
+ margin: 0; padding: 0;
+ }
+ .links-container ul li { margin-bottom: 0.75rem; word-wrap: break-word; }
+ .links-container ul li a {
+ display: inline-block;
+ max-width: 100%;
+ word-wrap: break-word;
+ color: #0366d6;
+ transition: color 0.3s ease;
+ }
+ .links-container ul li a:hover { color: #023e8a; text-decoration: underline; }
+
+ @media (max-width: 600px) {
+ .links-container { flex-direction: column; }
+ }
diff --git a/assets/css/pages/test_jp.css b/assets/css/pages/test_jp.css
new file mode 100644
index 0000000..4eacfa4
--- /dev/null
+++ b/assets/css/pages/test_jp.css
@@ -0,0 +1,52 @@
+html, body { margin: 0; padding: 0; }
+ table.layout { border-collapse: collapse; width: 100%; }
+ td.content { padding: 0; }
+ .logo { float: left; margin-right: 1rem; }
+
+ a {
+ text-decoration: none;
+ background-color: transparent;
+ transition: background-color 0.2s, color 0.2s;
+ padding: 4px;
+ border-radius: 4px;
+ }
+ a:hover { background-color: red; color: white; }
+ a.no-hover-box:hover { background-color: transparent; color: red; }
+
+ .sidebar {
+ position: fixed;
+ top: 0;
+ right: 0;
+ width: 250px;
+ max-height: 100vh;
+ overflow-y: auto;
+ background-color: #ffd;
+ transform: translateX(100%);
+ transition: transform 0.3s ease-in-out;
+ }
+ .sidebar.visible { transform: translateX(0); }
+
+ .links-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 1.5rem;
+ margin-bottom: 1rem;
+ }
+ .links-container ul {
+ flex: 1 1 200px;
+ list-style: none;
+ margin: 0; padding: 0;
+ }
+ .links-container ul li { margin-bottom: 0.75rem; word-wrap: break-word; }
+ .links-container ul li a {
+ display: inline-block;
+ max-width: 100%;
+ word-wrap: break-word;
+ color: #0366d6;
+ transition: color 0.3s ease;
+ }
+ .links-container ul li a:hover { color: #023e8a; text-decoration: underline; }
+
+ @media (max-width: 600px) {
+ .links-container { flex-direction: column; }
+ }
diff --git a/assets/css/pages/test_zh.css b/assets/css/pages/test_zh.css
new file mode 100644
index 0000000..4eacfa4
--- /dev/null
+++ b/assets/css/pages/test_zh.css
@@ -0,0 +1,52 @@
+html, body { margin: 0; padding: 0; }
+ table.layout { border-collapse: collapse; width: 100%; }
+ td.content { padding: 0; }
+ .logo { float: left; margin-right: 1rem; }
+
+ a {
+ text-decoration: none;
+ background-color: transparent;
+ transition: background-color 0.2s, color 0.2s;
+ padding: 4px;
+ border-radius: 4px;
+ }
+ a:hover { background-color: red; color: white; }
+ a.no-hover-box:hover { background-color: transparent; color: red; }
+
+ .sidebar {
+ position: fixed;
+ top: 0;
+ right: 0;
+ width: 250px;
+ max-height: 100vh;
+ overflow-y: auto;
+ background-color: #ffd;
+ transform: translateX(100%);
+ transition: transform 0.3s ease-in-out;
+ }
+ .sidebar.visible { transform: translateX(0); }
+
+ .links-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 1.5rem;
+ margin-bottom: 1rem;
+ }
+ .links-container ul {
+ flex: 1 1 200px;
+ list-style: none;
+ margin: 0; padding: 0;
+ }
+ .links-container ul li { margin-bottom: 0.75rem; word-wrap: break-word; }
+ .links-container ul li a {
+ display: inline-block;
+ max-width: 100%;
+ word-wrap: break-word;
+ color: #0366d6;
+ transition: color 0.3s ease;
+ }
+ .links-container ul li a:hover { color: #023e8a; text-decoration: underline; }
+
+ @media (max-width: 600px) {
+ .links-container { flex-direction: column; }
+ }
diff --git a/assets/css/pages/vinyls.css b/assets/css/pages/vinyls.css
new file mode 100644
index 0000000..ecf8b4b
--- /dev/null
+++ b/assets/css/pages/vinyls.css
@@ -0,0 +1,14 @@
+.vinyl-grid{
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
+ gap: var(--space-md);
+ margin-top: var(--space-md);
+}
+
+.vinyl-grid img{
+ width: 100%;
+ height: auto;
+ border: 1px solid var(--border);
+ border-radius: var(--r);
+ background: var(--bg);
+}
diff --git a/assets/css/site.css b/assets/css/site.css
new file mode 100644
index 0000000..4434b60
--- /dev/null
+++ b/assets/css/site.css
@@ -0,0 +1,891 @@
+/* cyrillic-ext */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7SUc.woff2) format('woff2');
+ unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7SUc.woff2) format('woff2');
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7SUc.woff2) format('woff2');
+ unicode-range: U+1F00-1FFF;
+}
+/* greek */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7SUc.woff2) format('woff2');
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+/* vietnamese */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7SUc.woff2) format('woff2');
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7SUc.woff2) format('woff2');
+ unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7.woff2) format('woff2');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 600;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7SUc.woff2) format('woff2');
+ unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 600;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7SUc.woff2) format('woff2');
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 600;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7SUc.woff2) format('woff2');
+ unicode-range: U+1F00-1FFF;
+}
+/* greek */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 600;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7SUc.woff2) format('woff2');
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+/* vietnamese */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 600;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7SUc.woff2) format('woff2');
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 600;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7SUc.woff2) format('woff2');
+ unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 600;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7.woff2) format('woff2');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 700;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7SUc.woff2) format('woff2');
+ unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 700;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7SUc.woff2) format('woff2');
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 700;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7SUc.woff2) format('woff2');
+ unicode-range: U+1F00-1FFF;
+}
+/* greek */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 700;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7SUc.woff2) format('woff2');
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+/* vietnamese */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 700;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7SUc.woff2) format('woff2');
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 700;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7SUc.woff2) format('woff2');
+ unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face {
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 700;
+ font-display: swap;
+ src: url(/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7.woff2) format('woff2');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+
+:root{
+ --bg: #ffd;
+ --ink: #000;
+ --muted: #333;
+ --accent: #d22;
+ --gold: #c69214;
+ --border: #caa;
+ --shadow: rgba(0,0,0,.10);
+
+ --wrap: 960px;
+ --r: 6px;
+
+ /* Unified spacing scale */
+ --space-xs: 0.25rem;
+ --space-sm: 0.5rem;
+ --space-md: 0.75rem;
+ --space-lg: 1rem;
+ --space-xl: 1.35rem;
+ --space-2xl: 1.75rem;
+ --space-3xl: 2.5rem;
+
+ /* Typography scale */
+ --fs-xs: 0.85rem;
+ --fs-sm: 0.9rem;
+ --fs-base: 1rem;
+ --fs-lg: 1.05rem;
+ --fs-xl: 1.15rem;
+ --fs-2xl: 1.5rem;
+ --fs-3xl: 1.8rem;
+}
+
+/* --- UNIFIED HEADER BUTTONS STYLE --- */
+.header-button {
+ font-size: var(--fs-sm);
+ border: 1px solid var(--border);
+ padding: 4px 8px;
+ border-radius: var(--r);
+ color: var(--ink);
+ text-decoration: none;
+ background: var(--bg);
+ cursor: pointer;
+ font-family: inherit;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ transition: background .15s, color .15s, border-color .15s;
+}
+
+.header-button:hover {
+ background: var(--accent);
+ color: #000;
+ border-color: var(--accent);
+}
+
+/* Reset */
+*, *::before, *::after { box-sizing: border-box; }
+html, body { height: 100%; }
+body { margin: 0; }
+
+body{
+ font-family: "Inter", system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
+ line-height: 1.6;
+ background: var(--bg);
+ color: var(--ink);
+ font-size: var(--fs-base);
+ /* Prevent horizontal scrollbar when menu icon rotates */
+ overflow-x: hidden;
+}
+
+/* Typography */
+p{
+ margin: var(--space-sm) 0;
+ line-height: 1.6;
+}
+p:first-child{ margin-top: 0; }
+p:last-child{ margin-bottom: 0; }
+
+img{ max-width: 100%; height: auto; display: block; }
+video{ display: block; }
+
+/* Accessibility helpers */
+.skip-link{
+ position: absolute;
+ left: -999px;
+ top: 0;
+ background: var(--bg);
+ border: 1px solid var(--border);
+ padding: .5rem .75rem;
+ border-radius: var(--r);
+ z-index: 2000;
+}
+.skip-link:focus{ left: 1rem; top: 1rem; }
+
+:focus-visible{
+ outline: 2px solid var(--accent);
+ outline-offset: 2px;
+}
+
+/* Layout */
+.wrap{
+ max-width: var(--wrap);
+ margin: 0 auto;
+ padding: var(--space-xl) var(--space-lg) var(--space-3xl);
+}
+
+/* Utilities */
+.stack{
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-xl);
+}
+
+header.site-header, footer.site-footer{
+ background: var(--bg);
+ border-bottom: 1px solid var(--border);
+}
+
+header.site-header{
+ position: sticky;
+ top: 0;
+ z-index: 1000;
+}
+
+.header-inner{
+ max-width: var(--wrap);
+ margin: 0 auto;
+ padding: var(--space-md) var(--space-lg);
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--space-lg);
+ flex-wrap: wrap;
+}
+
+.brand{
+ display: flex;
+ align-items: center;
+ gap: var(--space-md);
+ min-width: 240px;
+}
+
+.logo{
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: var(--r);
+}
+.logo img{
+ height: 48px;
+ width: 48px;
+ object-fit: contain;
+ border: 2px solid #000;
+ border-radius: var(--r);
+}
+
+header.site-header .logo:hover img,
+header.site-header .logo:focus-visible img{
+ border-color: var(--accent);
+}
+
+.brand-text{
+ display: flex;
+ flex-direction: column;
+ line-height: 1.2;
+}
+.brand-text strong{
+ font-size: var(--fs-lg);
+ color: var(--gold);
+ letter-spacing: .2px;
+}
+.brand-text span{
+ font-size: var(--fs-sm);
+ color: var(--muted);
+}
+
+.header-controls{
+ display: flex;
+ align-items: center;
+ gap: var(--space-md);
+}
+
+/* Links (match startpage hover) */
+a{
+ color: var(--accent);
+ text-decoration: none;
+ transition: 0.1s;
+}
+a:hover{
+ background: var(--accent);
+ color: #000 !important;
+}
+
+/* Menu */
+.menu-wrapper{ position: relative; }
+
+#menu-toggle{
+ width: 44px;
+ height: 44px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 22px;
+ line-height: 1;
+ transition: transform .25s ease;
+ border: 1px solid #000;
+ border-radius: var(--r);
+ background: var(--bg);
+ color: var(--ink);
+ padding: 0;
+}
+
+#menu-toggle:hover {
+ background: var(--accent);
+ color: #000;
+ border-color: #000;
+}
+
+#menu-toggle.active{
+ transform: rotate(90deg);
+ background: var(--bg);
+ color: var(--ink);
+ border-color: #000;
+}
+
+#menu-toggle.active:hover{
+ background: var(--accent);
+ color: #000;
+ border-color: #000;
+}
+
+nav.site-nav{
+ position: absolute;
+ right: 0;
+ top: calc(100% + var(--space-xs));
+ background: var(--bg);
+ border: 1px solid var(--ink);
+ border-radius: var(--r);
+ padding: var(--space-md) var(--space-md);
+ min-width: 200px;
+ box-shadow: 0 10px 22px var(--shadow);
+ z-index: 1200;
+}
+nav.site-nav ul{ list-style: none; margin: 0; padding: 0; }
+nav.site-nav li{ margin: var(--space-sm) 0; }
+nav.site-nav a{
+ display: block;
+ padding: var(--space-xs) var(--space-xs);
+ transition: background .15s, color .15s;
+ color: var(--ink);
+}
+nav.site-nav a:hover{ background: var(--accent); color: #000; }
+
+/* Nav sections */
+nav.site-nav .nav-section{ margin-bottom: var(--space-md); }
+nav.site-nav .nav-section:last-child{ margin-bottom: 0; }
+nav.site-nav .nav-section + .nav-section{
+ border-top: 1px solid var(--border);
+ padding-top: var(--space-md);
+}
+nav.site-nav .nav-label{
+ font-size: var(--fs-xs);
+ font-weight: 600;
+ color: var(--muted);
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ margin-bottom: var(--space-xs);
+ padding: var(--space-xs);
+}
+
+/* Language dropdown */
+.lang{ position: relative; }
+
+.lang-toggle-wrapper{ display: inline-block; }
+
+.lang-menu{
+ position: absolute;
+ right: 0;
+ top: calc(100% + var(--space-xs));
+ background: var(--bg);
+ border: 1px solid var(--border);
+ border-radius: var(--r);
+ box-shadow: 0 10px 22px var(--shadow);
+ min-width: 160px;
+ padding: var(--space-sm);
+ z-index: 1200;
+}
+.lang-menu a{
+ display: block;
+ padding: var(--space-sm) var(--space-sm);
+ color: var(--ink);
+ transition: background .15s;
+}
+.lang-menu a:hover{ background: var(--accent); color: #000; }
+
+/* Sections / cards */
+section{
+ margin: var(--space-xl) 0;
+ border: 1px solid var(--border);
+ border-radius: var(--r);
+ padding: var(--space-lg);
+ background: var(--bg);
+}
+
+h1{
+ margin: 0 0 var(--space-md) 0;
+ color: var(--gold);
+ font-weight: 700;
+ font-size: var(--fs-2xl);
+ letter-spacing: .2px;
+ line-height: 1.2;
+}
+
+h2{
+ margin: 0 0 var(--space-md) 0;
+ color: var(--gold);
+ font-size: var(--fs-xl);
+ font-weight: 700;
+ letter-spacing: .1px;
+ line-height: 1.3;
+}
+
+/* CHANGELOG SPECIFIC STYLES */
+.changelog-title{
+ color: var(--gold) !important;
+ margin-bottom: var(--space-sm) !important;
+}
+
+.changelog-date{
+ font-size: var(--fs-3xl) !important;
+ font-weight: 700;
+ color: var(--muted);
+ margin: var(--space-sm) 0 var(--space-lg) 0;
+ line-height: 1.1;
+}
+
+.muted{ color: var(--muted); font-size: var(--fs-sm); }
+
+/* Video */
+.video-wrap{ margin: var(--space-md) 0 0 0; }
+.video-wrap video{
+ width: 100%;
+ height: auto;
+ max-height: 80vh;
+ background: #000;
+ border: 1px solid var(--border);
+ border-radius: var(--r);
+}
+
+/* Lists */
+.contact-list{
+ list-style: none;
+ padding: 0;
+ margin: var(--space-md) 0 0 0;
+}
+.contact-list li{ margin: var(--space-sm) 0; line-height: 1.6; }
+.contact-address{ font-style: normal; }
+
+blockquote{
+ margin: var(--space-md) 0 0 0;
+ padding-left: var(--space-md);
+ border-left: 3px solid var(--border);
+ color: var(--muted);
+ font-style: italic;
+ font-size: var(--fs-sm);
+ line-height: 1.6;
+}
+
+/* Iframe loading overlay */
+.iframe-clip{ position: relative; }
+.iframe-clip iframe{ position: relative; z-index: 1; }
+.iframe-clip.is-loading::before{
+ content: "Loading...";
+ position: absolute;
+ inset: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: rgba(255, 255, 221, 0.92);
+ color: var(--ink);
+ font-size: var(--fs-sm);
+ z-index: 2;
+}
+.iframe-clip.is-loading::after{
+ content: "";
+ position: absolute;
+ width: 22px;
+ height: 22px;
+ border: 2px solid var(--border);
+ border-top-color: var(--accent);
+ border-radius: 50%;
+ top: calc(50% + 16px);
+ left: 50%;
+ transform: translateX(-50%);
+ animation: iframe-spin 0.8s linear infinite;
+ z-index: 3;
+}
+@keyframes iframe-spin{
+ to { transform: translateX(-50%) rotate(360deg); }
+}
+
+/* Friends grid */
+.friends-list{
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: var(--space-md);
+ margin: var(--space-md) 0 0 0;
+ padding: 0;
+ list-style: none;
+}
+.friends-list a{
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 42px;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+
+ background: var(--bg);
+ border: 1px solid var(--border);
+ border-radius: var(--r);
+ padding: var(--space-sm) var(--space-sm);
+ color: var(--accent);
+ transition: background .15s, color .15s, border-color .15s, box-shadow .15s;
+}
+.friends-list a:hover{
+ background: var(--accent);
+ color: #000;
+ border-color: var(--accent);
+ box-shadow: 0 1px 0 var(--accent) inset;
+}
+
+/* Tiles */
+.tile-grid{
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
+ gap: var(--space-md);
+ list-style: none;
+ padding: 0;
+ margin: var(--space-md) 0 0 0;
+}
+.tile-grid a{
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 64px;
+ background: var(--bg);
+ border: 1px solid var(--border);
+ border-radius: var(--r);
+ padding: var(--space-sm);
+ transition: background .15s, border-color .15s, box-shadow .15s;
+}
+.tile-grid a:hover{
+ background: var(--accent);
+ border-color: var(--accent);
+ box-shadow: 0 1px 0 var(--accent) inset;
+}
+.tile-grid img{ width: 100%; height: 100%; object-fit: contain; }
+
+/* Embeds */
+.embed-frame{
+ display: block;
+ width: 100%;
+ border: 1px solid var(--border);
+ border-radius: var(--r);
+ background: var(--bg);
+ margin: var(--space-md) 0 0 0;
+}
+.guestbook-form-frame{
+ height: 350px;
+ min-height: 350px;
+}
+.iframe-clip{
+ --sb: 18px;
+ overflow: hidden;
+ border-radius: var(--r);
+ border: 1px solid var(--border);
+}
+.iframe-clip > iframe{
+ width: calc(100% + var(--sb));
+ height: calc(100% + var(--sb));
+ border: 0;
+}
+.guestbook-frame{ min-height: 90vh; }
+
+#guestbook-comments-iframe {
+ min-height: 600px; /* Adjusted to show more comments */
+}
+
+.changelog-frame{ min-height: 360px; }
+
+/* Audio */
+.audio-wrap{ margin: var(--space-md) 0 0 0; }
+audio{ width: 100%; max-width: 400px; }
+
+/* Blog + changelog lists */
+#blog-list,
+#changelog-list{ margin: var(--space-md) 0 0 0; }
+#blog-list > div:first-child,
+#changelog-list > div:first-child{ margin-bottom: var(--space-md); }
+#blog-list ol,
+#changelog-list ol{ margin: var(--space-md) 0 0 0; padding-left: var(--space-xl); }
+#blog-list li,
+#changelog-list li{ margin: 0 0 var(--space-md) 0; line-height: 1.6; }
+#blog-list .muted,
+#changelog-list .muted{ font-size: var(--fs-sm); margin-top: var(--space-xs); }
+#blog-list .blog-more{ margin-top: var(--space-xs); }
+#changelog-list .changelog-body{ margin-top: var(--space-xs); white-space: pre-line; }
+#changelog-list time{ color: var(--accent); }
+
+/* Footer */
+footer.site-footer{ border-top: 1px solid var(--border); border-bottom: none; }
+.footer-inner{
+ max-width: var(--wrap);
+ margin: 0 auto;
+ padding: var(--space-lg) var(--space-lg) var(--space-2xl);
+ color: var(--muted);
+ font-size: var(--fs-sm);
+ line-height: 1.6;
+}
+.badge{
+ display: inline-flex;
+ margin-top: var(--space-sm);
+ border-radius: var(--r);
+ padding: var(--space-xs);
+ border: 1px solid var(--border);
+ background: var(--bg);
+ transition: border-color .15s;
+}
+.badge:hover{ border-color: var(--accent); }
+
+/* Reset Button */
+.reset-button{
+ font-size: var(--fs-sm);
+ border: 1px solid #000; /* Black border */
+ padding: 4px 8px;
+ border-radius: var(--r);
+ color: #000; /* Black text */
+ background: #ffd; /* Light yellow background */
+ cursor: pointer;
+ font-family: inherit;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ transition: background .15s, color .15s, border-color .15s;
+}
+
+.reset-button:hover {
+ background: var(--accent); /* Dark red on hover */
+ color: #000; /* Black text on hover */
+ border-color: #000; /* Black border on hover */
+}
+
+@media (max-width: 600px){
+ .tile-grid{
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ }
+ .tile-grid li{
+ margin-bottom: 1rem;
+ text-align: center;
+ }
+ .contact-list{
+ padding-left: 20px;
+ }
+ .friends-list{
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ }
+}
+
+@media (max-width: 520px){
+ header.site-header{ position: sticky; }
+ .header-inner{ position: relative; }
+ nav.site-nav{
+ min-width: 200px;
+ max-width: calc(100vw - 2 * var(--space-lg));
+ }
+ .lang-menu{
+ right: auto;
+ left: 0;
+ max-width: calc(100vw - 32px);
+ }
+ #menu-toggle{ display: inline-flex; transition: transform .25s ease; }
+ #menu-toggle.active{ transform: rotate(90deg); }
+}
+
+@media (prefers-reduced-motion: reduce){
+ *{ transition: none !important; scroll-behavior: auto !important; }
+ #menu-toggle.active{ transform: none; }
+}
+
+/* From style.css */
+h1 {
+ font-family: 'Comic Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif;
+ font-size: 1.875rem; /* 30px / 16px (base font size) */
+ padding-left: 40px;
+}
+
+pre {
+ font-family: 'Comic Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif;
+ font-size: 12px;
+ padding-left: 40px;
+}
+
+.button1 {
+ background-color: black;
+ font-family: Comic Sans MS;
+ font-weight: 300;
+}
+
+.member {
+ float: left;
+ width: calc(20% - 2%); /* Adjust width to account for margins */
+ margin: 1% 1% 45px 1%;
+}
+
+.name {
+ bottom: 0px;
+}
+
+.member img {
+ width: 50%;
+ display: block;
+}
+
+ul.flowxl {
+ display: flex;
+ flex-wrap: wrap;
+ padding: 0;
+}
+
+ul.flowxl > li {
+ list-style-type: none;
+ text-align: center;
+ width: var(--xiconsize);
+ min-width: var(--xiconsize);
+ max-width: var(--xiconsize);
+ max-height: calc(var(--xiconsize) + 60px);
+ padding: 2px;
+ margin: 0.125rem; /* Use rem for margin */
+ overflow: hidden;
+ font-size: 1rem;
+ line-height: 1;
+}
+
+ul.flowxl > li > a {
+ display: block;
+ line-height: 1;
+ font-size: 1.2rem;
+}
+
+ul.flowxl li > a > img,
+ul.flowxl li > img {
+ display: block;
+ border-radius: 9px;
+ width: auto;
+ max-width: var(--xiconsize);
+ max-height: var(--xiconsize);
+ overflow: hidden;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+ul.flowxl li > a > img[src$=".svg"] {
+ max-width: 170px;
+ max-height: 170px;
+}
+
+.country-img {
+ height: 12.5rem; /* Use rem units for height */
+ width: 250px; /* Desired width */
+ object-fit: contain; /* Scales image to fit within the box while maintaining aspect ratio */
+ max-width: 100%; /* Ensures responsiveness */
+}
+
+.countries {
+ flex-wrap: wrap; /* Ensures images wrap if they exceed container width */
+ gap: 10px; /* Adds spacing between images */
+ justify-content: center; /* Centers the images horizontally */
+}
+
+.vpn-logo {
+ width: 200px; /* Set max-width for scaling */
+ max-width: 100%; /* Responsive width */
+ height: auto; /* Maintain aspect ratio */
+ object-fit: contain; /* Handle scaling gracefully */
+}
+
+.toesu-logo {
+ width: 200px; /* Set the default width */
+ max-width: 100%; /* Ensure image scales on smaller screens */
+ height: auto; /* Maintain original aspect ratio */
+ object-fit: contain; /* Gracefully handle image scaling */
+}
+
+@import url('https://fonts.googleapis.com/css2?family=Anonymous+Pro:ital,wght@0,400;0,700;1,400;1,700&display=swap');
+
+
diff --git a/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7SUc.woff2 b/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7SUc.woff2
new file mode 100644
index 0000000..d750914
--- /dev/null
+++ b/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7SUc.woff2
Binary files differ
diff --git a/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7.woff2 b/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7.woff2
new file mode 100644
index 0000000..d15208d
--- /dev/null
+++ b/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7.woff2
Binary files differ
diff --git a/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7SUc.woff2 b/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7SUc.woff2
new file mode 100644
index 0000000..024f077
--- /dev/null
+++ b/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7SUc.woff2
Binary files differ
diff --git a/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7SUc.woff2 b/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7SUc.woff2
new file mode 100644
index 0000000..479d010
--- /dev/null
+++ b/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7SUc.woff2
Binary files differ
diff --git a/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7SUc.woff2 b/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7SUc.woff2
new file mode 100644
index 0000000..de83a9c
--- /dev/null
+++ b/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7SUc.woff2
Binary files differ
diff --git a/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7SUc.woff2 b/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7SUc.woff2
new file mode 100644
index 0000000..6e7141f
--- /dev/null
+++ b/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7SUc.woff2
Binary files differ
diff --git a/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7SUc.woff2 b/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7SUc.woff2
new file mode 100644
index 0000000..a40c469
--- /dev/null
+++ b/assets/fonts/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7SUc.woff2
Binary files differ
diff --git a/assets/js/includes.js b/assets/js/includes.js
new file mode 100644
index 0000000..4af99f4
--- /dev/null
+++ b/assets/js/includes.js
@@ -0,0 +1,32 @@
+(async function () {
+ async function loadInto(selector, url) {
+ const mount = document.querySelector(selector);
+ if (!mount) return;
+
+ try {
+ const res = await fetch(url);
+ if (!res.ok) throw new Error("HTTP " + res.status);
+ const html = await res.text();
+ mount.innerHTML = html;
+ } catch (e) {
+ // Fail open: if includes fail, keep page usable.
+ // Intentionally no console noise.
+ }
+ }
+
+ // Pages opt-in by including: <div data-include="header"></div>
+ // and/or <div data-include="footer"></div>
+ await Promise.all([
+ loadInto('[data-include="header"]', "/partials/header.html"),
+ loadInto('[data-include="footer"]', "/partials/footer.html"),
+ ]);
+
+ // If a page uses includes, it likely removed the built-in header/footer.
+ // Re-run site.js behaviors after injection.
+ if (document.querySelector('[data-include="header"], [data-include="footer"]')) {
+ const s = document.createElement("script");
+ s.src = "/assets/js/site.js";
+ s.defer = true;
+ document.head.appendChild(s);
+ }
+})();
diff --git a/assets/js/pages/404.js b/assets/js/pages/404.js
new file mode 100644
index 0000000..a97a5b1
--- /dev/null
+++ b/assets/js/pages/404.js
@@ -0,0 +1,5 @@
+(function(){
+ var el = document.getElementById('y');
+ if (!el) return;
+ el.textContent = String(new Date().getFullYear());
+ })();
diff --git a/assets/js/pages/50x.js b/assets/js/pages/50x.js
new file mode 100644
index 0000000..a97a5b1
--- /dev/null
+++ b/assets/js/pages/50x.js
@@ -0,0 +1,5 @@
+(function(){
+ var el = document.getElementById('y');
+ if (!el) return;
+ el.textContent = String(new Date().getFullYear());
+ })();
diff --git a/assets/js/pages/guestbook_jp.js b/assets/js/pages/guestbook_jp.js
new file mode 100644
index 0000000..5f3a055
--- /dev/null
+++ b/assets/js/pages/guestbook_jp.js
@@ -0,0 +1,91 @@
+document.addEventListener("DOMContentLoaded", function () {
+ const form = document.getElementById("guestbook-form");
+ const entriesContainer = document.getElementById("guestbook-entries");
+ const captchaCanvas = document.getElementById("captcha-canvas");
+ const refreshCaptchaButton = document.getElementById("refresh-captcha");
+ const captchaInput = document.getElementById("captcha-input");
+ const GUESTBOOK_KEY = "guestbookEntries";
+
+ let currentCaptcha = "";
+
+ function generateCaptcha() {
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ let captcha = "";
+ for (let i = 0; i < 6; i++) captcha += chars.charAt(Math.floor(Math.random() * chars.length));
+ return captcha;
+ }
+
+ function drawCaptcha(captcha) {
+ const ctx = captchaCanvas.getContext("2d");
+ ctx.clearRect(0, 0, captchaCanvas.width, captchaCanvas.height);
+ ctx.font = "22px Arial";
+ ctx.fillStyle = "#000";
+ ctx.fillText(captcha, 10, 32);
+ }
+
+ function refreshCaptcha() {
+ currentCaptcha = generateCaptcha();
+ drawCaptcha(currentCaptcha);
+ }
+
+ function formatMessage(message) {
+ return message
+ .split("\n")
+ .map((line) => {
+ if (line.startsWith(">")) return `<span class="anonymous">${line}</span>`;
+ return line;
+ })
+ .join("<br>");
+ }
+
+ function loadEntries() {
+ const entries = JSON.parse(localStorage.getItem(GUESTBOOK_KEY)) || [];
+ entriesContainer.innerHTML = "";
+
+ entries
+ .slice()
+ .reverse()
+ .forEach((entry) => {
+ const entryElement = document.createElement("div");
+ entryElement.className = "entry";
+
+ const nameElement = document.createElement("h4");
+ if (entry.name === "Anonymous") nameElement.className = "anonymous";
+ nameElement.textContent = entry.name;
+ entryElement.appendChild(nameElement);
+
+ const messageElement = document.createElement("p");
+ messageElement.innerHTML = formatMessage(entry.message);
+ entryElement.appendChild(messageElement);
+
+ entriesContainer.appendChild(entryElement);
+ });
+ }
+
+ form.addEventListener("submit", function (event) {
+ event.preventDefault();
+
+ let name = document.getElementById("name").value.trim();
+ if (!name) name = "Anonymous";
+
+ const message = document.getElementById("message").value.trim();
+ const captchaValue = captchaInput.value.trim();
+
+ if (name && message && captchaValue === currentCaptcha) {
+ const entries = JSON.parse(localStorage.getItem(GUESTBOOK_KEY)) || [];
+ entries.push({ name, message });
+ localStorage.setItem(GUESTBOOK_KEY, JSON.stringify(entries));
+
+ form.reset();
+ document.getElementById("name").value = "Anonymous";
+ loadEntries();
+ refreshCaptcha();
+ } else {
+ alert("CAPTCHA is incorrect. Please try again.");
+ }
+ });
+
+ refreshCaptcha();
+ refreshCaptchaButton.addEventListener("click", refreshCaptcha);
+ loadEntries();
+ });
diff --git a/assets/js/pages/guestbook_zh.js b/assets/js/pages/guestbook_zh.js
new file mode 100644
index 0000000..5f3a055
--- /dev/null
+++ b/assets/js/pages/guestbook_zh.js
@@ -0,0 +1,91 @@
+document.addEventListener("DOMContentLoaded", function () {
+ const form = document.getElementById("guestbook-form");
+ const entriesContainer = document.getElementById("guestbook-entries");
+ const captchaCanvas = document.getElementById("captcha-canvas");
+ const refreshCaptchaButton = document.getElementById("refresh-captcha");
+ const captchaInput = document.getElementById("captcha-input");
+ const GUESTBOOK_KEY = "guestbookEntries";
+
+ let currentCaptcha = "";
+
+ function generateCaptcha() {
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ let captcha = "";
+ for (let i = 0; i < 6; i++) captcha += chars.charAt(Math.floor(Math.random() * chars.length));
+ return captcha;
+ }
+
+ function drawCaptcha(captcha) {
+ const ctx = captchaCanvas.getContext("2d");
+ ctx.clearRect(0, 0, captchaCanvas.width, captchaCanvas.height);
+ ctx.font = "22px Arial";
+ ctx.fillStyle = "#000";
+ ctx.fillText(captcha, 10, 32);
+ }
+
+ function refreshCaptcha() {
+ currentCaptcha = generateCaptcha();
+ drawCaptcha(currentCaptcha);
+ }
+
+ function formatMessage(message) {
+ return message
+ .split("\n")
+ .map((line) => {
+ if (line.startsWith(">")) return `<span class="anonymous">${line}</span>`;
+ return line;
+ })
+ .join("<br>");
+ }
+
+ function loadEntries() {
+ const entries = JSON.parse(localStorage.getItem(GUESTBOOK_KEY)) || [];
+ entriesContainer.innerHTML = "";
+
+ entries
+ .slice()
+ .reverse()
+ .forEach((entry) => {
+ const entryElement = document.createElement("div");
+ entryElement.className = "entry";
+
+ const nameElement = document.createElement("h4");
+ if (entry.name === "Anonymous") nameElement.className = "anonymous";
+ nameElement.textContent = entry.name;
+ entryElement.appendChild(nameElement);
+
+ const messageElement = document.createElement("p");
+ messageElement.innerHTML = formatMessage(entry.message);
+ entryElement.appendChild(messageElement);
+
+ entriesContainer.appendChild(entryElement);
+ });
+ }
+
+ form.addEventListener("submit", function (event) {
+ event.preventDefault();
+
+ let name = document.getElementById("name").value.trim();
+ if (!name) name = "Anonymous";
+
+ const message = document.getElementById("message").value.trim();
+ const captchaValue = captchaInput.value.trim();
+
+ if (name && message && captchaValue === currentCaptcha) {
+ const entries = JSON.parse(localStorage.getItem(GUESTBOOK_KEY)) || [];
+ entries.push({ name, message });
+ localStorage.setItem(GUESTBOOK_KEY, JSON.stringify(entries));
+
+ form.reset();
+ document.getElementById("name").value = "Anonymous";
+ loadEntries();
+ refreshCaptcha();
+ } else {
+ alert("CAPTCHA is incorrect. Please try again.");
+ }
+ });
+
+ refreshCaptcha();
+ refreshCaptchaButton.addEventListener("click", refreshCaptcha);
+ loadEntries();
+ });
diff --git a/assets/js/pages/hitcounter.js b/assets/js/pages/hitcounter.js
new file mode 100644
index 0000000..9c3f888
--- /dev/null
+++ b/assets/js/pages/hitcounter.js
@@ -0,0 +1,16 @@
+function countUniqueVisitors() {
+ const VISITOR_KEY = "uniqueVisitorCount";
+ let visitorCount = localStorage.getItem(VISITOR_KEY);
+ if (!visitorCount) visitorCount = 0;
+
+ const hasVisited = sessionStorage.getItem("hasVisited");
+ if (!hasVisited) {
+ visitorCount++;
+ localStorage.setItem(VISITOR_KEY, visitorCount);
+ sessionStorage.setItem("hasVisited", "true");
+ }
+
+ document.getElementById("visitor-counter").innerText = visitorCount;
+ }
+
+ countUniqueVisitors();
diff --git a/assets/js/pages/hitcounter_jp.js b/assets/js/pages/hitcounter_jp.js
new file mode 100644
index 0000000..9c3f888
--- /dev/null
+++ b/assets/js/pages/hitcounter_jp.js
@@ -0,0 +1,16 @@
+function countUniqueVisitors() {
+ const VISITOR_KEY = "uniqueVisitorCount";
+ let visitorCount = localStorage.getItem(VISITOR_KEY);
+ if (!visitorCount) visitorCount = 0;
+
+ const hasVisited = sessionStorage.getItem("hasVisited");
+ if (!hasVisited) {
+ visitorCount++;
+ localStorage.setItem(VISITOR_KEY, visitorCount);
+ sessionStorage.setItem("hasVisited", "true");
+ }
+
+ document.getElementById("visitor-counter").innerText = visitorCount;
+ }
+
+ countUniqueVisitors();
diff --git a/assets/js/pages/hitcounter_zh.js b/assets/js/pages/hitcounter_zh.js
new file mode 100644
index 0000000..9c3f888
--- /dev/null
+++ b/assets/js/pages/hitcounter_zh.js
@@ -0,0 +1,16 @@
+function countUniqueVisitors() {
+ const VISITOR_KEY = "uniqueVisitorCount";
+ let visitorCount = localStorage.getItem(VISITOR_KEY);
+ if (!visitorCount) visitorCount = 0;
+
+ const hasVisited = sessionStorage.getItem("hasVisited");
+ if (!hasVisited) {
+ visitorCount++;
+ localStorage.setItem(VISITOR_KEY, visitorCount);
+ sessionStorage.setItem("hasVisited", "true");
+ }
+
+ document.getElementById("visitor-counter").innerText = visitorCount;
+ }
+
+ countUniqueVisitors();
diff --git a/assets/js/pages/mstartpage-index.js b/assets/js/pages/mstartpage-index.js
new file mode 100644
index 0000000..db518ab
--- /dev/null
+++ b/assets/js/pages/mstartpage-index.js
@@ -0,0 +1,118 @@
+// Date and time functionality
+ function updateDateRealtime() {
+ const dateElement = document.getElementById("realtime-date");
+ const clockElement = document.getElementById("realtime-clock");
+
+ const days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
+ const months = ["January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"];
+
+ const now = new Date();
+
+ // Date parts
+ const dayName = days[now.getDay()];
+ const month = months[now.getMonth()];
+ const dayNumber = now.getDate();
+ const year = now.getFullYear();
+
+ const ordinal = (dayNumber > 3 && dayNumber < 21) ? 'th' :
+ (dayNumber % 10 === 1) ? 'st' :
+ (dayNumber % 10 === 2) ? 'nd' :
+ (dayNumber % 10 === 3) ? 'rd' : 'th';
+
+ dateElement.textContent = `${dayName}, ${month} ${dayNumber}${ordinal}, ${year}.`;
+
+ // Clock parts (12-hour format)
+ let hours = now.getHours();
+ const minutes = now.getMinutes();
+ const seconds = now.getSeconds();
+
+ const period = hours >= 12 ? 'PM' : 'AM';
+ hours = hours % 12 || 12;
+
+ const formattedHours = hours.toString().padStart(2, "0");
+ const formattedMinutes = minutes.toString().padStart(2, "0");
+ const formattedSeconds = seconds.toString().padStart(2, "0");
+
+ clockElement.textContent = `${formattedHours}:${formattedMinutes}:${formattedSeconds} ${period}`;
+ }
+
+ updateDateRealtime();
+ setInterval(updateDateRealtime, 1000);
+
+ // Menu toggle functionality
+ document.getElementById('menu-toggle').addEventListener('click', function() {
+ const menu = document.getElementById('main-menu');
+ menu.classList.toggle('show');
+ });
+
+ // Collapsible sections
+ const collapsibles = document.getElementsByClassName("collapsible");
+
+ for (let i = 0; i < collapsibles.length; i++) {
+ collapsibles[i].addEventListener("click", function() {
+ this.classList.toggle("active");
+ const content = this.nextElementSibling;
+ if (content.style.display === "block") {
+ content.style.display = "none";
+ } else {
+ content.style.display = "block";
+ }
+ });
+ }
+
+ // XXX Verification functionality
+ const xxxToggle = document.getElementById('xxx-toggle');
+ const xxxModal = document.getElementById('xxx-modal');
+ const xxxConfirm = document.getElementById('xxx-confirm');
+ const xxxDeny = document.getElementById('xxx-deny');
+ const xxxList = document.getElementById('xxx-list');
+
+ // Check if already verified
+ let isVerified = localStorage.getItem('xxx-verified') === 'true';
+ let isContentVisible = localStorage.getItem('xxx-visible') === 'true';
+
+ // Initialize based on stored state
+ if (isVerified && isContentVisible) {
+ xxxToggle.textContent = 'Hide XXX Content';
+ xxxList.style.display = 'block';
+ } else if (isVerified && !isContentVisible) {
+ xxxToggle.textContent = 'Show XXX Content';
+ xxxList.style.display = 'none';
+ }
+
+ xxxToggle.addEventListener('click', function() {
+ if (isVerified) {
+ // Toggle visibility if already verified
+ if (xxxList.style.display === 'block') {
+ // Hide the content
+ xxxList.style.display = 'none';
+ xxxToggle.textContent = 'Show XXX Content';
+ localStorage.setItem('xxx-visible', 'false');
+ } else {
+ // Show the content
+ xxxList.style.display = 'block';
+ xxxToggle.textContent = 'Hide XXX Content';
+ localStorage.setItem('xxx-visible', 'true');
+ }
+ } else {
+ // Show verification modal if not verified
+ xxxModal.style.display = 'flex';
+ }
+ });
+
+ xxxConfirm.addEventListener('click', function() {
+ // Set verification status
+ isVerified = true;
+ localStorage.setItem('xxx-verified', 'true');
+ localStorage.setItem('xxx-visible', 'true');
+
+ // Hide modal and show content
+ xxxModal.style.display = 'none';
+ xxxList.style.display = 'block';
+ xxxToggle.textContent = 'Hide XXX Content';
+ });
+
+ xxxDeny.addEventListener('click', function() {
+ xxxModal.style.display = 'none';
+ });
diff --git a/assets/js/pages/test.js b/assets/js/pages/test.js
new file mode 100644
index 0000000..ee1c831
--- /dev/null
+++ b/assets/js/pages/test.js
@@ -0,0 +1,10 @@
+document.getElementById("lastmod").textContent = document.lastModified;
+
+document.getElementById("menu-toggle").addEventListener("click", function() {
+ const sidebar = document.getElementById("mobile-sidebar");
+ sidebar.classList.toggle("visible");
+ if (sidebar.classList.contains("visible")) {
+ sidebar.scrollTop = 0;
+ window.scrollTo(0, 0);
+ }
+ });
diff --git a/assets/js/pages/test_jp.js b/assets/js/pages/test_jp.js
new file mode 100644
index 0000000..ee1c831
--- /dev/null
+++ b/assets/js/pages/test_jp.js
@@ -0,0 +1,10 @@
+document.getElementById("lastmod").textContent = document.lastModified;
+
+document.getElementById("menu-toggle").addEventListener("click", function() {
+ const sidebar = document.getElementById("mobile-sidebar");
+ sidebar.classList.toggle("visible");
+ if (sidebar.classList.contains("visible")) {
+ sidebar.scrollTop = 0;
+ window.scrollTo(0, 0);
+ }
+ });
diff --git a/assets/js/pages/test_zh.js b/assets/js/pages/test_zh.js
new file mode 100644
index 0000000..ee1c831
--- /dev/null
+++ b/assets/js/pages/test_zh.js
@@ -0,0 +1,10 @@
+document.getElementById("lastmod").textContent = document.lastModified;
+
+document.getElementById("menu-toggle").addEventListener("click", function() {
+ const sidebar = document.getElementById("mobile-sidebar");
+ sidebar.classList.toggle("visible");
+ if (sidebar.classList.contains("visible")) {
+ sidebar.scrollTop = 0;
+ window.scrollTo(0, 0);
+ }
+ });
diff --git a/assets/js/site.js b/assets/js/site.js
new file mode 100644
index 0000000..39891b7
--- /dev/null
+++ b/assets/js/site.js
@@ -0,0 +1,415 @@
+(function () {
+ function byId(id) {
+ return document.getElementById(id);
+ }
+
+ const menuBtn = byId("menu-toggle");
+ const nav = byId("site-nav");
+
+ const langToggle = byId("lang-toggle");
+ const langMenu = byId("lang-options");
+ const currentLang = byId("current-lang");
+
+ let openDropdown = null;
+
+ function closeMenu() {
+ if (!menuBtn || !nav) return;
+ menuBtn.classList.remove("active");
+ menuBtn.setAttribute("aria-expanded", "false");
+ nav.hidden = true;
+ if (openDropdown === "menu") openDropdown = null;
+ }
+
+ function closeLang() {
+ if (!langToggle || !langMenu) return;
+ langToggle.setAttribute("aria-expanded", "false");
+ langMenu.hidden = true;
+ if (openDropdown === "lang") openDropdown = null;
+ }
+
+ function toggleMenu(e) {
+ if (!menuBtn || !nav) return;
+ e.stopPropagation();
+
+ if (openDropdown === "lang") closeLang();
+
+ const open = menuBtn.getAttribute("aria-expanded") === "true";
+ if (open) {
+ closeMenu();
+ return;
+ }
+
+ menuBtn.classList.add("active");
+ menuBtn.setAttribute("aria-expanded", "true");
+ nav.hidden = false;
+ openDropdown = "menu";
+ }
+
+ function toggleLang(e) {
+ if (!langToggle || !langMenu) return;
+ e.stopPropagation();
+
+ if (openDropdown === "menu") closeMenu();
+
+ const open = langToggle.getAttribute("aria-expanded") === "true";
+ if (open) {
+ closeLang();
+ return;
+ }
+
+ langToggle.setAttribute("aria-expanded", "true");
+ langMenu.hidden = false;
+ openDropdown = "lang";
+ }
+
+ if (nav) nav.hidden = true;
+ if (langMenu) langMenu.hidden = true;
+
+ if (menuBtn && nav) {
+ menuBtn.addEventListener("click", toggleMenu);
+ nav.addEventListener("click", (e) => e.stopPropagation());
+ }
+
+ if (langToggle && langMenu) {
+ langToggle.addEventListener("click", toggleLang);
+ langMenu.addEventListener("click", (e) => e.stopPropagation());
+ }
+
+ if ((menuBtn && nav) || (langToggle && langMenu)) {
+ document.addEventListener("click", function (e) {
+ if (
+ menuBtn && nav &&
+ (menuBtn.contains(e.target) || nav.contains(e.target))
+ ) {
+ return;
+ }
+ if (
+ langToggle && langMenu &&
+ (langToggle.contains(e.target) || langMenu.contains(e.target))
+ ) {
+ return;
+ }
+ closeMenu();
+ closeLang();
+ });
+
+ document.addEventListener("keydown", function (e) {
+ if (e.key === "Escape") {
+ closeMenu();
+ closeLang();
+ }
+ });
+ }
+
+ if (currentLang) {
+ const docLang = (document.documentElement.lang || "en").toLowerCase();
+ currentLang.textContent = docLang === "zh" ? "ZH" : docLang === "ja" ? "JA" : "EN";
+ }
+
+ if (langMenu) {
+ langMenu.querySelectorAll("a[data-lang]").forEach((a) => {
+ a.addEventListener("click", function () {
+ localStorage.setItem("preferredLang", this.getAttribute("data-lang") || "en");
+ });
+ });
+ }
+
+ // Language menu hrefs: convert current page to *_zh.html / *_jp.html.
+ // Needs to work for both:
+ // - directory URLs: /foo/ (serve /foo/index.html)
+ // - file URLs: /foo/page.html
+ function updateLangMenuHrefs() {
+ const links = document.querySelectorAll("a[data-lang-href][data-lang]");
+ if (!links.length) return;
+
+ let path = window.location.pathname || "/";
+ if (!path.startsWith("/")) path = "/" + path;
+
+ const isDir = path.endsWith("/");
+ const file = isDir ? "index.html" : (path.split("/").pop() || "index.html");
+ const base = isDir ? path : path.slice(0, -file.length);
+
+ const baseName = file.replace(/_(zh|jp)\.html$/i, ".html");
+ const map = {
+ en: baseName,
+ zh: baseName.replace(/(_(zh|jp))?\.html$/i, "_zh.html"),
+ ja: baseName.replace(/(_(zh|jp))?\.html$/i, "_jp.html"),
+ };
+
+ links.forEach((a) => {
+ const lang = (a.getAttribute("data-lang") || "en").toLowerCase();
+ const targetFile = map[lang] || map.en;
+ a.href = base + targetFile;
+ });
+ }
+
+ function updateAriaCurrent() {
+ const path = window.location.pathname || "/";
+ const normalized = path.startsWith("/") ? path : "/" + path;
+ const isDir = normalized.endsWith("/");
+ const file = isDir ? "index.html" : (normalized.split("/").pop() || "index.html");
+ const base = isDir ? normalized : normalized.slice(0, -file.length);
+ const indexPath = base + "index.html";
+ const dirPath = base || "/";
+ const candidates = new Set([normalized, indexPath, dirPath, dirPath.replace(/\/$/, "")]);
+
+ document.querySelectorAll("#site-nav a[href]").forEach((a) => {
+ const href = a.getAttribute("href") || "";
+ const hrefUrl = new URL(href, window.location.origin);
+ if (hrefUrl.origin !== window.location.origin) {
+ a.removeAttribute("aria-current");
+ return;
+ }
+ const hrefPath = hrefUrl.pathname;
+ if (candidates.has(hrefPath)) {
+ a.setAttribute("aria-current", "page");
+ } else {
+ a.removeAttribute("aria-current");
+ }
+ });
+
+ const docLang = (document.documentElement.lang || "en").toLowerCase();
+ document.querySelectorAll("#lang-options a[data-lang]").forEach((a) => {
+ const lang = (a.getAttribute("data-lang") || "en").toLowerCase();
+ if (lang === docLang) {
+ a.setAttribute("aria-current", "page");
+ } else {
+ a.removeAttribute("aria-current");
+ }
+ });
+ }
+
+ // Run once now...
+ updateLangMenuHrefs();
+ updateAriaCurrent();
+ // ...and again after layout/injection settles.
+ setTimeout(updateLangMenuHrefs, 0);
+ setTimeout(updateAriaCurrent, 0);
+
+ // Home page blog list (optional)
+ const blogList = byId("blog-list");
+ if (blogList) {
+ const blogHost = "https://blog.sillylaird.ca";
+ const ctrl = new AbortController();
+ const timeout = setTimeout(() => ctrl.abort(), 3500);
+
+ fetch(blogHost + "/api/posts.php", { signal: ctrl.signal })
+ .then((r) => r.json())
+ .then((posts) => {
+ blogList.textContent = "";
+
+ const top = document.createElement("div");
+
+ const strong = document.createElement("strong");
+ strong.textContent = "Posts";
+ top.appendChild(strong);
+
+ top.appendChild(document.createTextNode(" ("));
+ const allLink = document.createElement("a");
+ allLink.href = blogHost;
+ allLink.textContent = "all";
+ top.appendChild(allLink);
+ top.appendChild(document.createTextNode(")"));
+
+ blogList.appendChild(top);
+
+ const list = document.createElement("ol");
+ (posts || []).forEach((post) => {
+ const li = document.createElement("li");
+
+ const a = document.createElement("a");
+ a.href = blogHost + "/" + String(post.path || "").replace(/^\/+/, "");
+ a.textContent = String(post.title || "Untitled");
+
+ const date = document.createElement("div");
+ date.className = "muted";
+
+ const y = String(post.year || "");
+ const m = String(post.month || "").padStart(2, "0");
+ const d = String(post.day || "").padStart(2, "0");
+ const iso = y && m && d ? `${y}-${m}-${d}` : "";
+
+ const time = document.createElement("time");
+ if (iso) time.setAttribute("datetime", iso);
+ time.textContent = iso || "";
+
+ li.appendChild(a);
+ li.appendChild(document.createElement("br"));
+ date.appendChild(time);
+ li.appendChild(date);
+
+ list.appendChild(li);
+ });
+
+ blogList.appendChild(list);
+
+ if (!list.children.length) {
+ const empty = document.createElement("div");
+ empty.className = "muted";
+ empty.textContent = "No posts yet.";
+ blogList.appendChild(empty);
+ }
+ })
+ .catch((err) => {
+ blogList.textContent = err && err.name === "AbortError"
+ ? "Blog list timed out."
+ : "Cannot load posts right now.";
+ })
+ .finally(() => clearTimeout(timeout));
+ }
+
+ const changelogList = byId("changelog-list");
+ if (changelogList) {
+ const changelogPage = "/changelog/";
+ const ctrl = new AbortController();
+ const timeout = setTimeout(() => ctrl.abort(), 3500);
+
+ fetch(changelogPage, { signal: ctrl.signal })
+ .then((r) => r.text())
+ .then((html) => {
+ changelogList.textContent = "";
+ const doc = new DOMParser().parseFromString(html, "text/html");
+ const entries = Array.from(doc.querySelectorAll(".changelog-entry"));
+
+ const top = document.createElement("div");
+
+ const strong = document.createElement("strong");
+ strong.textContent = "Entries";
+ top.appendChild(strong);
+
+ top.appendChild(document.createTextNode(" ("));
+ const allLink = document.createElement("a");
+ allLink.href = "/changelog/";
+ allLink.textContent = "all";
+ top.appendChild(allLink);
+ top.appendChild(document.createTextNode(")"));
+
+ changelogList.appendChild(top);
+
+ const list = document.createElement("ol");
+ entries.forEach((entry) => {
+ const li = document.createElement("li");
+
+ const titleText = (entry.querySelector("strong, b")?.textContent || "").trim();
+ const timeEl = entry.querySelector("time");
+ const dateText = (timeEl?.textContent || "").trim();
+ const dateIso = (timeEl?.getAttribute("datetime") || "").trim();
+ const bodyEl = entry.querySelector(".text");
+ const bodyText = (bodyEl?.textContent || "").trim();
+
+ const title = document.createElement("strong");
+ if (titleText) {
+ title.textContent = titleText;
+ li.appendChild(title);
+
+ if (dateText) {
+ const date = document.createElement("div");
+ date.className = "muted";
+ const time = document.createElement("time");
+ if (dateIso) time.setAttribute("datetime", dateIso);
+ time.textContent = dateText;
+ date.appendChild(time);
+ li.appendChild(date);
+ }
+ } else if (dateText) {
+ title.textContent = dateText;
+ li.appendChild(title);
+ }
+
+ if (bodyText) {
+ const body = document.createElement("div");
+ body.className = "changelog-body";
+ body.textContent = bodyText;
+ li.appendChild(body);
+ }
+
+ list.appendChild(li);
+ });
+
+ changelogList.appendChild(list);
+
+ if (!list.children.length) {
+ const empty = document.createElement("div");
+ empty.className = "muted";
+ empty.textContent = "No updates yet.";
+ changelogList.appendChild(empty);
+ }
+ })
+ .catch((err) => {
+ changelogList.textContent = err && err.name === "AbortError"
+ ? "Changelog list timed out."
+ : "Cannot load changelog right now.";
+ })
+ .finally(() => clearTimeout(timeout));
+ }
+
+ const resetBtn = byId("guestbook-reset");
+ if (resetBtn) {
+ resetBtn.addEventListener("click", () => {
+ const frame = byId("guestbook-form-iframe");
+ if (!frame) return;
+ setIframeLoading(frame);
+ const src = frame.getAttribute("src") || frame.src;
+ if (src) {
+ // Reset by reloading the iframe URL (works cross-origin too).
+ frame.setAttribute("src", src);
+ return;
+ }
+ if (frame.contentWindow) frame.contentWindow.location.reload();
+ });
+ }
+
+ function setIframeLoading(frame) {
+ if (!frame) return;
+ const clip = frame.closest(".iframe-clip");
+ if (!clip) return;
+ clip.classList.add("is-loading");
+ const onLoad = () => {
+ clip.classList.remove("is-loading");
+ };
+ frame.addEventListener("load", onLoad, { once: true });
+ }
+
+ function resetIframe(frame) {
+ if (!frame) return;
+ const src = frame.getAttribute("src") || frame.src;
+ if (src) {
+ frame.setAttribute("src", src);
+ return;
+ }
+ if (frame.contentWindow) frame.contentWindow.location.reload();
+ }
+
+ document.querySelectorAll("button[data-reset-iframe]").forEach((btn) => {
+ btn.addEventListener("click", () => {
+ const key = btn.getAttribute("data-reset-iframe");
+ if (!key) return;
+ const scope = btn.closest(".iframe-clip") || btn.parentElement || document;
+ const frame = scope.querySelector(`iframe[data-iframe=\"${key}\"]`) || document.querySelector(`iframe[data-iframe=\"${key}\"]`);
+ setIframeLoading(frame);
+ resetIframe(frame);
+ });
+ });
+
+ // Footer year: local time only.
+ (function () {
+ const el = byId("copyright-year");
+ if (!el) return;
+
+ el.textContent = String(new Date().getFullYear());
+ })();
+
+ // Footer last-updated: based on document.lastModified.
+ (function () {
+ const el = byId("last-updated");
+ if (!el) return;
+
+ const raw = document.lastModified;
+ const date = raw ? new Date(raw) : null;
+ if (!date || Number.isNaN(date.getTime())) return;
+
+ const iso = date.toISOString().slice(0, 10);
+ el.setAttribute("datetime", iso);
+ el.textContent = iso;
+ })();
+})();