www.git

www

~ Personal site — projects, posts, about. Built with sqzass.
git clone https://github.com/debuman/www.git
commit cea848899c151f83c4122f4d3eb211b2716acd8c
parent 67344fd68a8204c4277abe01aba03b9a7666fa91
Author: sqzer <110329244+sqzer-x@users.noreply.github.com>
Date: Tue, 4 Aug 2026 16:45:10 +0900

Hand the ornament over: fence, squeeze, Ctrl+/, socials

Wave 2 of the differentiation. Wave 1 moved the colours; this moves the devices
that were invented somewhere else.

The plumb line above page titles is gone. It was a 1px vertical gradient with
the reference's clamp, direction and stops, and the Korean comment attached to
it — "the one decorative-looking thing here, and it is not decoration" — was
written about a different device entirely. That device is the +++ fence, and it
is now where it belongs: between the title block and the body, once per page,
marking the same boundary it marks in the source file. Every page on this site
literally is a sqzass document that opens with +++.

Hover stops being light and starts being pressure. The radial pools under tiles
and cells, and the directional wash under ledger rows, were the reference's
named central primitive down to the alpha and the gradient stop. They are
replaced by scale(0.988) — the value the sqzass landing already uses, under a
comment that says the squeeze is not decoration, it is what the product does.
The names in this house all have their vowels pressed out. Pressing is the
point.

Artwork stops desaturating. Grayscale-at-rest arrived from the reference and
from nowhere else; ommp ships full-colour screenshots and argues for it. The
portrait and the covers now rest in colour, and the ommp cover finally looks
like ommp.

Section headings lose their flanking hairlines, and the class loses the name it
borrowed — .wing came from the reference's own comment. Whitespace was already
doing that job; one device per job.

Search moves to Ctrl + / , the binding the sqzass docs already ship, matched on
e.key rather than e.code because the layouts where slash needs a modifier put
the physical key somewhere else. It toggles, it stays out of the way while you
are typing in a field, and the Cmd-K binding is gone so there is one shortcut
and one label. The hint becomes an actual keycap — thin border, 3px radius —
because it is the one control that names a key, and the trigger holds its width
so it does not move under the pointer when the label changes on macOS.

The colophon gains GitHub and Instagram on their own row. Inside links and
outside links on one line blur which is which.

One fix on the way through: the ledger measure was set to 68ch, which resolves
against the body font on a container and came out half the intended width, so
titles wrapped. It is derived from --read now instead of picked.

Diffstat:
Mi18n/en.toml | 2++
Mi18n/ko.toml | 2++
Mstatic/css/main.css | 111+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mstatic/fonts/display-kr.woff2 | 0binary
Mstatic/js/search.js | 18+++++++++++++-----
Mtemplates/404.html | 2++
Mtemplates/about.html | 2++
Mtemplates/base.html | 4++--
Mtemplates/home.html | 4++--
Mtemplates/page.html | 2++
Mtemplates/partials/footer.html | 20++++++++++++++++----
Mtemplates/partials/icons.html | 6++++++
Mtemplates/post.html | 2++
Mtemplates/posts.html | 2++
Mtemplates/projects.html | 6++++--
15 files changed, 121 insertion(+), 62 deletion(-)

diff --git a/i18n/en.toml b/i18n/en.toml
@@ -39,6 +39,8 @@ active       = "Active"
 copy         = "Copy"
 copied       = "Copied"
 
+elsewhere    = "Elsewhere"
+
 feed         = "Feed"
 sitemap      = "Sitemap"
 
diff --git a/i18n/ko.toml b/i18n/ko.toml
@@ -39,6 +39,8 @@ active       = "유지 중"
 copy         = "복사"
 copied       = "복사됨"
 
+elsewhere    = "다른 곳"
+
 feed         = "피드"
 sitemap      = "사이트맵"
 
diff --git a/static/css/main.css b/static/css/main.css
@@ -178,7 +178,10 @@ a { color: inherit; text-decoration: none; }
   padding-inline: var(--gutter);
 }
 .wrap-read { max-width: var(--read); }
-.wrap-ledger { max-width: 68ch; }
+/* 읽는 폭에 날짜 거터가 나가 앉을 자리를 더한 값. 숫자를 따로 고르지 않고
+   --read 에서 유도하면 본문 폭을 바꿨을 때 원장이 따라온다.
+   (ch 는 컨테이너에 쓰면 본문 글꼴 기준이라 절반밖에 안 나온다 — 한 번 틀렸다.) */
+.wrap-ledger { max-width: calc(var(--read) + 12rem); }
 
 main { display: block; padding-bottom: clamp(5rem, 12vw, 9rem); }
 
