diff --git a/bin/unit_tests/assets/html/body_height_miscalculation.css b/bin/unit_tests/assets/html/body_height_miscalculation.css
new file mode 100644
index 000000000..fcfb3df1e
--- /dev/null
+++ b/bin/unit_tests/assets/html/body_height_miscalculation.css
@@ -0,0 +1,3196 @@
+:root {
+ --bg: #0c1520;
+ --bg-surface: #141f2e;
+ --bg-surface-2: #1a2637;
+ --text: #bfc8d2;
+ --text-muted: #566a7c;
+ --text-bright: #e4e9ee;
+ --accent: #d4a853;
+ --accent-hover: #e4bc6a;
+ --tag-color: #7eb8a8;
+ --eng-color: #3d7b8a;
+ --danger: #e07a5f;
+ --border: rgba(255, 255, 255, 0.07);
+ --border-strong: rgba(255, 255, 255, 0.12);
+ --max-width: 54rem;
+ --read-width: 54rem;
+ --font-text: text, "Roboto", -apple-system, BlinkMacSystemFont, sans-serif;
+ --font-mono: code, "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
+ --font-serif: serif, "Fraunces", "Iowan Old Style", Georgia, serif;
+}
+
+*, *::before, *::after {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+html {
+ font-size: 17.5px;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ scroll-behavior: smooth;
+}
+
+@media (max-width: 560px) {
+ html {
+ font-size: 16px;
+ }
+}
+
+body {
+ font-family: var(--font-text);
+ font-weight: 300;
+ line-height: 1.65;
+ color: var(--text);
+ background: var(--bg);
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ overflow-x: clip;
+}
+
+::selection {
+ background: rgba(212, 168, 83, 0.25);
+ color: var(--text-bright);
+}
+
+a {
+ color: var(--accent);
+ text-decoration: none;
+ transition: color 0.15s;
+}
+a:hover {
+ color: var(--accent-hover);
+}
+
+img {
+ max-width: 100%;
+ height: auto;
+}
+
+/* ============ Nav ============ */
+.site-nav {
+ padding: 1.25rem 0;
+ border-bottom: 1px solid var(--border);
+}
+.site-nav .wrap {
+ max-width: var(--max-width);
+ margin: 0 auto;
+ padding: 0 1.5rem;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+.site-nav .brand {
+ font-family: var(--font-mono);
+ font-size: 0.9rem;
+ color: var(--text-bright);
+ letter-spacing: -0.02em;
+}
+.site-nav .links {
+ display: flex;
+ gap: 1.5rem;
+ font-family: var(--font-mono);
+ font-size: 0.8rem;
+}
+.site-nav .links a {
+ color: var(--text-muted);
+}
+.site-nav .links a:hover,
+.site-nav .links a[aria-current="page"] {
+ color: var(--text-bright);
+}
+
+/* ============ Shell ============ */
+main.shell {
+ flex: 1;
+ max-width: var(--max-width);
+ margin: 0 auto;
+ width: 100%;
+ padding: 3rem 1.5rem 5rem;
+ animation: fadeIn 0.3s ease;
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(0.4rem);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+/* ============ Footer ============ */
+footer.site-footer {
+ margin-top: auto;
+ border-top: 1px solid var(--border);
+ padding: 2rem 0;
+}
+footer.site-footer .wrap {
+ max-width: var(--max-width);
+ margin: 0 auto;
+ padding: 0 1.5rem;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 1rem;
+ flex-wrap: wrap;
+ font-family: var(--font-mono);
+ font-size: 0.72rem;
+ color: var(--text-muted);
+}
+footer.site-footer a {
+ color: var(--text-muted);
+}
+footer.site-footer a:hover {
+ color: var(--accent);
+}
+
+/* ============ Masthead (home / about) ============ */
+.masthead {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) 12.5rem;
+ gap: 3rem;
+ align-items: start;
+ padding-bottom: 2.5rem;
+ margin-bottom: 2.5rem;
+ border-bottom: 1px solid var(--border-strong);
+}
+.masthead:has(.portrait-wrap.has-outline) {
+ grid-template-columns: minmax(0, 1fr) 21rem;
+}
+.mh-eyebrow {
+ font-family: var(--font-mono);
+ font-size: 0.68rem;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: var(--text-muted);
+ margin-bottom: 1.5rem;
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+}
+.mh-eyebrow .dot {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: var(--accent);
+ box-shadow: 0 0 6px var(--accent);
+}
+.mh-eyebrow .sep {
+ flex: 1;
+ height: 1px;
+ background: var(--border);
+}
+
+.masthead h1 {
+ font-family: var(--font-serif);
+ font-weight: 300;
+ font-variation-settings: "opsz" 144;
+ font-size: clamp(2.3rem, 5vw, 4rem);
+ line-height: 1;
+ letter-spacing: -0.03em;
+ color: var(--text-bright);
+ text-wrap: balance;
+ margin-bottom: 1rem;
+}
+.masthead h1 em {
+ font-style: italic;
+ font-variation-settings: "opsz" 144;
+ color: var(--accent);
+}
+.masthead h1 .accent {
+ font-style: normal;
+ color: var(--accent);
+}
+.masthead .dek {
+ font-family: var(--font-text);
+ font-weight: 300;
+ font-style: normal;
+ font-size: 1.1rem;
+ line-height: 1.55;
+ color: var(--text);
+ max-width: 38rem;
+ margin-bottom: 1.5rem;
+}
+
+.mh-stats {
+ display: flex;
+ gap: 2rem;
+ flex-wrap: wrap;
+ font-family: var(--font-mono);
+ font-size: 0.7rem;
+ color: var(--text-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.1em;
+ padding-top: 1rem;
+ border-top: 1px solid var(--border);
+}
+.mh-stats div b {
+ display: block;
+ font-family: var(--font-serif);
+ font-style: normal;
+ font-weight: 400;
+ font-size: 1.5rem;
+ color: var(--text-bright);
+ margin-bottom: 0.15rem;
+ letter-spacing: -0.02em;
+ text-transform: none;
+}
+
+.masthead-right {
+ position: relative;
+}
+.portrait-wrap {
+ position: relative;
+ aspect-ratio: 1;
+ border-radius: 0.35rem;
+ overflow: hidden;
+ display: block;
+}
+.portrait-wrap.has-outline {
+ border: 1px solid var(--border);
+}
+.portrait-art {
+ position: absolute;
+ inset: 0;
+}
+.portrait-art img {
+ width: 100%;
+ height: 100%;
+ display: block;
+}
+.portrait-art img[src$=".jpeg"],
+.portrait-art img[src$=".jpg"],
+.portrait-art img[src$=".png"] {
+ object-fit: cover;
+ filter: grayscale(0.15) contrast(1.02);
+}
+.portrait-art img[src$=".svg"] {
+ object-fit: contain;
+ padding: 8%;
+ opacity: 0.92;
+}
+
+/* White line-art overlay on photo portrait (about page) — body only, face masked out */
+.portrait-outline {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ object-position: center;
+ pointer-events: none;
+ opacity: 0;
+ filter: drop-shadow(0 0 4px rgba(255, 255, 255, 0.45));
+ animation:
+ portrait-outline-in 1.4s ease-out 0.3s forwards,
+ portrait-outline-breathe 6s ease-in-out 1.7s infinite;
+ -webkit-mask-image: linear-gradient(
+ to bottom,
+ transparent 0%,
+ transparent 38%,
+ rgba(0, 0, 0, 0.4) 46%,
+ black 54%,
+ black 100%
+ );
+ mask-image: linear-gradient(
+ to bottom,
+ transparent 0%,
+ transparent 38%,
+ rgba(0, 0, 0, 0.4) 46%,
+ black 54%,
+ black 100%
+ );
+}
+@keyframes portrait-outline-in {
+ 0% {
+ opacity: 0;
+ transform: scale(0.985);
+ }
+ 100% {
+ opacity: 0.85;
+ transform: scale(1);
+ }
+}
+@keyframes portrait-outline-breathe {
+ 0%, 100% {
+ opacity: 0.78;
+ }
+ 50% {
+ opacity: 0.95;
+ }
+}
+.portrait-wrap.has-outline .portrait-art img {
+ filter: grayscale(0.15) contrast(1) brightness(0.95);
+}
+
+/* Floating dots overlay on portrait */
+.portrait-dot {
+ position: absolute;
+ width: 3px;
+ height: 3px;
+ background: var(--text-bright);
+ border-radius: 50%;
+ box-shadow: 0 0 6px var(--text-bright);
+ animation: portrait-float var(--dur, 5s) ease-in-out var(--del, 0s) infinite
+ alternate;
+ pointer-events: none;
+}
+@keyframes portrait-float {
+ 0% {
+ left: var(--x1);
+ top: var(--y1);
+ opacity: 0;
+ }
+ 15% {
+ opacity: 0.7;
+ }
+ 85% {
+ opacity: 0.7;
+ }
+ 100% {
+ left: var(--x2);
+ top: var(--y2);
+ opacity: 0;
+ }
+}
+.portrait-caption {
+ font-family: var(--font-mono);
+ font-size: 0.62rem;
+ color: var(--text-muted);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ margin-top: 0.75rem;
+ display: flex;
+ justify-content: space-between;
+}
+.portrait-caption .loc {
+ color: var(--accent);
+}
+
+/* ============ Section bar ============ */
+.section-bar {
+ display: grid;
+ grid-template-columns: auto 1fr auto auto;
+ align-items: baseline;
+ gap: 1rem;
+ padding-bottom: 0.75rem;
+ border-bottom: 1px solid var(--border-strong);
+ margin-bottom: 1.75rem;
+ font-family: var(--font-mono);
+}
+.section-bar .sb-num {
+ font-size: 0.68rem;
+ color: var(--accent);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ padding: 0.2rem 0.55rem;
+ border: 1px solid rgba(212, 168, 83, 0.3);
+ border-radius: 0.15rem;
+ background: rgba(212, 168, 83, 0.04);
+}
+.section-bar .sb-label {
+ font-family: var(--font-serif);
+ font-weight: 400;
+ font-size: 1.4rem;
+ color: var(--text-bright);
+ letter-spacing: -0.015em;
+}
+.section-bar .sb-label em {
+ font-style: italic;
+ color: var(--accent);
+}
+.section-bar .sb-count {
+ font-size: 0.7rem;
+ color: var(--text-muted);
+ letter-spacing: 0.06em;
+}
+.section-bar .sb-filter {
+ display: flex;
+ gap: 0.35rem;
+ flex-wrap: wrap;
+}
+.section-bar .sb-filter button {
+ background: transparent;
+ border: 1px solid var(--border);
+ color: var(--text-muted);
+ font-family: inherit;
+ font-size: 0.62rem;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+ padding: 0.28rem 0.55rem;
+ border-radius: 0.15rem;
+ cursor: pointer;
+ transition: color 0.15s, border-color 0.15s, background 0.15s;
+}
+.section-bar .sb-filter button:hover {
+ color: var(--text-bright);
+ border-color: var(--border-strong);
+}
+.section-bar .sb-filter button.on {
+ color: var(--accent);
+ border-color: rgba(212, 168, 83, 0.4);
+ background: rgba(212, 168, 83, 0.08);
+}
+
+/* ============ Featured block (home) ============ */
+.featured {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) 12.5rem;
+ gap: 3rem;
+ padding: 0.5rem 0 2.5rem;
+ border-bottom: 1px solid var(--border);
+ margin-bottom: 2.5rem;
+ align-items: center;
+}
+.feat-main .f-eyebrow {
+ font-family: var(--font-mono);
+ font-size: 0.65rem;
+ color: var(--accent);
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ margin-bottom: 0.9rem;
+ display: flex;
+ align-items: center;
+ gap: 0.6rem;
+}
+.feat-main .f-eyebrow .pulse {
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: var(--accent);
+ animation: pulse 2s ease-in-out infinite;
+}
+@keyframes pulse {
+ 0%, 100% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0.35;
+ }
+}
+
+.feat-main h2 {
+ font-family: var(--font-serif);
+ font-weight: 400;
+ font-variation-settings: "opsz" 96;
+ font-size: clamp(1.6rem, 3vw, 2.3rem);
+ line-height: 1.1;
+ letter-spacing: -0.02em;
+ color: var(--text-bright);
+ text-wrap: balance;
+ margin-bottom: 1rem;
+}
+.feat-main h2 a {
+ color: inherit;
+}
+.feat-main h2 a:hover {
+ color: var(--accent);
+}
+.feat-main .dek {
+ font-family: var(--font-text);
+ font-weight: 300;
+ font-size: 1.05rem;
+ line-height: 1.6;
+ color: var(--text);
+ max-width: 40rem;
+ margin-bottom: 1.25rem;
+ text-wrap: pretty;
+}
+.feat-main .meta {
+ display: flex;
+ gap: 1.25rem;
+ flex-wrap: wrap;
+ font-family: var(--font-mono);
+ font-size: 0.68rem;
+ color: var(--text-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.1em;
+}
+.feat-main .meta .tag {
+ color: var(--tag-color);
+}
+.feat-main .meta .tag::before {
+ content: "#";
+ opacity: 0.6;
+}
+
+.feat-art {
+ aspect-ratio: 1;
+ border: 1px solid var(--border);
+ border-radius: 0.35rem;
+ overflow: hidden;
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+.feat-art > div {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+.feat-art svg {
+ width: 100%;
+ height: 100%;
+ transform: scale(0.49);
+ transform-origin: center center;
+ display: block;
+}
+.feat-art.has-cover img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ display: block;
+}
+.feat-art:not(.has-cover) .art-spin,
+.feat-art:not(.has-cover) .art-orbit {
+ animation: none !important;
+}
+.feat-art .art-draw {
+ animation: art-build-slow 18s ease-in-out calc(var(--d, 0s) * 4) infinite;
+}
+.feat-art .art-dot,
+.feat-art .art-pulse-dot {
+ animation: art-build-dot-slow 18s ease-in-out calc(var(--d, 0s) * 4) infinite;
+}
+@keyframes art-build-slow {
+ 0% {
+ stroke-dashoffset: 1;
+ opacity: 0;
+ }
+ 8% {
+ opacity: 1;
+ }
+ 55% {
+ stroke-dashoffset: 0;
+ opacity: 1;
+ }
+ 85% {
+ stroke-dashoffset: 0;
+ opacity: 1;
+ }
+ 100% {
+ stroke-dashoffset: 0;
+ opacity: 0;
+ }
+}
+@keyframes art-build-dot-slow {
+ 0%, 100% {
+ opacity: 0;
+ }
+ 15%, 85% {
+ opacity: var(--op, 0.6);
+ }
+}
+.feat-art::after {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background: radial-gradient(
+ ellipse at center,
+ transparent 60%,
+ rgba(12, 21, 32, 0.3) 100%
+ );
+ pointer-events: none;
+}
+
+/* ============ Post list (home) ============ */
+.post-list {
+ list-style: none;
+}
+.post-list > li {
+ display: grid;
+ grid-template-columns: 5rem minmax(0, 1fr) 4.5rem;
+ gap: 1.5rem;
+ padding: 1.5rem 0;
+ border-bottom: 1px solid var(--border);
+ align-items: start;
+ transition: background 0.2s ease;
+}
+.post-list > li:first-child {
+ padding-top: 0.5rem;
+}
+.post-list > li:hover {
+ background: linear-gradient(
+ to right,
+ rgba(212, 168, 83, 0.02),
+ transparent 80%
+ );
+}
+.post-list .p-art {
+ width: 4.75rem;
+ height: 4.75rem;
+ border: 1px solid var(--border);
+ border-radius: 0.25rem;
+ background: transparent;
+ overflow: hidden;
+}
+.post-list .p-art svg {
+ width: 100%;
+ height: 100%;
+ display: block;
+}
+.post-list .p-body {
+ min-width: 0;
+}
+.post-list .p-date {
+ font-family: var(--font-mono);
+ font-size: 0.64rem;
+ color: var(--text-muted);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ margin-bottom: 0.35rem;
+ display: flex;
+ gap: 0.75rem;
+ align-items: center;
+}
+.post-list .p-date .n {
+ color: var(--accent);
+ opacity: 0.7;
+ font-size: 0.6rem;
+}
+.post-list .p-title {
+ font-family: var(--font-serif);
+ font-weight: 400;
+ font-variation-settings: "opsz" 48;
+ font-size: 1.35rem;
+ line-height: 1.2;
+ color: var(--text-bright);
+ text-wrap: balance;
+ margin-bottom: 0.35rem;
+ letter-spacing: -0.01em;
+}
+.post-list .p-title a {
+ color: inherit;
+}
+.post-list > li:hover .p-title a {
+ color: var(--accent);
+}
+.post-list .p-desc {
+ font-family: var(--font-text);
+ font-weight: 300;
+ font-style: normal;
+ font-size: 0.98rem;
+ line-height: 1.55;
+ color: var(--text);
+ margin: 0 0 0.5rem;
+ max-width: 48rem;
+ text-wrap: pretty;
+}
+.post-list .p-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.6rem;
+ font-family: var(--font-mono);
+ font-size: 0.64rem;
+}
+.post-list .p-tags a {
+ color: var(--tag-color);
+ letter-spacing: 0.04em;
+}
+.post-list .p-tags a::before {
+ content: "#";
+ opacity: 0.5;
+}
+.post-list .p-read {
+ font-family: var(--font-mono);
+ font-size: 0.62rem;
+ color: var(--text-muted);
+ letter-spacing: 0.06em;
+ text-align: right;
+ padding-top: 0.35rem;
+}
+.post-list .p-read b {
+ display: block;
+ font-family: var(--font-serif);
+ font-style: normal;
+ font-weight: 400;
+ font-size: 1.1rem;
+ color: var(--text-bright);
+ letter-spacing: -0.02em;
+ margin-bottom: 0.1rem;
+}
+.post-list .p-read .mini {
+ display: block;
+ margin-top: 0.25rem;
+ font-size: 0.6rem;
+ color: var(--text-muted);
+}
+
+/* Series row */
+.post-list > li.is-series {
+ align-items: stretch;
+}
+.series-wrap {
+ grid-column: 2 / span 2;
+ padding: 1.1rem 1.2rem;
+ border: 1px solid rgba(212, 168, 83, 0.2);
+ background: rgba(212, 168, 83, 0.03);
+ border-radius: 0.3rem;
+}
+.series-head {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 0;
+ gap: 1rem;
+}
+.series-wrap.is-expanded .series-head {
+ margin-bottom: 0.4rem;
+}
+.series-head .s-name {
+ font-family: var(--font-serif);
+ font-weight: 500;
+ font-size: 1.2rem;
+ color: var(--text-bright);
+ letter-spacing: -0.01em;
+}
+.series-head .s-badge {
+ font-family: var(--font-mono);
+ font-size: 0.62rem;
+ color: var(--accent);
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ padding: 0.2rem 0.55rem;
+ border: 1px solid rgba(212, 168, 83, 0.35);
+ border-radius: 0.15rem;
+ white-space: nowrap;
+}
+.series-desc {
+ font-family: var(--font-text);
+ font-style: normal;
+ font-size: 0.95rem;
+ line-height: 1.55;
+ color: var(--text-muted);
+ margin-bottom: 0.9rem;
+ display: none;
+}
+.series-parts {
+ list-style: none;
+ display: none;
+ flex-direction: column;
+ gap: 0.1rem;
+}
+.series-wrap.is-expanded .series-desc {
+ display: block;
+}
+.series-wrap.is-expanded .series-parts {
+ display: flex;
+}
+.series-parts li {
+ border-top: 1px solid var(--border);
+}
+.series-parts li:first-child {
+ border-top: none;
+}
+.series-parts a {
+ display: grid;
+ grid-template-columns: 4.5rem 1fr auto;
+ gap: 1rem;
+ align-items: baseline;
+ padding: 0.55rem 0;
+ color: var(--text);
+}
+.series-parts a:hover {
+ color: var(--accent);
+}
+.series-parts .sp-num {
+ font-family: var(--font-mono);
+ font-size: 0.62rem;
+ color: var(--text-muted);
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+.series-parts .sp-title {
+ font-family: var(--font-text);
+ font-weight: 400;
+ font-size: 0.95rem;
+ color: var(--text-bright);
+ text-wrap: balance;
+}
+.series-parts a:hover .sp-title {
+ color: var(--accent);
+}
+.series-parts .sp-date {
+ font-family: var(--font-mono);
+ font-size: 0.62rem;
+ color: var(--text-muted);
+ letter-spacing: 0.06em;
+}
+.series-toggle {
+ display: flex;
+ align-items: center;
+ background: none;
+ border: 1px solid rgba(212, 168, 83, 0.35);
+ border-radius: 0.15rem;
+ padding: 0.25rem 0.4rem;
+ color: var(--accent);
+ cursor: pointer;
+ transition: background 0.15s, transform 0.2s;
+ margin-left: auto;
+}
+.series-toggle:hover {
+ background: rgba(212, 168, 83, 0.1);
+}
+.series-toggle svg {
+ display: block;
+ transition: transform 0.2s;
+}
+.series-wrap.is-expanded .series-toggle svg {
+ transform: rotate(180deg);
+}
+
+/* ============ Bottom grid: tags + archive ============ */
+.bottom-grid {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
+ gap: 3rem;
+ align-items: start;
+ margin-top: 3rem;
+ padding-top: 2.5rem;
+ border-top: 1px solid var(--border-strong);
+}
+.bg-left, .bg-right {
+ min-width: 0;
+}
+
+.card {
+ margin-bottom: 2.5rem;
+}
+.card-title {
+ font-family: var(--font-mono);
+ font-size: 0.65rem;
+ color: var(--text-muted);
+ letter-spacing: 0.15em;
+ text-transform: uppercase;
+ margin-bottom: 1rem;
+ padding-bottom: 0.5rem;
+ border-bottom: 1px solid var(--border);
+ display: flex;
+ justify-content: space-between;
+ align-items: baseline;
+}
+.card-title .c {
+ color: var(--accent);
+}
+
+.tagcloud {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.4rem;
+}
+.tagcloud a {
+ display: inline-flex;
+ align-items: baseline;
+ gap: 0.35rem;
+ font-family: var(--font-mono);
+ font-size: 0.74rem;
+ color: var(--tag-color);
+ padding: 0.25rem 0.55rem;
+ border: 1px solid rgba(126, 184, 168, 0.2);
+ border-radius: 0.15rem;
+ transition: color 0.15s, border-color 0.15s, background 0.15s;
+}
+.tagcloud a::before {
+ content: "#";
+ opacity: 0.5;
+}
+.tagcloud a .ct {
+ font-size: 0.62rem;
+ color: var(--text-muted);
+}
+.tagcloud a:hover {
+ color: var(--accent);
+ border-color: rgba(212, 168, 83, 0.35);
+ background: rgba(212, 168, 83, 0.05);
+}
+
+.archive-list {
+ list-style: none;
+ font-family: var(--font-mono);
+ font-size: 0.78rem;
+}
+.archive-list li {
+ display: flex;
+ justify-content: space-between;
+ padding: 0.4rem 0;
+ border-bottom: 1px solid var(--border);
+ color: var(--text-muted);
+}
+.archive-list li:last-child {
+ border-bottom: none;
+}
+.archive-list li b {
+ color: var(--text-bright);
+ font-weight: 400;
+}
+.archive-list li a {
+ color: var(--text-muted);
+}
+.archive-list li a:hover {
+ color: var(--accent);
+}
+
+/* ============ Newsletter ============ */
+.newsletter {
+ margin-top: 3.5rem;
+ padding: 2.25rem 2rem;
+ border: 1px solid var(--border);
+ border-radius: 0.35rem;
+ background: rgba(255, 255, 255, 0.015);
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
+ gap: 2.5rem;
+ align-items: center;
+}
+.newsletter .nl-title {
+ font-family: var(--font-serif);
+ font-weight: 400;
+ font-size: 1.75rem;
+ color: var(--text-bright);
+ line-height: 1.15;
+ margin-bottom: 0.5rem;
+ letter-spacing: -0.01em;
+}
+.newsletter .nl-title em {
+ font-style: italic;
+ color: var(--accent);
+}
+.newsletter .nl-desc {
+ font-family: var(--font-text);
+ font-style: normal;
+ font-size: 1rem;
+ color: var(--text-muted);
+ line-height: 1.6;
+}
+.nl-form-wrap {
+ min-width: 0;
+}
+.nl-form {
+ display: flex;
+ flex-direction: row;
+ gap: 0.5rem;
+ min-width: 0;
+}
+.nl-form input[type="email"] {
+ flex: 1 1 auto;
+ min-width: 0;
+ background: var(--bg);
+ border: 1px solid var(--border);
+ border-radius: 0.2rem;
+ padding: 0.6rem 0.8rem;
+ color: var(--text);
+ font-family: var(--font-mono);
+ font-size: 0.82rem;
+ outline: none;
+ transition: border-color 0.15s, box-shadow 0.15s;
+}
+.nl-form input[type="email"]:focus {
+ border-color: var(--accent);
+ box-shadow: 0 0 0 2px rgba(212, 168, 83, 0.2);
+}
+.nl-form input[type="email"]::placeholder {
+ color: var(--text-muted);
+}
+.nl-form button {
+ flex: 0 0 auto;
+ background: var(--accent);
+ color: var(--bg);
+ border: none;
+ border-radius: 0.2rem;
+ padding: 0.6rem 1.1rem;
+ font-family: var(--font-mono);
+ font-size: 0.74rem;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ font-weight: 500;
+ cursor: pointer;
+ transition: background 0.15s;
+ white-space: nowrap;
+}
+.nl-form button:hover {
+ background: var(--accent-hover);
+}
+.nl-form button:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+}
+
+.nl-count {
+ font-family: var(--font-mono);
+ font-size: 0.64rem;
+ color: var(--text-muted);
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ margin-top: 0.7rem;
+ padding-top: 0.7rem;
+ border-top: 1px dashed var(--border);
+ display: flex;
+ justify-content: space-between;
+}
+.nl-count b {
+ color: var(--accent);
+ font-weight: 400;
+}
+
+.nl-spinner {
+ display: none;
+ animation: spin 0.8s linear infinite;
+}
+.newsletter.is-loading .nl-btn-text {
+ display: none;
+}
+.newsletter.is-loading .nl-spinner {
+ display: inline-block;
+}
+.newsletter.is-success .nl-form {
+ display: none;
+}
+.nl-success, .nl-error {
+ display: none;
+ font-size: 0.88rem;
+ margin-top: 0.75rem;
+}
+.newsletter.is-success .nl-success {
+ display: block;
+ color: var(--tag-color);
+}
+.newsletter.is-error .nl-error {
+ display: block;
+ color: var(--danger);
+}
+
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+/* ============ Post page ============ */
+.progress-top {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 2px;
+ background: transparent;
+ z-index: 50;
+ pointer-events: none;
+}
+.progress-top .bar {
+ height: 100%;
+ width: 0;
+ background: var(--accent);
+ transition: width 0.08s linear;
+ box-shadow: 0 0 8px rgba(212, 168, 83, 0.4);
+}
+
+main.post-shell {
+ max-width: var(--max-width);
+ margin: 0 auto;
+ width: 100%;
+ padding: 2.5rem 1.5rem 4rem;
+}
+
+.mm-art {
+ width: 8rem;
+ height: 8rem;
+ opacity: 0.9;
+ flex-shrink: 0;
+}
+.mm-art svg {
+ width: 100%;
+ height: 100%;
+ display: block;
+}
+
+/* Header KV box (post meta) */
+.header-box {
+ border: 1px solid var(--border);
+ border-radius: 0.25rem;
+ padding: 0.9rem 1.1rem;
+ background: rgba(255, 255, 255, 0.015);
+ margin-bottom: 2.5rem;
+ font-family: var(--font-mono);
+ font-size: 0.72rem;
+}
+.header-box .hb-row {
+ display: grid;
+ grid-template-columns: 6.5rem minmax(0, 1fr);
+ gap: 1rem;
+ padding: 0.2rem 0;
+}
+.header-box .hb-k {
+ color: var(--text-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ font-size: 0.64rem;
+ padding-top: 0.15rem;
+}
+.header-box .hb-v {
+ color: var(--text-bright);
+ text-wrap: pretty;
+ overflow-wrap: anywhere;
+ min-width: 0;
+}
+.header-box .hb-v .tag {
+ color: var(--tag-color);
+ margin-right: 0.6rem;
+}
+.header-box .hb-v .tag::before {
+ content: "#";
+ opacity: 0.5;
+}
+.header-box .hb-v a {
+ color: var(--text-bright);
+ border-bottom: 1px dashed var(--border-strong);
+}
+.header-box .hb-v a:hover {
+ color: var(--accent);
+ border-bottom-color: var(--accent);
+}
+.header-box .hb-v .muted {
+ color: var(--text-muted);
+}
+
+/* Hero */
+.post-hero {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ gap: 2.5rem;
+ align-items: start;
+ margin-bottom: 3rem;
+}
+.post-hero .hero-text {
+ min-width: 0;
+}
+.post-hero .eyebrow {
+ font-family: var(--font-mono);
+ font-size: 0.68rem;
+ text-transform: uppercase;
+ letter-spacing: 0.15em;
+ color: var(--text-muted);
+ margin-bottom: 1.25rem;
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+}
+.post-hero .eyebrow::after {
+ content: "";
+ flex: 1;
+ height: 1px;
+ background: var(--border);
+}
+.post-hero .eyebrow b {
+ color: var(--accent);
+ font-weight: 400;
+}
+.post-hero h1 {
+ font-family: var(--font-serif);
+ font-weight: 300;
+ font-variation-settings: "opsz" 144;
+ font-size: clamp(1.9rem, 4.3vw, 3.15rem);
+ line-height: 1.15;
+ letter-spacing: -0.02em;
+ color: var(--text-bright);
+ text-wrap: balance;
+ overflow-wrap: break-word;
+ margin-bottom: 1.75rem;
+}
+.post-hero .dek {
+ font-family: var(--font-serif);
+ font-weight: 300;
+ font-style: italic;
+ font-size: 1.2rem;
+ line-height: 1.5;
+ color: var(--text);
+ text-wrap: balance;
+ margin-bottom: 1.5rem;
+}
+.post-hero .byline {
+ font-family: var(--font-mono);
+ font-size: 0.7rem;
+ color: var(--text-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.12em;
+ display: flex;
+ gap: 1.5rem;
+ flex-wrap: wrap;
+ padding-top: 1rem;
+ border-top: 1px solid var(--border);
+}
+.post-hero .byline b {
+ color: var(--text-bright);
+ font-weight: 400;
+}
+.post-hero .byline a {
+ color: var(--text-bright);
+}
+.post-hero .byline a:hover {
+ color: var(--accent);
+}
+
+/* Series strip */
+.series-strip {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin-bottom: 2rem;
+ padding: 0.75rem 0.9rem;
+ background: rgba(212, 168, 83, 0.04);
+ border: 1px solid rgba(212, 168, 83, 0.2);
+ border-radius: 0.25rem;
+ font-family: var(--font-mono);
+ font-size: 0.7rem;
+}
+.series-strip .label {
+ color: var(--accent);
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ margin-right: 0.5rem;
+}
+.series-strip .parts {
+ display: flex;
+ gap: 0.25rem;
+ flex: 1;
+ min-width: 0;
+}
+.series-strip .parts a, .series-strip .parts .cur {
+ flex: 1;
+ min-width: 0;
+ text-align: center;
+ padding: 0.35rem 0.5rem;
+ border-radius: 0.15rem;
+ color: var(--text-muted);
+ background: rgba(255, 255, 255, 0.02);
+ transition: color 0.15s, background 0.15s;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+}
+.series-strip .parts a:hover {
+ color: var(--text-bright);
+ background: rgba(255, 255, 255, 0.04);
+}
+.series-strip .parts .cur {
+ color: var(--text-bright);
+ background: rgba(212, 168, 83, 0.15);
+}
+.series-strip .progress-pct {
+ color: var(--text-muted);
+ white-space: nowrap;
+}
+.series-strip .progress-pct b {
+ color: var(--accent);
+ font-weight: 400;
+}
+
+/* Post content (serif editorial body) */
+.post-content {
+ font-family: var(--font-serif);
+ font-weight: 400;
+ font-size: 1.15rem;
+ line-height: 1.6;
+ color: #d4dce4;
+}
+.post-content p {
+ margin-bottom: 1.35em;
+ text-wrap: pretty;
+}
+.post-content > p:first-of-type .first-word {
+ color: var(--accent);
+}
+
+.post-content h2 {
+ font-family: var(--font-serif);
+ font-weight: 400;
+ font-variation-settings: "opsz" 72;
+ font-size: 1.6rem;
+ color: var(--text-bright);
+ line-height: 1.15;
+ letter-spacing: -0.015em;
+ text-wrap: balance;
+ margin: 3.5rem 0 1.25rem;
+ padding-top: 1.25rem;
+ border-top: 1px solid var(--border-strong);
+ scroll-margin-top: 1.5rem;
+}
+
+.post-content h3 {
+ font-family: var(--font-serif);
+ font-weight: 500;
+ font-style: italic;
+ font-size: 1.25rem;
+ color: var(--text-bright);
+ margin: 2.25rem 0 0.5rem;
+ line-height: 1.2;
+}
+.post-content h3::before {
+ content: "—";
+ color: var(--accent);
+ margin-right: 0.5rem;
+ opacity: 0.7;
+}
+
+.post-content em {
+ font-style: italic;
+ color: var(--text);
+}
+.post-content strong {
+ font-weight: 500;
+ color: var(--text-bright);
+}
+.post-content a {
+ color: var(--accent);
+ border-bottom: 1px solid rgba(212, 168, 83, 0.3);
+ transition: border-bottom-color 0.15s, color 0.15s;
+}
+.post-content a:hover {
+ color: var(--accent-hover);
+ border-bottom-color: var(--accent-hover);
+}
+
+.post-content ul, .post-content ol {
+ margin: 1.5rem 0 1.5rem 1.5rem;
+ padding-left: 0.75rem;
+}
+.post-content li {
+ margin-bottom: 0.5rem;
+}
+
+.post-content code {
+ font-family: var(--font-mono);
+ background: rgba(255, 255, 255, 0.04);
+ border: 1px solid var(--border);
+ padding: 0.08em 0.35em;
+ border-radius: 0.2rem;
+ font-size: 0.82em;
+ color: var(--text-bright);
+}
+.post-content pre {
+ background: var(--bg-surface);
+ border: 1px solid var(--border);
+ border-radius: 0.3rem;
+ margin: 1.75rem 0;
+ overflow: hidden;
+ font-family: var(--font-mono);
+}
+.post-content pre code {
+ display: block;
+ background: none;
+ border: none;
+ padding: 1rem 1.25rem;
+ font-size: 0.83rem;
+ line-height: 1.65;
+ color: var(--text);
+ overflow-x: auto;
+ border-radius: 0;
+}
+
+.post-content blockquote {
+ font-family: var(--font-serif);
+ font-weight: 300;
+ font-variation-settings: "opsz" 144;
+ font-size: 1.45rem;
+ line-height: 1.3;
+ color: var(--text-bright);
+ margin: 2.5rem 0;
+ padding: 1.25rem 0 1.25rem 1.5rem;
+ border-top: 1px solid var(--border-strong);
+ border-bottom: 1px solid var(--border-strong);
+ border-left: 2px solid var(--accent);
+ text-wrap: balance;
+ font-style: italic;
+}
+
+.post-content img {
+ max-width: 100%;
+ border-radius: 0.375rem;
+ margin: 2rem 0;
+}
+
+.post-content table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 2rem 0;
+ font-family: var(--font-text);
+ font-size: 0.9rem;
+}
+.post-content th, .post-content td {
+ padding: 0.6rem 1rem;
+ text-align: left;
+ border-bottom: 1px solid var(--border);
+}
+.post-content th {
+ font-family: var(--font-mono);
+ font-size: 0.72rem;
+ font-weight: 400;
+ color: var(--text-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+}
+
+.post-content .mermaid {
+ margin: 2rem 0;
+ text-align: center;
+ font-family: var(--font-text);
+}
+.post-content .mermaid svg {
+ max-width: 100%;
+}
+
+/* Margin notes rail (right) */
+.margin-notes {
+ position: sticky;
+ top: 1.5rem;
+ align-self: start;
+ font-family: var(--font-text);
+ font-size: 0.8rem;
+ line-height: 1.5;
+ color: var(--text-muted);
+ display: flex;
+ flex-direction: column;
+ gap: 0.2rem;
+}
+.mnote {
+ padding: 0.6rem 0 0.7rem 0.9rem;
+ border-left: 1px solid var(--border-strong);
+}
+.mnote .mref {
+ font-family: var(--font-mono);
+ font-size: 0.62rem;
+ color: var(--accent);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ display: block;
+ margin-bottom: 0.3rem;
+}
+
+/* Post footer / nav */
+.post-footer {
+ margin-top: 4rem;
+ padding-top: 2rem;
+ border-top: 1px solid var(--border);
+}
+.footer-title {
+ font-family: var(--font-mono);
+ font-size: 0.68rem;
+ color: var(--text-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.1em;
+ margin-bottom: 1rem;
+}
+.post-nav {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 1rem;
+ margin-bottom: 3rem;
+}
+.post-nav .cell {
+ padding: 1rem 1.1rem;
+ border: 1px solid var(--border);
+ border-radius: 0.3rem;
+ background: rgba(255, 255, 255, 0.015);
+ transition: border-color 0.15s, background 0.15s;
+}
+.post-nav .cell:hover {
+ border-color: rgba(212, 168, 83, 0.3);
+ background: rgba(212, 168, 83, 0.03);
+}
+.post-nav .cell .dir {
+ font-family: var(--font-mono);
+ font-size: 0.62rem;
+ color: var(--text-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.1em;
+ margin-bottom: 0.4rem;
+}
+.post-nav .cell .ttl {
+ font-family: var(--font-serif);
+ font-size: 1.02rem;
+ line-height: 1.35;
+ color: var(--text-bright);
+ font-weight: 400;
+ text-wrap: balance;
+}
+.post-nav .cell.right {
+ text-align: right;
+}
+
+.related {
+ margin-bottom: 3rem;
+}
+.related-list {
+ list-style: none;
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 0.75rem;
+}
+.related-list li a {
+ display: block;
+ padding: 0.9rem 1rem;
+ border: 1px solid var(--border);
+ border-radius: 0.3rem;
+ color: var(--text);
+ transition: border-color 0.15s, color 0.15s;
+}
+.related-list li a:hover {
+ border-color: rgba(212, 168, 83, 0.3);
+ color: var(--accent);
+}
+.related-list li .rl-date {
+ font-family: var(--font-mono);
+ font-size: 0.62rem;
+ color: var(--text-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ margin-bottom: 0.35rem;
+ display: block;
+}
+.related-list li .rl-title {
+ font-family: var(--font-serif);
+ font-size: 0.98rem;
+ line-height: 1.3;
+ color: var(--text-bright);
+}
+.related-list li a:hover .rl-title {
+ color: var(--accent);
+}
+
+.comments {
+ margin-top: 3rem;
+ padding-top: 2rem;
+ border-top: 1px solid var(--border);
+}
+.comments h2 {
+ font-family: var(--font-mono);
+ font-size: 0.68rem;
+ font-weight: 400;
+ color: var(--text-muted);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ margin-bottom: 1.5rem;
+}
+
+/* ============ About page ============ */
+.bio {
+ font-family: var(--font-text);
+ font-weight: 300;
+ font-size: 1.05rem;
+ line-height: 1.7;
+ color: var(--text);
+ max-width: 42rem;
+ text-wrap: pretty;
+}
+.bio p + p {
+ margin-top: 0.9rem;
+}
+.bio em {
+ color: var(--text-bright);
+ font-style: italic;
+}
+
+.now-card {
+ display: grid;
+ grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
+ gap: 2.5rem;
+ align-items: start;
+ padding: 1.75rem;
+ border: 1px solid var(--border);
+ border-radius: 0.4rem;
+ background: linear-gradient(
+ to bottom right,
+ rgba(212, 168, 83, 0.03),
+ transparent 60%
+ );
+}
+.now-head {
+ display: flex;
+ align-items: baseline;
+ gap: 0.75rem;
+ margin-bottom: 0.9rem;
+}
+.now-badge {
+ font-family: var(--font-mono);
+ font-size: 0.6rem;
+ color: var(--accent);
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ padding: 0.2rem 0.5rem;
+ border: 1px solid rgba(212, 168, 83, 0.35);
+ border-radius: 0.15rem;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.4rem;
+}
+.now-badge .pulse {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: var(--accent);
+ animation: pulse 2s ease-in-out infinite;
+}
+.now-role {
+ font-family: var(--font-serif);
+ font-weight: 400;
+ font-size: 1.6rem;
+ letter-spacing: -0.015em;
+ color: var(--text-bright);
+ line-height: 1.15;
+}
+.now-role em {
+ font-style: italic;
+ color: var(--accent);
+}
+.now-since {
+ font-family: var(--font-mono);
+ font-size: 0.68rem;
+ color: var(--text-muted);
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ margin-bottom: 1rem;
+}
+.now-desc {
+ font-family: var(--font-text);
+ font-weight: 300;
+ font-size: 1rem;
+ line-height: 1.65;
+ color: var(--text);
+ text-wrap: pretty;
+ margin-bottom: 1rem;
+}
+.now-tech {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.35rem;
+}
+.now-tech .tp {
+ font-family: var(--font-mono);
+ font-size: 0.64rem;
+ color: var(--text);
+ background: rgba(255, 255, 255, 0.03);
+ border: 1px solid var(--border);
+ padding: 0.18rem 0.5rem;
+ border-radius: 0.15rem;
+}
+.now-facts {
+ display: grid;
+ gap: 0.65rem;
+ font-family: var(--font-mono);
+ font-size: 0.75rem;
+}
+.now-facts .row {
+ display: grid;
+ grid-template-columns: 5.5rem 1fr;
+ gap: 1rem;
+ padding: 0.4rem 0;
+ border-bottom: 1px dashed var(--border);
+}
+.now-facts .row:last-child {
+ border-bottom: none;
+}
+.now-facts .k {
+ color: var(--text-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ font-size: 0.64rem;
+ padding-top: 0.15rem;
+}
+.now-facts .v {
+ color: var(--text-bright);
+ text-wrap: balance;
+}
+.now-facts .v .accent {
+ color: var(--accent);
+}
+.now-facts .v a {
+ color: var(--text-bright);
+ border-bottom: 1px dashed var(--border-strong);
+}
+.now-facts .v a:hover {
+ color: var(--accent);
+ border-bottom-color: var(--accent);
+}
+
+/* Timeline */
+.timeline-wrap {
+ display: grid;
+ grid-template-columns: 7rem minmax(0, 1fr);
+ gap: 2.5rem;
+ align-items: start;
+}
+.tl-axis {
+ position: sticky;
+ top: 2rem;
+ font-family: var(--font-mono);
+ font-size: 0.68rem;
+ color: var(--text-muted);
+}
+.tl-axis .tl-label {
+ font-size: 0.6rem;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ color: var(--text-muted);
+ margin-bottom: 1rem;
+}
+.tl-axis .tl-bigyr {
+ font-family: var(--font-serif);
+ font-size: 2rem;
+ font-weight: 400;
+ color: var(--text-bright);
+ letter-spacing: -0.02em;
+ line-height: 1;
+}
+.tl-axis .tl-bigyr span {
+ color: var(--text-muted);
+ font-size: 1rem;
+ margin-left: 0.25rem;
+}
+.tl-axis .tl-bigyr-sub {
+ font-size: 0.62rem;
+ color: var(--text-muted);
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ margin-top: 0.2rem;
+}
+.tl-axis .tl-key {
+ display: grid;
+ gap: 0.45rem;
+ font-size: 0.65rem;
+ margin-top: 1.2rem;
+ padding-top: 1rem;
+ border-top: 1px solid var(--border);
+}
+.tl-axis .tl-key .row {
+ display: flex;
+ align-items: center;
+ gap: 0.55rem;
+ color: var(--text-muted);
+}
+.tl-axis .tl-key .sw {
+ width: 8px;
+ height: 8px;
+ border-radius: 2px;
+ flex-shrink: 0;
+}
+.tl-axis .tl-key .sw.lead {
+ background: var(--accent);
+}
+.tl-axis .tl-key .sw.found {
+ background: var(--tag-color);
+}
+.tl-axis .tl-key .sw.eng {
+ background: var(--eng-color);
+}
+.tl-axis .tl-key .sw.other {
+ background: var(--text-muted);
+}
+
+.tl-track {
+ position: relative;
+ padding-left: 1.5rem;
+ min-width: 0;
+}
+.tl-track::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 0.5rem;
+ bottom: 0.5rem;
+ width: 1px;
+ background: linear-gradient(
+ to bottom,
+ transparent,
+ var(--border-strong) 3%,
+ var(--border-strong) 97%,
+ transparent
+ );
+}
+.tl-track::after {
+ content: "";
+ position: absolute;
+ left: -2px;
+ top: 0;
+ width: 5px;
+ height: var(--pct, 16%);
+ background: linear-gradient(to bottom, var(--accent), transparent);
+ opacity: 0.6;
+ border-radius: 2px;
+ transition: height 0.4s cubic-bezier(0.22, 1, 0.36, 1);
+}
+.tl-item {
+ position: relative;
+ padding: 0 0 2.5rem 0;
+ transition: opacity 0.2s;
+}
+.tl-item.hidden {
+ display: none;
+}
+.tl-dot {
+ position: absolute;
+ left: -1.75rem;
+ top: 0.55rem;
+ width: 0.65rem;
+ height: 0.65rem;
+ border-radius: 50%;
+ background: var(--bg);
+ border: 2px solid var(--text-muted);
+ transition: all 0.2s;
+}
+.tl-item[data-kind="lead"] .tl-dot {
+ border-color: var(--accent);
+}
+.tl-item[data-kind="found"] .tl-dot {
+ border-color: var(--tag-color);
+}
+.tl-item[data-kind="eng"] .tl-dot {
+ border-color: var(--eng-color);
+}
+.tl-item.expanded .tl-dot {
+ box-shadow: 0 0 0 4px rgba(212, 168, 83, 0.12);
+ transform: scale(1.15);
+}
+.tl-item[data-kind="lead"].expanded .tl-dot {
+ background: var(--accent);
+ box-shadow: 0 0 0 4px rgba(212, 168, 83, 0.12);
+}
+.tl-item[data-kind="found"].expanded .tl-dot {
+ background: var(--tag-color);
+ box-shadow: 0 0 0 4px rgba(126, 184, 168, 0.12);
+}
+.tl-item[data-kind="eng"].expanded .tl-dot {
+ background: var(--eng-color);
+ box-shadow: 0 0 0 4px rgba(61, 123, 138, 0.12);
+}
+
+.tl-header {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ gap: 1rem;
+ align-items: baseline;
+ cursor: pointer;
+ padding: 0.2rem 0;
+ user-select: none;
+}
+.tl-header:hover .tl-company {
+ color: var(--accent);
+}
+.tl-when {
+ font-family: var(--font-mono);
+ font-size: 0.64rem;
+ color: var(--text-muted);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ margin-bottom: 0.2rem;
+}
+.tl-when .dur {
+ color: var(--accent);
+ opacity: 0.8;
+ margin-left: 0.4rem;
+}
+.tl-company {
+ font-family: var(--font-serif);
+ font-weight: 400;
+ font-size: 1.4rem;
+ letter-spacing: -0.015em;
+ color: var(--text-bright);
+ line-height: 1.2;
+ transition: color 0.15s;
+}
+.tl-role {
+ font-family: var(--font-serif);
+ font-style: italic;
+ font-weight: 300;
+ font-size: 1rem;
+ color: var(--text);
+ margin-top: 0.2rem;
+}
+.tl-toggle {
+ font-family: var(--font-mono);
+ font-size: 0.6rem;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ color: var(--text-muted);
+ padding: 0.2rem 0.5rem;
+ border: 1px solid var(--border);
+ border-radius: 0.15rem;
+ transition: all 0.15s;
+ white-space: nowrap;
+}
+.tl-header:hover .tl-toggle {
+ color: var(--accent);
+ border-color: rgba(212, 168, 83, 0.3);
+}
+.tl-item.expanded .tl-toggle {
+ color: var(--accent);
+ border-color: rgba(212, 168, 83, 0.4);
+ background: rgba(212, 168, 83, 0.05);
+}
+.tl-item.expanded .tl-toggle::after {
+ content: " −";
+}
+.tl-item:not(.expanded) .tl-toggle::after {
+ content: " +";
+}
+
+.tl-detail {
+ overflow: hidden;
+ max-height: 0;
+ opacity: 0;
+ transition: max-height 0.35s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.25s;
+}
+.tl-item.expanded .tl-detail {
+ max-height: 60rem;
+ opacity: 1;
+ margin-top: 0.9rem;
+}
+.tl-detail-inner {
+ padding: 1rem 1.1rem;
+ border: 1px solid var(--border);
+ border-radius: 0.3rem;
+ background: rgba(255, 255, 255, 0.015);
+}
+.tl-detail p {
+ font-family: var(--font-text);
+ font-weight: 300;
+ font-size: 0.98rem;
+ line-height: 1.65;
+ color: var(--text);
+ text-wrap: pretty;
+ margin-bottom: 0.7rem;
+}
+.tl-detail p:last-child {
+ margin-bottom: 0;
+}
+.tl-detail em {
+ color: var(--text-bright);
+ font-style: italic;
+}
+.tl-highlights {
+ list-style: none;
+ display: grid;
+ gap: 0.5rem;
+ margin-top: 0.9rem;
+ padding-top: 0.9rem;
+ border-top: 1px dashed var(--border);
+}
+.tl-highlights li {
+ font-family: var(--font-mono);
+ font-size: 0.72rem;
+ color: var(--text);
+ padding-left: 1rem;
+ position: relative;
+ line-height: 1.5;
+}
+.tl-highlights li::before {
+ content: "▸";
+ position: absolute;
+ left: 0;
+ color: var(--accent);
+ opacity: 0.7;
+}
+.tl-highlights li b {
+ color: var(--accent);
+ font-weight: 400;
+}
+.tl-sub-role {
+ font-family: var(--font-mono);
+ font-size: 0.68rem;
+ color: var(--text-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ margin: 0.9rem 0 0.4rem;
+ padding-top: 0.6rem;
+ border-top: 1px dashed var(--border);
+ display: flex;
+ gap: 0.7rem;
+ align-items: baseline;
+ flex-wrap: wrap;
+}
+.tl-sub-role:first-of-type {
+ border-top: none;
+ padding-top: 0;
+ margin-top: 0;
+}
+.tl-sub-role .sr-title {
+ color: var(--text-bright);
+ letter-spacing: -0.005em;
+ font-size: 0.9rem;
+ text-transform: none;
+ font-family: var(--font-serif);
+ font-style: italic;
+ font-weight: 400;
+}
+.tl-sub-role .sr-when {
+ color: var(--accent);
+ font-size: 0.6rem;
+}
+
+/* Contact CTA (shared by About + redirect) */
+.contact-cta {
+ margin-top: 3.5rem;
+ padding: 2.25rem 2rem;
+ border: 1px solid var(--border);
+ border-radius: 0.35rem;
+ background: rgba(255, 255, 255, 0.015);
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
+ gap: 2.5rem;
+ align-items: center;
+}
+.contact-cta .cc-title {
+ font-family: var(--font-serif);
+ font-weight: 400;
+ font-size: 1.75rem;
+ color: var(--text-bright);
+ line-height: 1.15;
+ margin-bottom: 0.5rem;
+ letter-spacing: -0.01em;
+}
+.contact-cta .cc-title em {
+ font-style: italic;
+ color: var(--accent);
+}
+.contact-cta .cc-sub {
+ font-family: var(--font-text);
+ font-style: normal;
+ font-size: 1rem;
+ color: var(--text-muted);
+ line-height: 1.6;
+}
+.cc-links {
+ display: grid;
+ gap: 0.5rem;
+}
+.cc-link {
+ display: grid;
+ grid-template-columns: 5.5rem 1fr auto;
+ gap: 1rem;
+ align-items: center;
+ padding: 0.75rem 0.9rem;
+ border: 1px solid var(--border);
+ border-radius: 0.25rem;
+ background: rgba(255, 255, 255, 0.015);
+ font-family: var(--font-mono);
+ font-size: 0.78rem;
+ color: var(--text);
+ transition: all 0.15s;
+}
+.cc-link:hover {
+ border-color: var(--accent);
+ background: rgba(212, 168, 83, 0.04);
+ color: var(--text);
+}
+.cc-link .ck {
+ color: var(--text-muted);
+ text-transform: uppercase;
+ font-size: 0.62rem;
+ letter-spacing: 0.1em;
+}
+.cc-link .cv {
+ color: var(--text-bright);
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.cc-link .arr {
+ color: var(--accent);
+ opacity: 0.7;
+ font-family: var(--font-mono);
+ transition: transform 0.15s;
+}
+.cc-link:hover .arr {
+ transform: translate(2px, -2px);
+}
+
+/* ============ Tag pages ============ */
+.tag-page {
+ margin-bottom: 2rem;
+}
+.tag-page h1 {
+ font-family: var(--font-serif);
+ font-weight: 400;
+ font-size: 2.15rem;
+ color: var(--text-bright);
+ letter-spacing: -0.02em;
+ margin-bottom: 0.5rem;
+}
+.tag-page .tag-label {
+ font-family: var(--font-mono);
+ font-size: 0.72rem;
+ color: var(--text-muted);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ margin-bottom: 1rem;
+}
+.back-to-tags {
+ font-family: var(--font-mono);
+ font-size: 0.8rem;
+ color: var(--text-muted);
+}
+.tag-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.75rem;
+}
+
+/* ============ Projects page (kept from old) ============ */
+.project-list {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+.project {
+ position: relative;
+ padding: 1.75rem 0;
+ border-bottom: 1px solid var(--border);
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) 14rem;
+ grid-template-areas:
+ "header shot"
+ "blurb shot"
+ "meta shot";
+ column-gap: 3rem;
+ row-gap: 0.75rem;
+ align-items: start;
+}
+.project-art {
+ display: none;
+}
+.project-header {
+ grid-area: header;
+ display: flex;
+ justify-content: space-between;
+ align-items: baseline;
+ gap: 1.5rem;
+}
+.project-name {
+ font-family: var(--font-serif);
+ font-size: 1.5rem;
+ font-weight: 400;
+ letter-spacing: -0.015em;
+ color: var(--text-bright);
+}
+.project-links {
+ display: flex;
+ gap: 1rem;
+}
+.project-link {
+ font-family: var(--font-mono);
+ font-size: 0.75rem;
+ color: var(--accent);
+}
+.project-blurb {
+ grid-area: blurb;
+ font-family: var(--font-text);
+ color: var(--text);
+ font-size: 1.02rem;
+ line-height: 1.65;
+}
+.project-meta {
+ grid-area: meta;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+}
+.project-tech {
+ font-family: var(--font-mono);
+ font-size: 0.7rem;
+ color: var(--tag-color);
+ padding: 0.2rem 0.5rem;
+ border: 1px solid rgba(126, 184, 168, 0.2);
+ border-radius: 0.2rem;
+}
+.project-screenshot {
+ grid-area: shot;
+ align-self: center;
+ width: 100%;
+ aspect-ratio: 1;
+ border: 1px solid var(--border);
+ border-radius: 0.35rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--text-muted);
+ font-family: var(--font-mono);
+ font-size: 0.75rem;
+ opacity: 0.4;
+ background: var(--bg-surface);
+ overflow: hidden;
+}
+.project-screenshot.has-image {
+ border: 1px solid var(--border);
+ opacity: 1;
+ background: #0d1117;
+}
+.project-screenshot.has-image img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ display: block;
+}
+
+/* ============ Not found ============ */
+.not-found {
+ text-align: center;
+ padding: 4rem 0;
+}
+.not-found h1 {
+ font-family: var(--font-serif);
+ font-size: 4.5rem;
+ font-weight: 300;
+ color: var(--accent);
+ margin-bottom: 1rem;
+ letter-spacing: -0.05em;
+}
+.not-found p {
+ color: var(--text-muted);
+ font-size: 1.1rem;
+}
+
+/* ============ Generative art keyframes ============ */
+@keyframes art-spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+@keyframes art-draw-in {
+ to {
+ stroke-dashoffset: 0;
+ }
+}
+@keyframes art-dot-in {
+ to {
+ opacity: var(--op, 0.6);
+ }
+}
+@keyframes art-pulse-scale {
+ 0%, 100% {
+ transform: scale(1);
+ }
+ 50% {
+ transform: scale(2);
+ }
+}
+.art-spin {
+ transform-origin: 50% 50%;
+ animation: art-spin var(--dur) linear infinite;
+ animation-direction: var(--dir);
+}
+.art-draw {
+ stroke-dasharray: 1;
+ stroke-dashoffset: 1;
+ animation: art-draw-in 0.8s ease-out var(--d, 0s) forwards;
+}
+.art-dot {
+ opacity: 0;
+ animation: art-dot-in 0.4s ease-out var(--d, 0s) forwards;
+}
+.art-pulse-dot {
+ opacity: 0;
+ transform-box: fill-box;
+ transform-origin: center;
+ animation:
+ art-dot-in 0.4s ease-out var(--d, 0s) forwards,
+ art-pulse-scale var(--pd, 3s) ease-in-out calc(var(--d, 0s) + 0.5s)
+ infinite;
+}
+.art-orbit {
+ transform-origin: 50px 50px;
+ animation: art-spin var(--dur, 4s) linear infinite;
+ animation-direction: var(--dir, normal);
+}
+
+/* ============ Responsive ============ */
+@media (max-width: 1200px) {
+ .margin-notes {
+ display: none;
+ }
+}
+@media (max-width: 900px) {
+ .masthead {
+ grid-template-columns: 1fr;
+ gap: 1.75rem;
+ }
+ .masthead:has(.portrait-wrap.has-outline) {
+ grid-template-columns: 1fr;
+ }
+ .masthead-right {
+ max-width: 12rem;
+ }
+ .featured {
+ grid-template-columns: 1fr;
+ gap: 1.75rem;
+ }
+ .feat-art {
+ max-width: 12.5rem;
+ }
+ .project {
+ grid-template-columns: 1fr;
+ grid-template-areas:
+ "header"
+ "shot"
+ "blurb"
+ "meta";
+ column-gap: 0;
+ row-gap: 1rem;
+ }
+ .project-screenshot {
+ max-width: 14rem;
+ align-self: start;
+ }
+ .bottom-grid {
+ grid-template-columns: 1fr;
+ gap: 0;
+ }
+ .now-card {
+ grid-template-columns: 1fr;
+ gap: 1.5rem;
+ }
+ .timeline-wrap {
+ grid-template-columns: 1fr;
+ gap: 1.5rem;
+ }
+ .tl-axis {
+ position: static;
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 1rem;
+ padding-bottom: 1rem;
+ border-bottom: 1px solid var(--border);
+ }
+ .tl-axis .tl-key {
+ display: flex;
+ gap: 1rem;
+ margin-top: 0;
+ padding-top: 0;
+ border-top: none;
+ }
+ .contact-cta {
+ grid-template-columns: 1fr;
+ gap: 1.25rem;
+ padding: 1.5rem;
+ }
+ .newsletter {
+ grid-template-columns: 1fr;
+ gap: 1.25rem;
+ padding: 1.5rem;
+ }
+ .post-list > li {
+ grid-template-columns: 4rem 1fr;
+ gap: 1rem;
+ }
+ .post-list .p-art {
+ width: 4rem;
+ height: 4rem;
+ }
+ .post-list .p-read {
+ grid-column: 2;
+ text-align: left;
+ padding-top: 0.25rem;
+ }
+ .series-wrap {
+ grid-column: 1 / -1;
+ }
+}
+@media (max-width: 820px) {
+ main.post-shell {
+ padding: 2rem 1.25rem 3rem;
+ }
+ .post-hero {
+ grid-template-columns: 1fr;
+ gap: 1.25rem;
+ }
+ .post-hero .mm-art {
+ width: 5rem;
+ height: 5rem;
+ order: -1;
+ }
+ .progress-top {
+ display: block;
+ }
+ .post-content {
+ font-size: 1.05rem;
+ }
+ .post-hero h1 {
+ font-size: 2rem;
+ }
+ .post-hero .dek {
+ font-size: 1.05rem;
+ }
+ .post-content h2 {
+ font-size: 1.4rem;
+ }
+ .post-content h3 {
+ font-size: 1.15rem;
+ }
+ .post-content pre {
+ font-size: 0.78rem;
+ }
+ .post-content img,
+ .post-content video,
+ .post-content iframe {
+ max-width: 100%;
+ height: auto;
+ }
+ .header-box {
+ font-size: 0.78rem;
+ }
+ .header-box .hb-row {
+ flex-wrap: wrap;
+ gap: 0.35rem 0.65rem;
+ }
+ .byline {
+ flex-wrap: wrap;
+ gap: 0.5rem 1rem;
+ }
+ .series-strip {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 0.6rem;
+ }
+ .series-strip .label {
+ margin-right: 0;
+ text-align: center;
+ }
+ .series-strip .parts {
+ flex-wrap: wrap;
+ gap: 0.35rem;
+ justify-content: center;
+ }
+ .series-strip .parts a, .series-strip .parts .cur {
+ flex: 0 0 auto;
+ min-width: 2.5rem;
+ padding: 0.4rem 0.6rem;
+ white-space: nowrap;
+ overflow: visible;
+ text-overflow: clip;
+ }
+ .series-strip .sp-sep,
+ .series-strip .sp-preview {
+ display: none;
+ }
+ .related-list {
+ grid-template-columns: 1fr;
+ }
+ .post-nav {
+ grid-template-columns: 1fr;
+ gap: 0.5rem;
+ }
+ .post-nav .cell.right {
+ text-align: left;
+ }
+}
+@media (max-width: 560px) {
+ main.shell {
+ padding: 1.5rem 1rem 2.5rem;
+ }
+ .site-nav {
+ padding: 0.85rem 0;
+ }
+ .site-nav .wrap {
+ padding: 0 1rem;
+ gap: 0.75rem;
+ }
+ .site-nav .brand {
+ font-size: 0.82rem;
+ }
+ .site-nav .links {
+ gap: 1rem;
+ font-size: 0.72rem;
+ }
+
+ .section-bar {
+ grid-template-columns: 1fr;
+ row-gap: 0.5rem;
+ margin-bottom: 1.25rem;
+ text-align: center;
+ }
+ .section-bar .sb-label {
+ font-size: 1.15rem;
+ }
+ .section-bar .sb-filter {
+ gap: 0.3rem;
+ justify-content: center;
+ }
+ .section-bar .sb-filter button {
+ font-size: 0.6rem;
+ padding: 0.22rem 0.45rem;
+ }
+
+ .masthead {
+ padding-bottom: 1.5rem;
+ margin-bottom: 1.75rem;
+ gap: 1.25rem;
+ text-align: center;
+ }
+ .masthead h1 {
+ font-size: 2rem;
+ margin-bottom: 0.75rem;
+ }
+ .masthead .dek {
+ font-size: 1rem;
+ margin-bottom: 1rem;
+ margin-left: auto;
+ margin-right: auto;
+ }
+ .mh-stats {
+ gap: 0.85rem 1.5rem;
+ padding-top: 0.75rem;
+ justify-content: center;
+ }
+ .mh-stats div b {
+ font-size: 1.2rem;
+ }
+ .masthead-right {
+ display: none;
+ }
+
+ .featured {
+ padding: 0.25rem 0 1.75rem;
+ margin-bottom: 1.75rem;
+ gap: 1rem;
+ text-align: center;
+ }
+ .feat-art {
+ display: none;
+ }
+ .feat-main .f-eyebrow {
+ margin-bottom: 0.65rem;
+ font-size: 0.6rem;
+ justify-content: center;
+ }
+ .feat-main h2 {
+ font-size: clamp(1.4rem, 6vw, 1.8rem);
+ margin-bottom: 0.75rem;
+ }
+ .feat-main .dek {
+ font-size: 0.95rem;
+ margin-bottom: 0.9rem;
+ margin-left: auto;
+ margin-right: auto;
+ }
+ .feat-main .meta {
+ gap: 0.65rem 1rem;
+ font-size: 0.62rem;
+ justify-content: center;
+ }
+
+ .post-list > li {
+ grid-template-columns: 3rem minmax(0, 1fr);
+ gap: 0.85rem;
+ padding: 1.1rem 0;
+ }
+ .post-list .p-art {
+ width: 3rem;
+ height: 3rem;
+ }
+ .post-list .p-date {
+ gap: 0.55rem;
+ font-size: 0.6rem;
+ margin-bottom: 0.3rem;
+ }
+ .post-list .p-title {
+ font-size: 1.15rem;
+ margin-bottom: 0.3rem;
+ }
+ .post-list .p-desc {
+ font-size: 0.92rem;
+ margin-bottom: 0.45rem;
+ }
+ .post-list .p-tags {
+ gap: 0.45rem;
+ font-size: 0.6rem;
+ }
+ .post-list .p-read {
+ grid-column: 2;
+ display: flex;
+ align-items: baseline;
+ gap: 0.5rem;
+ padding-top: 0.4rem;
+ font-size: 0.6rem;
+ }
+ .post-list .p-read b {
+ display: inline;
+ font-size: 0.82rem;
+ margin-bottom: 0;
+ }
+ .post-list .p-read .mini {
+ display: inline;
+ margin-top: 0;
+ font-size: 0.6rem;
+ }
+
+ .series-wrap {
+ padding: 0.9rem 0.9rem;
+ }
+ .series-head {
+ flex-wrap: wrap;
+ gap: 0.35rem 0.75rem;
+ }
+ .series-head .s-name {
+ font-size: 1.05rem;
+ }
+ .series-desc {
+ font-size: 0.88rem;
+ margin-bottom: 0.7rem;
+ }
+ .series-wrap.is-expanded .series-head {
+ margin-bottom: 0.5rem;
+ }
+ .series-parts a {
+ grid-template-columns: 3.25rem minmax(0, 1fr);
+ gap: 0.6rem;
+ padding: 0.5rem 0;
+ }
+ .series-parts .sp-title {
+ font-size: 0.9rem;
+ grid-column: 2;
+ }
+ .series-parts .sp-date {
+ grid-column: 2;
+ font-size: 0.58rem;
+ }
+
+ .bottom-grid {
+ margin-top: 2rem;
+ padding-top: 1.75rem;
+ }
+ .card {
+ margin-bottom: 1.75rem;
+ }
+ .card-title {
+ font-size: 0.6rem;
+ margin-bottom: 0.75rem;
+ }
+ .tagcloud a {
+ font-size: 0.7rem;
+ padding: 0.22rem 0.5rem;
+ }
+ .archive-list {
+ font-size: 0.72rem;
+ }
+
+ .newsletter {
+ margin-top: 2.25rem;
+ padding: 1.25rem;
+ gap: 1rem;
+ border-radius: 0.25rem;
+ text-align: center;
+ }
+ .newsletter .nl-title {
+ font-size: 1.35rem;
+ }
+ .newsletter .nl-desc {
+ font-size: 0.92rem;
+ }
+ .nl-form {
+ flex-direction: column;
+ gap: 0.5rem;
+ }
+ .nl-form button {
+ width: 100%;
+ padding: 0.7rem 1rem;
+ }
+ .nl-count {
+ gap: 0.5rem;
+ flex-wrap: wrap;
+ justify-content: center;
+ }
+ .post-hero h1 {
+ font-size: 1.7rem;
+ }
+ .post-hero .dek {
+ font-size: 0.98rem;
+ }
+ .post-content {
+ font-size: 1rem;
+ }
+ .post-content h2 {
+ font-size: 1.25rem;
+ margin: 2.5rem 0 1rem;
+ }
+ .post-content h3 {
+ font-size: 1.1rem;
+ }
+ .post-content pre {
+ padding: 0.75rem;
+ font-size: 0.74rem;
+ border-radius: 0.25rem;
+ }
+ .post-content blockquote {
+ padding-left: 1rem;
+ }
+ .project-name {
+ font-size: 1.25rem;
+ }
+ .project-blurb {
+ font-size: 0.95rem;
+ }
+ .project-screenshot {
+ max-width: 11rem;
+ }
+ .project-header {
+ flex-wrap: wrap;
+ gap: 0.5rem 1rem;
+ }
+ .now-role {
+ font-size: 1.1rem;
+ }
+ .now-card {
+ padding: 1.25rem;
+ }
+ .now-tech {
+ gap: 0.35rem;
+ }
+ .tl-header {
+ grid-template-columns: 1fr;
+ }
+ .tl-toggle {
+ justify-self: start;
+ margin-top: 0.3rem;
+ }
+ .tl-company {
+ font-size: 1.15rem;
+ }
+ .tl-when {
+ font-size: 0.7rem;
+ flex-wrap: wrap;
+ }
+ .cc-link {
+ padding: 0.85rem 1rem;
+ gap: 0.4rem;
+ flex-wrap: wrap;
+ }
+ .cc-link .cv {
+ font-size: 0.85rem;
+ }
+ .site-nav .wrap {
+ flex-direction: column;
+ gap: 0.75rem;
+ }
+ .site-nav .links {
+ gap: 1rem;
+ flex-wrap: wrap;
+ justify-content: center;
+ }
+ main.shell {
+ padding: 2rem 1rem 3rem;
+ }
+ main.post-shell {
+ padding: 1.75rem 1rem 3rem;
+ }
+ .header-box {
+ padding: 0.85rem 1rem;
+ }
+ .post-nav .cell {
+ padding: 1rem;
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ *, *::before, *::after {
+ animation-duration: 0.01ms !important;
+ transition-duration: 0.01ms !important;
+ }
+ html {
+ scroll-behavior: auto;
+ }
+}
+
+/* Visually hidden (a11y) */
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
+
+/* ============ Skip link (a11y / SEO) ============ */
+.skip-link {
+ position: absolute;
+ left: -9999px;
+ top: 0;
+ background: var(--accent);
+ color: var(--bg);
+ padding: 0.5rem 1rem;
+ font-family: var(--font-mono);
+ font-size: 0.75rem;
+ z-index: 999;
+}
+.skip-link:focus {
+ left: 1rem;
+ top: 1rem;
+}
+
+@font-face {
+ font-family: 'text';
+ font-style: italic;
+ font-weight: 100 900;
+ src: url('fonts/text-italic-100_900-cyrillic-ext.woff2') format('woff2');
+ unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+
+@font-face {
+ font-family: 'text';
+ font-style: italic;
+ font-weight: 100 900;
+ src: url('fonts/text-italic-100_900-cyrillic.woff2') format('woff2');
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+
+@font-face {
+ font-family: 'text';
+ font-style: italic;
+ font-weight: 100 900;
+ src: url('fonts/text-italic-100_900-greek-ext.woff2') format('woff2');
+ unicode-range: U+1F00-1FFF;
+}
+
+@font-face {
+ font-family: 'text';
+ font-style: italic;
+ font-weight: 100 900;
+ src: url('fonts/text-italic-100_900-greek.woff2') format('woff2');
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+
+@font-face {
+ font-family: 'text';
+ font-style: italic;
+ font-weight: 100 900;
+ src: url('fonts/text-italic-100_900-math.woff2') format('woff2');
+ unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
+}
+
+@font-face {
+ font-family: 'text';
+ font-style: italic;
+ font-weight: 100 900;
+ src: url('fonts/text-italic-100_900-symbols.woff2') format('woff2');
+ unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
+}
+
+@font-face {
+ font-family: 'text';
+ font-style: italic;
+ font-weight: 100 900;
+ src: url('fonts/text-italic-100_900-vietnamese.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;
+}
+
+@font-face {
+ font-family: 'text';
+ font-style: italic;
+ font-weight: 100 900;
+ src: url('fonts/text-italic-100_900-latin-ext.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;
+}
+
+@font-face {
+ font-family: 'text';
+ font-style: italic;
+ font-weight: 100 900;
+ src: url('fonts/text-italic-100_900-latin.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;
+}
+
+@font-face {
+ font-family: 'text';
+ font-style: normal;
+ font-weight: 100 900;
+ src: url('fonts/text-normal-100_900-cyrillic-ext.woff2') format('woff2');
+ unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+
+@font-face {
+ font-family: 'text';
+ font-style: normal;
+ font-weight: 100 900;
+ src: url('fonts/text-normal-100_900-cyrillic.woff2') format('woff2');
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+
+@font-face {
+ font-family: 'text';
+ font-style: normal;
+ font-weight: 100 900;
+ src: url('fonts/text-normal-100_900-greek-ext.woff2') format('woff2');
+ unicode-range: U+1F00-1FFF;
+}
+
+@font-face {
+ font-family: 'text';
+ font-style: normal;
+ font-weight: 100 900;
+ src: url('fonts/text-normal-100_900-greek.woff2') format('woff2');
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+
+@font-face {
+ font-family: 'text';
+ font-style: normal;
+ font-weight: 100 900;
+ src: url('fonts/text-normal-100_900-math.woff2') format('woff2');
+ unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
+}
+
+@font-face {
+ font-family: 'text';
+ font-style: normal;
+ font-weight: 100 900;
+ src: url('fonts/text-normal-100_900-symbols.woff2') format('woff2');
+ unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
+}
+
+@font-face {
+ font-family: 'text';
+ font-style: normal;
+ font-weight: 100 900;
+ src: url('fonts/text-normal-100_900-vietnamese.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;
+}
+
+@font-face {
+ font-family: 'text';
+ font-style: normal;
+ font-weight: 100 900;
+ src: url('fonts/text-normal-100_900-latin-ext.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;
+}
+
+@font-face {
+ font-family: 'text';
+ font-style: normal;
+ font-weight: 100 900;
+ src: url('fonts/text-normal-100_900-latin.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;
+}
+@font-face {
+ font-family: 'code';
+ font-style: normal;
+ font-weight: 400;
+ src: url('fonts/code-normal-400-cyrillic-ext.woff2') format('woff2');
+ unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+
+@font-face {
+ font-family: 'code';
+ font-style: normal;
+ font-weight: 400;
+ src: url('fonts/code-normal-400-cyrillic.woff2') format('woff2');
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+
+@font-face {
+ font-family: 'code';
+ font-style: normal;
+ font-weight: 400;
+ src: url('fonts/code-normal-400-greek.woff2') format('woff2');
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+
+@font-face {
+ font-family: 'code';
+ font-style: normal;
+ font-weight: 400;
+ src: url('fonts/code-normal-400-vietnamese.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;
+}
+
+@font-face {
+ font-family: 'code';
+ font-style: normal;
+ font-weight: 400;
+ src: url('fonts/code-normal-400-latin-ext.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;
+}
+
+@font-face {
+ font-family: 'code';
+ font-style: normal;
+ font-weight: 400;
+ src: url('fonts/code-normal-400-latin.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;
+}
+
+@font-face {
+ font-family: 'code';
+ font-style: normal;
+ font-weight: 500;
+ src: url('fonts/code-normal-500-cyrillic-ext.woff2') format('woff2');
+ unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+
+@font-face {
+ font-family: 'code';
+ font-style: normal;
+ font-weight: 500;
+ src: url('fonts/code-normal-500-cyrillic.woff2') format('woff2');
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+
+@font-face {
+ font-family: 'code';
+ font-style: normal;
+ font-weight: 500;
+ src: url('fonts/code-normal-500-greek.woff2') format('woff2');
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+
+@font-face {
+ font-family: 'code';
+ font-style: normal;
+ font-weight: 500;
+ src: url('fonts/code-normal-500-vietnamese.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;
+}
+
+@font-face {
+ font-family: 'code';
+ font-style: normal;
+ font-weight: 500;
+ src: url('fonts/code-normal-500-latin-ext.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;
+}
+
+@font-face {
+ font-family: 'code';
+ font-style: normal;
+ font-weight: 500;
+ src: url('fonts/code-normal-500-latin.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;
+}
+@font-face {
+ font-family: 'serif';
+ font-style: italic;
+ font-weight: 300;
+ src: url('fonts/serif-italic-300-vietnamese.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;
+}
+
+@font-face {
+ font-family: 'serif';
+ font-style: italic;
+ font-weight: 300;
+ src: url('fonts/serif-italic-300-latin-ext.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;
+}
+
+@font-face {
+ font-family: 'serif';
+ font-style: italic;
+ font-weight: 300;
+ src: url('fonts/serif-italic-300-latin.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;
+}
+
+@font-face {
+ font-family: 'serif';
+ font-style: italic;
+ font-weight: 400;
+ src: url('fonts/serif-italic-400-vietnamese.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;
+}
+
+@font-face {
+ font-family: 'serif';
+ font-style: italic;
+ font-weight: 400;
+ src: url('fonts/serif-italic-400-latin-ext.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;
+}
+
+@font-face {
+ font-family: 'serif';
+ font-style: italic;
+ font-weight: 400;
+ src: url('fonts/serif-italic-400-latin.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;
+}
+
+@font-face {
+ font-family: 'serif';
+ font-style: normal;
+ font-weight: 300;
+ src: url('fonts/serif-normal-300-vietnamese.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;
+}
+
+@font-face {
+ font-family: 'serif';
+ font-style: normal;
+ font-weight: 300;
+ src: url('fonts/serif-normal-300-latin-ext.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;
+}
+
+@font-face {
+ font-family: 'serif';
+ font-style: normal;
+ font-weight: 300;
+ src: url('fonts/serif-normal-300-latin.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;
+}
+
+@font-face {
+ font-family: 'serif';
+ font-style: normal;
+ font-weight: 400;
+ src: url('fonts/serif-normal-400-vietnamese.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;
+}
+
+@font-face {
+ font-family: 'serif';
+ font-style: normal;
+ font-weight: 400;
+ src: url('fonts/serif-normal-400-latin-ext.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;
+}
+
+@font-face {
+ font-family: 'serif';
+ font-style: normal;
+ font-weight: 400;
+ src: url('fonts/serif-normal-400-latin.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;
+}
+
+@font-face {
+ font-family: 'serif';
+ font-style: normal;
+ font-weight: 500;
+ src: url('fonts/serif-normal-500-vietnamese.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;
+}
+
+@font-face {
+ font-family: 'serif';
+ font-style: normal;
+ font-weight: 500;
+ src: url('fonts/serif-normal-500-latin-ext.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;
+}
+
+@font-face {
+ font-family: 'serif';
+ font-style: normal;
+ font-weight: 500;
+ src: url('fonts/serif-normal-500-latin.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;
+}
diff --git a/bin/unit_tests/assets/html/body_height_miscalculation.html b/bin/unit_tests/assets/html/body_height_miscalculation.html
new file mode 100644
index 000000000..8297798a9
--- /dev/null
+++ b/bin/unit_tests/assets/html/body_height_miscalculation.html
@@ -0,0 +1,466 @@
+
+
+
+
+
+
+
+ What if your browser built the UI for you? — jonno.nz
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Skip to content
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
What if your browser built the UI for you?
+
We're still shipping hand-crafted frontends while
+ AI can generate entire interfaces. What if the browser itself generated
+ the UI from an API manifest and your preferences?
+
+
+
+
+
+
+
+
+
We're at a genuinely weird inflection point in frontend development. AI can
+generate entire interfaces now. LLMs can reason about data and layout. And yet —
+most SaaS products still ship hand-crafted React apps, each building its own UI,
+its own accessibility layer, its own theme system, its own responsive
+breakpoints. Not every service, but the vast majority.
+
That's a lot of duplicated effort for what's essentially the same job — showing
+a human some data and letting them do stuff with it.
+
I've been thinking about this a lot lately, and I built a proof of concept to
+test an idea: what if the browser itself generated the UI?
+
Where we are right now
+
The industry is circling this idea from multiple angles, but nobody's quite
+landed on it yet.
+
Server-driven UI
+has been around for a while — Airbnb and others pioneered it for mobile, where
+app store review cycles make shipping UI changes painful. The server sends down
+a JSON tree describing what to render, and the client just follows instructions.
+It's clever, but the server is still calling the shots. x.
+
Google recently shipped
+Natively Adaptive Interfaces
+— a framework that uses AI agents to make accessibility a default rather than an
+afterthought. Really cool idea, and the right instinct. But it's still operating
+within a single app's boundaries. Your accessibility preferences don't carry
+between Google's products and, say, your project management tool.
+
Then there's the
+generative UI
+wave — CopilotKit, Vercel's AI SDK, and others building frameworks where LLMs
+generate components on the fly. These are powerful developer tools, but they're
+still developer tools. The generation happens at build time or on the server.
+The service is still in control.
+
See the pattern? Every approach keeps the power on the service side.
+
Flip it
+
Here's the idea behind the
+adaptive browser : what if the
+generation happened on your side?
+
Instead of a service shipping you a finished frontend, it publishes a manifest —
+a structured description of what it can do. Its capabilities, endpoints, data
+shapes, what actions are available. Think of it like an API spec, but semantic.
+Not just "here's a GET endpoint" but "here's a list of repositories, they're
+sortable by stars and language, you can create, delete, star, or fork them."
+
Your browser takes that manifest, calls the actual APIs, gets real data back,
+and then generates the UI based on your preferences. Your font size. Your colour
+scheme. Your preferred layout (tables vs cards vs kanban). Your accessibility
+needs. All applied universally, across every service.
+
The manifest for something like GitHub looks roughly like this — a service
+describes its capabilities and the browser figures out the rest:
+
service:
+ name: "GitHub"
+ domain: "api.github.com"
+
+capabilities:
+ - id: "repositories"
+ endpoints:
+ - path: "/user/repos"
+ semantic: "list"
+ entity: "repository"
+ sortable_fields: [name , updated_at , stargazers_count ]
+ actions: [create , delete , star , fork ]
+
+
The browser takes that, fetches the data, and generates a bespoke interface —
+using an LLM to reason about the best way to present it given who you are and
+what you're trying to do.
+
Why this matters more than it sounds
+
When I was building the app store and integrations platforms at Xero, one of the
+constant headaches was that every third-party integration had its own UI
+patterns. Users had to learn a new interface for every app they connected. If
+the browser was generating the UI from a shared set of preferences, that problem
+just… goes away.
+
Accessibility is the big one though. Right now, accessibility is a feature that
+gets bolted on — and often badly. When the browser generates the UI,
+accessibility isn't a feature. It's the default. Your preferences — high
+contrast, keyboard-first navigation, screen reader optimisation, larger text —
+apply everywhere. Not because every developer remembered to implement them, but
+because they're baked into how the UI gets generated in the first place.
+
Customisation becomes genuinely personal too. Not "pick from three themes the
+developer made" but "this is how I interact with software, full stop."
+
The trade-off is real though
+
Frontend complexity drops dramatically, but the complexity doesn't disappear —
+it moves behind the API. And honestly, it probably increases.
+
API design becomes way more important. You can't just throw together some REST
+endpoints and call it a day. Your manifest needs to be semantic — describing
+what the data means, not just what shape it is. Data contracts between services
+matter more. Versioning matters more.
+
Publishes manifest + APIs
+
But here's the thing — this trade-off pushes us somewhere genuinely interesting.
+If every service needs to describe itself semantically through APIs and
+manifests, those APIs become the actual product surface. Not the frontend. The
+APIs.
+
And once APIs are the product surface, sharing context between platforms becomes
+the interesting problem. Your project management tool knows what you're working
+on. Your email client knows who you're talking to. Your code editor knows what
+you're building. Right now, none of these talk to each other in any meaningful
+way because they're all locked behind their own UIs. In a manifest-driven world,
+that context flows through the APIs — and your browser can stitch it all
+together into something coherent.
+
Where this is headed (IMHO)
+
I reckon we're about 3-5 years from this being mainstream. The pieces are all
+there — LLMs that can reason about UI,
+standardisation efforts around
+sending UI intent over APIs, and a growing expectation from users that software
+should adapt to them, not the other way around.
+
The services that win in this world won't be the ones with the prettiest
+hand-crafted UI. They'll be the ones with the best APIs, the richest manifests,
+and the most useful data. The frontend becomes a generated output, not a
+hand-crafted input.
+
Organisations will set preference guardrails — "our people can use dark or light
+mode, must have destructive action confirmations, these fields are always
+visible" — while individuals customise within those bounds. Your browser becomes
+your agent, not just a renderer.
+
I built the adaptive browser as
+a proof of concept to test this thinking — it uses Claude to generate UIs from a
+GitHub manifest and user preferences defined in YAML. It's rough, but the
+direction feels right.
+
The frontend isn't dying. But what we think of as "frontend development" is
+about to change. The interesting work moves to API design, semantic data
+contracts, and building browsers smart enough to be genuine user agents.
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/include/eepp/ui/uihtmlwidget.hpp b/include/eepp/ui/uihtmlwidget.hpp
index d6a33b121..2c33cf833 100644
--- a/include/eepp/ui/uihtmlwidget.hpp
+++ b/include/eepp/ui/uihtmlwidget.hpp
@@ -46,6 +46,8 @@ class EE_API UIHTMLWidget : public UILayout {
void setCSSClear( CSSClear cssClear );
+ Rectf getNormalFlowLayoutPixelsMargin() const;
+
const CSSBaselineAlignValue& getBaselineAlign() const { return mBaselineAlign; }
void setBaselineAlign( const CSSBaselineAlignValue& baselineAlign );
diff --git a/src/eepp/ui/blocklayouter.cpp b/src/eepp/ui/blocklayouter.cpp
index e04f65bfb..064ec85f1 100644
--- a/src/eepp/ui/blocklayouter.cpp
+++ b/src/eepp/ui/blocklayouter.cpp
@@ -435,7 +435,10 @@ void BlockLayouter::positionRichTextChildren( Graphics::RichText* rt ) {
curCharIdx += 1;
Rectf atomicBounds( maxF, maxF, lowF, lowF );
if ( getAtomicWidgetFragmentBounds( widget, atomicBounds ) ) {
- Rectf margin = widget->getLayoutPixelsMargin();
+ Rectf margin =
+ widget->isType( UI_TYPE_HTML_WIDGET )
+ ? widget->asType()->getNormalFlowLayoutPixelsMargin()
+ : widget->getLayoutPixelsMargin();
Vector2f targetPos( atomicBounds.Left + margin.Left,
atomicBounds.Top + margin.Top );
@@ -459,7 +462,10 @@ void BlockLayouter::positionRichTextChildren( Graphics::RichText* rt ) {
size_t lineIdx = currentSpan > 0 ? currentLine : currentLine - 1;
Float lineY = lines[lineIdx].y;
- Rectf margin = widget->getLayoutPixelsMargin();
+ Rectf margin =
+ widget->isType( UI_TYPE_HTML_WIDGET )
+ ? widget->asType()->getNormalFlowLayoutPixelsMargin()
+ : widget->getLayoutPixelsMargin();
Vector2f targetPos( contentOffset.Left + span->position.x + margin.Left,
contentOffset.Top + lineY + span->position.y + margin.Top );
diff --git a/src/eepp/ui/uihtmlwidget.cpp b/src/eepp/ui/uihtmlwidget.cpp
index f6183b40b..3e30ee932 100644
--- a/src/eepp/ui/uihtmlwidget.cpp
+++ b/src/eepp/ui/uihtmlwidget.cpp
@@ -154,6 +154,15 @@ void UIHTMLWidget::setCSSClear( CSSClear cssClear ) {
}
}
+Rectf UIHTMLWidget::getNormalFlowLayoutPixelsMargin() const {
+ Rectf margin = getLayoutPixelsMargin();
+ if ( hasLayoutMarginTopAuto() )
+ margin.Top = 0.f;
+ if ( hasLayoutMarginBottomAuto() )
+ margin.Bottom = 0.f;
+ return margin;
+}
+
void UIHTMLWidget::setBaselineAlign( const CSSBaselineAlignValue& baselineAlign ) {
if ( mBaselineAlign != baselineAlign ) {
mBaselineAlign = baselineAlign;
diff --git a/src/eepp/ui/uirichtext.cpp b/src/eepp/ui/uirichtext.cpp
index 676569d5d..79a32372b 100644
--- a/src/eepp/ui/uirichtext.cpp
+++ b/src/eepp/ui/uirichtext.cpp
@@ -1331,7 +1331,10 @@ void UIRichText::rebuildRichText( UILayout* container, RichText& richText, Intri
"\n", widget->asType()->getRichText().getFontStyleConfig() );
lastSpanEndsWithSpace = false;
} else {
- Rectf margin = widget->getLayoutPixelsMargin();
+ Rectf margin =
+ widget->isType( UI_TYPE_HTML_WIDGET )
+ ? widget->asType()->getNormalFlowLayoutPixelsMargin()
+ : widget->getLayoutPixelsMargin();
bool isBlock = widget->getLayoutWidthPolicy() == SizePolicy::MatchParent;
if ( widget->isType( UI_TYPE_HTML_WIDGET ) ) {
CSSDisplay display = widget->asType()->getDisplay();
diff --git a/src/tests/unit_tests/uihtml_tests.cpp b/src/tests/unit_tests/uihtml_tests.cpp
index 2dea0a8e4..4f1c5f7ca 100644
--- a/src/tests/unit_tests/uihtml_tests.cpp
+++ b/src/tests/unit_tests/uihtml_tests.cpp
@@ -1650,6 +1650,8 @@ static UISceneNode* init_test_inline_block() {
font = FontTrueType::New( "NotoSans-Regular" );
font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" );
FontFamily::loadFromRegular( font );
+ FontTrueType* monoFont = FontTrueType::New( "monospace" );
+ monoFont->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" );
UISceneNode* sceneNode = UISceneNode::New();
SceneManager::instance()->add( sceneNode );
SceneManager::instance()->setCurrentUISceneNode( sceneNode );
@@ -2002,6 +2004,33 @@ UTEST( UIHTML, HeightExpansion_FixedDoesNotExpand ) {
Engine::destroySingleton();
}
+UTEST( UIHTML, BodyHeightMiscalculationFixture ) {
+ Engine::instance()->createWindow( WindowSettings( 1024, 653, "Body Height Miscalculation Test",
+ WindowStyle::Default, WindowBackend::Default,
+ 32, {}, 1, false, true ),
+ ContextSettings( false, 0, 0, GLv_default, true, false ) );
+
+ UI::UISceneNode* sceneNode = init_test_inline_block();
+ sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" );
+
+ std::string html;
+ FileSystem::fileGet( "assets/html/body_height_miscalculation.html", html );
+
+ sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) );
+ sceneNode->update( Seconds( 1 ) );
+ sceneNode->updateDirtyLayouts();
+
+ auto bodyNode = sceneNode->getRoot()->findByType( UI_TYPE_HTML_BODY );
+ ASSERT_TRUE( bodyNode != nullptr );
+
+ auto bodyWidget = bodyNode->asType();
+
+ EXPECT_GT( bodyWidget->getPixelsSize().getHeight(), 3000.f );
+ EXPECT_LT( bodyWidget->getPixelsSize().getHeight(), 6000.f );
+
+ Engine::destroySingleton();
+}
+
UTEST( UIHTML, ContactFormLayout ) {
Engine::instance()->createWindow( WindowSettings( 1024, 653, "Contact Form Layout Test",
WindowStyle::Default, WindowBackend::Default,