/* layout.css — Three-column page structure: TOC | body | sidenotes */ /* ============================================================ PAGE WRAPPER The outer shell. Wide enough for TOC + body + sidenotes. ============================================================ */ /* Body is plain block — keeps the sticky nav header (a direct body child) working reliably on iOS WebKit, where position: sticky on a direct flex/grid child silently degrades to static. The sticky-footer math (full-viewport min-height + flex column to push footer down) moves to .page-shell, which wraps everything below the nav. */ .page-shell { display: flex; flex-direction: column; min-height: calc(100dvh - var(--nav-height, 4rem)); } /* ============================================================ HEADER Full-width, sits above the three-column area. (Nav styles live in components.css) ============================================================ */ /* Site-nav header only — exclude the essay-frontmatter
that essay/reading/blog templates emit as a body-level sibling so its monogram and figure columns can span full viewport width. Without the :not() guard, the essay header inherits the sticky / nav-bg / border-bottom chrome meant only for the top navigation, painting the wrong color band over the page and pinning the frontmatter to the viewport top. */ body > header:not(.essay-frontmatter) { width: 100%; border-bottom: 1px solid var(--border); background-color: var(--bg-nav); position: sticky; top: 0; z-index: 100; } /* ============================================================ CONTENT AREA Three-column grid: [toc 220px] [body] [phantom 220px] The phantom right column mirrors the TOC width so that the body column is geometrically centered on the viewport. Sidenotes are absolutely positioned inside #markdownBody and overflow into the phantom column naturally. ============================================================ */ #content { display: grid; grid-template-columns: 1fr minmax(0, var(--body-max-width)) 1fr; align-items: start; flex: 1; width: 100%; padding: 2rem var(--page-padding); } /* ============================================================ LEFT COLUMN — TABLE OF CONTENTS Sticky sidebar, collapses below a breakpoint. (TOC content + scroll tracking in toc.js / components.css) ============================================================ */ #toc { grid-column: 1; position: sticky; top: calc(var(--nav-height, 4rem) + 1.5rem); max-height: calc(100vh - var(--nav-height, 4rem) - 3rem); overflow-y: auto; padding-right: 1.5rem; align-self: start; } /* ============================================================ CENTER COLUMN — MAIN BODY Pinned to column 2 explicitly so it stays centered even when the TOC is hidden (below 900px breakpoint). #markdownBody must be position: relative — sidenotes.js appends absolutely-positioned sidenote columns inside it. ============================================================ */ #markdownBody { grid-column: 2; width: min(var(--body-max-width), 100%); position: relative; /* REQUIRED by sidenotes.js */ min-width: 0; } /* ============================================================ STANDALONE PAGES (no #content wrapper) essay-index, blog-index, tag-index, page, blog-post, search — these emit #markdownBody as a direct child of .page-shell. Without the #content flex-row wrapper there is no centering; fix it here. (Was body > #markdownBody before the page-shell wrapper was introduced to keep iOS sticky working.) ============================================================ */ .page-shell > #markdownBody { align-self: center; padding: 2rem var(--page-padding); flex: 1 0 auto; } @media (max-width: 680px) { .page-shell > #markdownBody { padding: 1.25rem var(--page-padding); } } /* ============================================================ FOOTER ============================================================ */ .page-shell > footer { width: 100%; border-top: 1px solid var(--border); padding: 1.5rem var(--page-padding); font-family: var(--font-sans); font-size: var(--text-size-small); color: var(--text-muted); display: flex; justify-content: space-between; align-items: center; margin-top: auto; } .footer-left { display: flex; align-items: center; flex: 1; gap: 1rem; } .footer-left a { color: var(--text-faint); text-decoration: none; transition: color var(--transition-fast); } .footer-left a:hover { color: var(--text-muted); } .footer-center { font-size: 0.72rem; color: var(--text-faint); } .footer-license a { color: var(--text-faint); text-decoration: none; transition: color var(--transition-fast); } .footer-license a:hover { color: var(--text-muted); } .footer-totop { background: none; border: none; cursor: pointer; color: var(--text-faint); font-family: var(--font-sans); font-size: var(--text-size-small); padding: 0; line-height: 1; transition: color var(--transition-fast); } .footer-totop:hover { color: var(--text-muted); } .footer-right { flex: 1; text-align: right; } .footer-build-link { font-size: 0.72rem; color: var(--text-faint); font-variant-numeric: tabular-nums; text-decoration: none; } .footer-build-link:hover { text-decoration: underline; text-underline-offset: 2px; } /* ============================================================ RESPONSIVE BREAKPOINTS ============================================================ */ /* Below ~1100px: not enough horizontal space for sidenotes. The .sidenote asides are hidden by sidenotes.css; the Pandoc-generated section.footnotes is shown instead (also handled by sidenotes.css). */ /* ============================================================ FOCUS MODE Hides the TOC, fades the header until hovered. Activated by [data-focus-mode] on (settings.js). ============================================================ */ [data-focus-mode] #toc { display: none; } /* Below ~900px: hide the TOC. #markdownBody stays in grid-column 2, so it remains centered with the phantom right column still balancing it. */ @media (max-width: 900px) { #toc { display: none; } } /* Below ~680px: collapse to single-column, full-width body. */ @media (max-width: 680px) { #content { grid-template-columns: 1fr; padding: 1.25rem var(--page-padding); } #markdownBody { grid-column: 1; width: 100%; } /* Footer: stack vertically so three sections don't fight for width. */ .page-shell > footer { flex-direction: column; align-items: center; gap: 0.3rem; padding: 0.9rem 1rem; text-align: center; } .footer-left { justify-content: center; } .footer-right { text-align: center; } } /* Below ~900px: body spans full width (TOC hidden, no phantom column). */ @media (max-width: 900px) and (min-width: 681px) { #markdownBody { grid-column: 1 / -1; } }