@@ -252,9 +255,23 @@ main { display: block; padding-bottom: clamp(5rem, 12vw, 9rem); }
   transition: color var(--tr-ink);
 }
 .chip:hover { color: var(--ink); }
-.chip kbd { font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0; }
 .chip-lang { font-size: 0.7rem; }
 
+/* 키를 말하는 컨트롤 하나만 진짜 키캡이 된다 — 터미널에서 모는 도구라 얇은
+   테두리에 작은 반경. 네 칩에 전부 테두리를 두르면 테두리 없는 타일·원장·
+   액자와 싸워서 마스트헤드가 툴바가 된다. */
+.chip kbd {
+  font-family: var(--mono); font-size: 0.66rem; line-height: 1; letter-spacing: 0;
+  padding: 0.18rem 0.36rem;
+  border: 1px solid var(--rule-2); border-radius: 3px;
+  color: var(--ink-4);
+  transition: border-color var(--tr-ink), color var(--tr-ink);
+}
+.chip:hover kbd { border-color: var(--rule-3); color: var(--ink-3); }
+/* 라벨이 `Ctrl /` 에서 `⌘ /` 로 바뀌어도 폭이 안 변한다 — 누르려던 것이
+   도망가면 안 된다. */
+.chip-search { min-width: 6.5rem; justify-content: space-between; }
+
 .i-moon { display: none; }
 :root[data-theme="light"] .i-sun { display: none; }
 :root[data-theme="light"] .i-moon { display: inline; }
@@ -266,17 +283,23 @@ main { display: block; padding-bottom: clamp(5rem, 12vw, 9rem); }
 
 /* ---------- page head: 가운데 축 ---------- */
 
-/* 제목 위로 1px 다림줄이 내려온다. 페이지마다 같은 자리에서 시작한다는 표시이고,
-   이 사이트에서 유일한 장식처럼 보이는 것이며 장식이 아니다. */
 .page-head {
   padding-block: clamp(3.5rem, 9vw, 7rem) clamp(2rem, 4vw, 3.5rem);
   text-align: center;
 }
-.page-head::before {
-  content: ""; display: block; width: 1px;
-  height: clamp(1.75rem, 4vh, 2.75rem);
-  margin: 0 auto clamp(1.25rem, 3vw, 2rem);
-  background: linear-gradient(180deg, transparent, var(--accent));
+
+/* 프런트매터 펜스. 제목 블록이 끝나고 본문이 시작하는 자리에 한 페이지 한 번.
+   소스 파일에서 `+++` 가 표시하는 것과 같은 경계이고, 이 사이트의 모든 페이지가
+   실제로 `+++` 로 시작하는 sqzass 문서다. 장식처럼 보이는 유일한 것이며
+   장식이 아니다. (sqzass 문서 시트가 같은 장치를 같은 이유로 쓴다.) */
+.fence {
+  font-family: var(--mono);
+  font-size: 1.0625rem;
+  letter-spacing: 0.1em;
+  color: var(--ink-4);
+  text-align: center;
+  margin: 0 0 clamp(2rem, 4vw, 2.75rem);
+  user-select: none;
 }
 
 .page-title {
@@ -317,22 +340,18 @@ main { display: block; padding-bottom: clamp(5rem, 12vw, 9rem); }
 
 /* 가운데 제목 양옆으로 짧은 헤어라인. 페이지를 연으로 끊는 장치이고,
    위 여백이 넉넉해야 연이 연으로 읽힌다. */
-.wing {
-  display: flex; align-items: center; justify-content: center;
-  gap: clamp(1rem, 2.5vw, 1.75rem);
+/* 갈래 머리. 옆줄은 없다 — 한 가지 일에 한 가지 장치이고, 위아래 여백이
+   이미 연을 끊고 있다. 좁아지면 줄이는 게 아니라 뺀다. */
+.room-head {
   margin: clamp(4.5rem, 11vw, 7rem) 0 clamp(1.75rem, 4vw, 2.5rem);
   text-align: center;
 }
-.wing::before, .wing::after {
-  content: ""; width: clamp(1.5rem, 4vw, 2.25rem); height: 1px;
-  background: var(--rule-3); flex: none;
-}
-.wing h2 {
+.room-head h2 {
   margin: 0;
   font-size: clamp(1.35rem, 1.15rem + 0.9vw, 1.7rem);
   font-weight: 300; letter-spacing: 0.02em; line-height: 1.2;
 }
-.wing-sub {
+.room-head-sub {
   margin: -0.75rem auto clamp(1.75rem, 4vw, 2.5rem);
   max-width: 54ch; text-align: center;
   font-size: 0.92rem; color: var(--ink-3);
@@ -421,16 +440,13 @@ main { display: block; padding-bottom: clamp(5rem, 12vw, 9rem); }
   border: 1px solid var(--rule-2); border-radius: var(--radius);
   overflow: hidden; display: grid; place-items: center;
   background: var(--bg-2);
-  transition: border-color var(--tr-ink), transform var(--tr-light);
+  transition: border-color var(--tr-ink), transform var(--tr-ink);
 }
-.frame:hover .frame-media { border-color: var(--rule-3); transform: translateY(-4px); }
+.frame:hover .frame-media { border-color: var(--rule-3); transform: scale(0.988); }
 
-.frame-media img {
-  width: 100%; height: 100%; object-fit: cover;
-  filter: grayscale(1) brightness(0.92);
-  transition: filter var(--tr-light), transform var(--tr-light);
-}
-.frame:hover .frame-media img { filter: none; transform: scale(1.04); }
+/* 그림은 제 색으로 쉰다. 탈색해 두었다가 손이 닿으면 돌려주는 건 다른 집의
+   장치였다 — 여기서는 ommp 가 4K 컬러 스크린샷을 그대로 싣는 쪽이 기준이다. */
+.frame-media img { width: 100%; height: 100%; object-fit: cover; }
 
 .frame-mark {
   width: 42%; aspect-ratio: 1;
@@ -516,13 +532,11 @@ main { display: block; padding-bottom: clamp(5rem, 12vw, 9rem); }
   padding: clamp(1.25rem, 3vw, 1.75rem) 1rem;
   border: 0; background: transparent;
 }
-/* 빛웅덩이. 채우는 것이 아니라 아래에서 번지는 것이라 경계가 없다. */
-.tile::before {
-  content: ""; position: absolute; inset: 0; z-index: -1;
-  background: radial-gradient(closest-side, rgba(var(--ink-rgb), 0.05), transparent 72%);
-  opacity: 0; transition: opacity var(--tr-light);
-}
-.tile:hover::before, .tile:focus-within::before { opacity: 1; }
+/* 누르면 눌린다. 빛이 번지는 대신 물건이 압축된다 — 이 집의 이름들이 전부
+   모음을 눌러 뺀 것이고(sqzer, sqzass, ommp), 랜딩이 이미 같은 값을 쓴다.
+   0.988 은 눈이 알아채되 글자가 흐려지지는 않는 지점이다. */
+.tile { transition: transform var(--tr-ink); }
+.tile:hover, .tile:focus-within { transform: scale(0.988); }
 
 .tile-title {
   display: flex; align-items: center; justify-content: center; gap: 0.45rem;
@@ -605,9 +619,9 @@ html[lang="ko"] [data-tooltip]::after { letter-spacing: 0; text-transform: none;
   border-radius: var(--radius);
   transition: background var(--tr-light);
 }
-.ledger-link:hover, .ledger-link:focus-visible {
-  background: linear-gradient(100deg, rgba(var(--ink-rgb), 0.045), transparent 70%);
-}
+/* 행은 평평한 한 단만 올라온다. 방향성 wash 는 빛의 어휘이고, 이 테마의
+   어휘는 누름이다. */
+.ledger-link:hover, .ledger-link:focus-visible { background: var(--bg-2); }
 
 .ledger-date {
   font-family: var(--mono); font-size: 0.72rem;
@@ -805,12 +819,8 @@ html[lang="ko"] [data-tooltip]::after { letter-spacing: 0; text-transform: none;
 }
 /* hover 에서 위로 올린다. 그러지 않으면 이웃에 가려 두 변만 밝아진다. */
 .cell:hover { z-index: 1; border-color: var(--rule-2); }
-.cell::before {
-  content: ""; position: absolute; inset: 0; z-index: -1;
-  background: radial-gradient(closest-side, rgba(var(--ink-rgb), 0.045), transparent 72%);
-  opacity: 0; transition: opacity var(--tr-light);
-}
-.cell:hover::before { opacity: 1; }
+.cell { transition: transform var(--tr-ink), border-color var(--tr-ink); }
+.cell:hover, .cell:focus-within { transform: scale(0.988); }
 
 .cell-index {
   display: block; font-family: var(--mono); font-size: 0.66rem;
@@ -871,10 +881,7 @@ html[lang="ko"] [data-tooltip]::after { letter-spacing: 0; text-transform: none;
 .portrait img {
   display: block; width: 100%; aspect-ratio: 4 / 5; object-fit: cover;
   border: 1px solid var(--rule-2); border-radius: var(--radius);
-  filter: grayscale(1) brightness(0.95);
-  transition: filter var(--tr-light);
 }
-.about-head:hover .portrait img { filter: none; }
 
 /* ---------- toc & pager ---------- */
 
@@ -1035,6 +1042,16 @@ html[lang="ko"] [data-tooltip]::after { letter-spacing: 0; text-transform: none;
   color: var(--ink-4); transition: color var(--tr-ink);
 }
 .colophon-nav a:hover { color: var(--ink-2); }
+/* 갈래 링크와 소셜은 다른 줄에 선다 — 안을 도는 것과 밖으로 나가는 것이다. */
+.colophon-social { display: flex; flex-wrap: wrap; justify-content: center; gap: 1.75rem; flex-basis: 100%; }
+.colophon-social a {
+  display: inline-flex; align-items: center; gap: 0.45rem;
+  font-size: 0.66rem; letter-spacing: 0.22em; text-transform: uppercase;
+  color: var(--ink-3); transition: color var(--tr-ink);
+}
+.colophon-social a:hover { color: var(--accent); }
+.colophon-social svg { flex: none; }
+
 .colophon-note { margin: 0; flex-basis: 100%; font-size: 0.66rem; letter-spacing: 0.16em; color: var(--ink-4); }
 .colophon-note a { color: var(--ink-3); transition: color var(--tr-ink); }
 .colophon-note a:hover { color: var(--accent); }
@@ -1047,7 +1064,6 @@ html[lang="ko"] [data-tooltip]::after { letter-spacing: 0; text-transform: none;
    폴백하는데 공백만 mono 폭이라 어절 사이가 두 배로 벌어진다.
    구조는 크기·굵기·색으로도 읽히고 그쪽은 도움이 필요 없다. */
 html[lang="ko"] .eyebrow,
-html[lang="ko"] .wing-minor,
 html[lang="ko"] .fact-label,
 html[lang="ko"] .toc-label,
 html[lang="ko"] .pager-dir,
@@ -1055,6 +1071,7 @@ html[lang="ko"] .prose th,
 html[lang="ko"] .crumbs,
 html[lang="ko"] .palette-crumb,
 html[lang="ko"] .colophon-nav a,
+html[lang="ko"] .colophon-social a,
 html[lang="ko"] .colophon-note,
 html[lang="ko"] .nav a,
 html[lang="ko"] .chip,
@@ -1077,7 +1094,7 @@ html[lang="ko"] .page-title,
 html[lang="ko"] .hero-name,
 html[lang="ko"] .cta-title,
 html[lang="ko"] .fact-value { font-weight: 300; }
-html[lang="ko"] .wing h2,
+html[lang="ko"] .room-head h2,
 html[lang="ko"] .block-title,
 html[lang="ko"] .feature-title,
 html[lang="ko"] .ledger-title { font-weight: 350; }
diff --git a/static/fonts/display-kr.woff2 b/static/fonts/display-kr.woff2
Binary files differ.
diff --git a/static/js/search.js b/static/js/search.js
@@ -29,10 +29,11 @@
   var loading = false;
   var cursor = -1;
 
-  /* 수정자 키를 읽는 사람의 것으로 바꿔 쓴다. 템플릿이 쓴 값은 JS 가 없을 때의 것. */
+  /* 단축키 힌트는 페이지의 성질이 아니라 읽는 사람 키보드의 성질이라 여기서 쓴다.
+     템플릿이 넣어 둔 값은 JS 가 없을 때 남는 것. */
   var mac = /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);
   var kbd = trigger.querySelector("kbd");
-  if (kbd) kbd.textContent = mac ? "⌘ K" : "Ctrl K";
+  if (kbd) kbd.textContent = mac ? "⌘ /" : "Ctrl /";
 
   function load() {
     if (rows || loading) return Promise.resolve();
@@ -140,11 +141,18 @@
 
   trigger.addEventListener("click", open);
 
+  /* Ctrl/⌘ + /. e.code 가 아니라 e.key 로 잡는다 — 슬래시에 수정자가 필요한
+     레이아웃에서는 물리 키가 딴 데 있고, 중요한 건 읽는 사람이 무엇을 쳤는가다.
+     preventDefault 는 Firefox 가 맨 / 를 빠른 검색에 묶어 두기 때문이다. */
   document.addEventListener("keydown", function (e) {
-    if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
-      e.preventDefault();
-      open();
+    if (!(e.ctrlKey || e.metaKey) || e.key !== "/") return;
+    /* 글을 치고 있는 중이면 끼어들지 않는다. */
+    var el = document.activeElement;
+    if (el && (el.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName))) {
+      if (el !== input) return;
     }
+    e.preventDefault();
+    dialog.open ? dialog.close() : open();
   });
 
   input.addEventListener("input", function () { render(input.value); });
diff --git a/templates/404.html b/templates/404.html
@@ -12,6 +12,8 @@
     <p class="page-lede">{{ t("not_found_body") }}</p>
   </header>
 
+  <div class="fence" aria-hidden="true">+++</div>
+
   <nav class="btn-row" aria-label="{{ t("menu") }}">
     {%- for s in site.sections %}
     <a class="btn btn-ghost" href="{{ s.url }}">{{ s.title }}</a>
diff --git a/templates/about.html b/templates/about.html
@@ -21,6 +21,8 @@
     {%- endif %}
   </header>
 
+  <div class="fence" aria-hidden="true">+++</div>
+
   <div class="prose">
     {{ page.content | safe }}
   </div>
diff --git a/templates/base.html b/templates/base.html
@@ -88,11 +88,11 @@
 
     <div class="tools">
       {%- if site.search %}
-      <button class="chip search-trigger" type="button" data-index="{{ site.search }}"
+      <button class="chip chip-search search-trigger" type="button" data-index="{{ site.search }}"
               aria-label="{{ t("search_the_site") }}">
         <svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>
         {#- 라벨은 search.js 가 읽는 사람의 수정자 키로 바꿔 쓴다. 이건 JS 가 없을 때의 값. #}
-        <kbd>Ctrl K</kbd>
+        <kbd>Ctrl /</kbd>
       </button>
       {%- endif %}
       {%- for lang in page.translations %}
diff --git a/templates/home.html b/templates/home.html
@@ -28,7 +28,7 @@
 {%- for s in site.sections %}
 {%- if s.url == home ~ "posts/" and s.pages %}
 <section class="room wrap wrap-ledger">
-  <div class="wing"><h2>{{ t("writing") }}</h2></div>
+  <div class="room-head"><h2>{{ t("writing") }}</h2></div>
 
   {%- for p in s.pages %}
   {%- if loop.first %}
@@ -56,7 +56,7 @@
 {%- for s in site.sections %}
 {%- if s.url == home ~ "projects/" and s.pages %}
 <section class="room wrap">
-  <div class="wing"><h2>{{ s.title }}</h2></div>
+  <div class="room-head"><h2>{{ s.title }}</h2></div>
   <div class="tiles">
     {%- for p in s.pages %}
     <article class="tile">
diff --git a/templates/page.html b/templates/page.html
@@ -10,6 +10,8 @@
     {%- endif %}
   </header>
 
+  <div class="fence" aria-hidden="true">+++</div>
+
   <div class="prose">
     {{ page.content | safe }}
   </div>
diff --git a/templates/partials/footer.html b/templates/partials/footer.html
@@ -1,8 +1,14 @@
-{#- 푸터. 위 내비와 같은 자료(site.sections)를 쓰므로 갈래가 늘어도 두 곳을
-    고칠 일이 없다. 콜로폰은 이 사이트가 무엇으로 만들어졌는지 한 줄로 말한다 —
-    자기 도구로 자기 사이트를 짓는 것 자체가 그 도구에 대한 주장이다. #}
+{#- 콜로폰. 갈래 링크는 위 내비와 같은 자료(site.sections)를 쓰므로 갈래가 늘어도
+    두 곳을 고칠 일이 없다.
+
+    소셜은 따로 선다. 왼쪽 갈래 링크는 이 사이트 안을 도는 것이고 오른쪽은 밖으로
+    나가는 것이라, 같은 줄에 섞으면 어느 쪽이 어디로 가는지 흐려진다.
+
+    마지막 줄은 이 사이트가 무엇으로 만들어졌는지 말한다 — 자기 도구로 자기 사이트를
+    짓는 것 자체가 그 도구에 대한 주장이다. #}
 {%- set ko = page.language == "ko" %}
 {%- set home = site.base_path ~ ("/ko/" if ko else "/") %}
+{% from "partials/icons.html" import i_github, i_instagram %}
 <footer class="colophon">
   <div class="colophon-in">
     <nav class="colophon-nav" aria-label="{{ t("menu") }}">
@@ -13,7 +19,13 @@
       <a href="{{ site.feed }}">{{ t("feed") }}</a>
       {%- endif %}
       <a href="{{ site.base_path }}/sitemap.xml">{{ t("sitemap") }}</a>
-      <a href="https://github.com/sqzer-x">GitHub</a>
+    </nav>
+
+    {#- rel="me" 는 이 계정들이 같은 사람이라는 선언이다. Mastodon 류가 이걸 읽고,
+        읽지 않는 곳에서도 틀린 말이 아니다. #}
+    <nav class="colophon-social" aria-label="{{ t("elsewhere") }}">
+      <a href="https://github.com/sqzer-x" rel="me" target="_blank">{{ i_github() }}<span>GitHub</span></a>
+      <a href="https://www.instagram.com/sqzer_x" rel="me" target="_blank">{{ i_instagram() }}<span>Instagram</span></a>
     </nav>
 
     <p class="colophon-note">
diff --git a/templates/partials/icons.html b/templates/partials/icons.html
@@ -9,6 +9,12 @@
 
 {% macro i_repo() %}<svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true" focusable="false"><path fill="currentColor" d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>{% endmacro %}
 
+{#- 소셜 마크. fill 이 currentColor 라 잉크 램프를 타고 hover 에 액센트가 된다 —
+    별도 CSS 가 필요 없다. em 단위라 옆 글자 크기를 따라간다. #}
+{% macro i_github() %}<svg viewBox="0 0 24 24" width="1.05em" height="1.05em" aria-hidden="true" focusable="false"><path fill="currentColor" d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>{% endmacro %}
+
+{% macro i_instagram() %}<svg viewBox="0 0 24 24" width="1.05em" height="1.05em" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="1.9"><rect x="2.6" y="2.6" width="18.8" height="18.8" rx="5.2"/><circle cx="12" cy="12" r="4.2"/><circle cx="17.4" cy="6.6" r="1.15" fill="currentColor" stroke="none"/></svg>{% endmacro %}
+
 {% macro i_out() %}<svg class="i-out" viewBox="0 0 24 24" width="14" height="14" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 17 17 7M9 7h8v8"/></svg>{% endmacro %}
 
 {% macro i_arrow() %}<svg class="i-arrow" viewBox="0 0 24 24" width="14" height="14" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h13M13 6l6 6-6 6"/></svg>{% endmacro %}
diff --git a/templates/post.html b/templates/post.html
@@ -22,6 +22,8 @@
     {%- endif %}
   </header>
 
+  <div class="fence" aria-hidden="true">+++</div>
+
   {%- if page.toc and page.toc_entries %}
   <nav class="toc" aria-label="{{ t("on_this_page") }}">
     <p class="toc-label">{{ t("on_this_page") }}</p>
diff --git a/templates/posts.html b/templates/posts.html
@@ -12,6 +12,8 @@
     {%- endif %}
   </header>
 
+  <div class="fence" aria-hidden="true">+++</div>
+
   {%- if page.content %}
   <div class="prose prose-intro">{{ page.content | safe }}</div>
   {%- endif %}
diff --git a/templates/projects.html b/templates/projects.html
@@ -20,6 +20,8 @@
     {%- endif %}
   </header>
 
+  <div class="fence" aria-hidden="true">+++</div>
+
   {%- if page.content %}
   <div class="prose prose-intro">{{ page.content | safe }}</div>
   {%- endif %}
@@ -65,9 +67,9 @@
       분류가 곧 저자의 편집 결정이 된다. #}
   {%- for g in page.extra.group %}
   <section id="{{ g.id }}">
-    <div class="wing"><h2>{{ g.label }}</h2></div>
+    <div class="room-head"><h2>{{ g.label }}</h2></div>
     {%- if g.note %}
-    <p class="wing-sub">{{ g.note }}</p>
+    <p class="room-head-sub">{{ g.note }}</p>
     {%- endif %}
 
     <div class="tiles">