/* ========================================================================= Fonts ========================================================================= */ @font-face { font-family: 'Source Sans 3'; src: url('/fonts/source-sans-3-latin-variable.woff2') format('woff2-variations'); font-style: normal; font-weight: 200 900; font-display: swap; } @font-face { font-family: 'IBM Plex Mono'; src: url('/fonts/ibm-plex-mono-latin-400.woff2') format('woff2'); font-style: normal; font-weight: 400; font-display: swap; } @layer reset, base, layout, components, utilities, overrides; /* ========================================================================= Tokens: colors, type, space, radius, weights, layout widths ========================================================================= */ :root { color-scheme: light dark; --font-sans: 'Source Sans 3', Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; --font-mono: 'IBM Plex Mono', 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace; /* Palette: light-dark() pairs each token (light, dark) */ --color-bg: light-dark(#fbfaf7, #151514); --color-fg: light-dark(#181817, #f1eee7); /* Contrast with --color-bg: light ~5.4:1, dark ~7.1:1 (both clear WCAG AA 4.5:1 for normal text). Darken-on-light / lighten-on-dark slightly from the previous values that fell just below threshold. */ --color-muted: light-dark(#3d3b35, #c8c0b3); --color-link: light-dark(#285f74, #8ab8c8); --color-link-hover: light-dark( color-mix(in oklch, #285f74 70%, black 30%), color-mix(in oklch, #8ab8c8 70%, white 30%) ); --color-accent: light-dark(oklch(55% 0.13 15), oklch(72% 0.13 15)); --color-rule: light-dark(#d9d5ca, #39352f); --color-rule-medium: light-dark(#7a7466, #8a8478); --color-rule-strong: light-dark(#4a4340, #d0c5b7); --color-code-bg: light-dark(#efede6, #2f2c27); --color-callout-bg: light-dark(#f4f1e8, #211f1c); --color-selection-bg: light-dark(#ecddd0, #4a3a2e); --theme-switcher-track: var(--color-rule-medium); --theme-switcher-icon-light: #f0e2b6; --theme-switcher-icon-dark: #f1eee7; /* Typography */ --fs-xs: 0.75rem; --fs-sm: 0.8125rem; --fs-caption: 0.875rem; --fs-base: 1rem; --fs-body: 1.1875rem; --fs-lg: 1.25rem; --fs-xl: 1.75rem; --fs-3xl: clamp(2rem, 1.5rem + 1.8vw, 3rem); --fs-dek: clamp(1.08rem, 0.95rem + 0.6vw, 1.25rem); --leading-tight: 1.18; --leading-snug: 1.35; --leading-prose: 1.6; --weight-regular: 400; --weight-medium: 500; --weight-semibold: 650; --weight-bold: 700; /* Spacing */ --space-1: 0.25rem; --space-2: 0.5rem; --space-3: 0.75rem; --space-4: 1rem; --space-5: 1.25rem; --space-6: 1.5rem; --space-8: 2rem; --space-10: 2.5rem; --space-12: 3rem; --space-16: 4rem; /* Radius */ --radius-sm: 4px; --radius-md: 6px; --radius-lg: 8px; --radius-pill: 999px; /* Layout */ --measure: 36rem; --measure-wide: 56rem; --page: 72rem; --gutter: clamp(20px, 4vw, 32px); } :root[data-theme='light'] { color-scheme: light; } :root[data-theme='dark'] { color-scheme: dark; } /* ========================================================================= Reset ========================================================================= */ @layer reset { *, *::before, *::after { box-sizing: border-box; } body, h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd { margin: 0; } ul[role='list'], ol[role='list'] { list-style: none; padding: 0; } button, input, textarea, select { font: inherit; } img, video, canvas, svg { display: block; max-width: 100%; height: auto; } } /* ========================================================================= Base ========================================================================= */ @layer base { html { background: var(--color-bg); scroll-behavior: smooth; } body { min-block-size: 100vh; min-block-size: 100dvh; display: flex; flex-direction: column; background: var(--color-bg); color: var(--color-fg); font-family: var(--font-sans); font-size: var(--fs-body); line-height: var(--leading-snug); transition: background-color 200ms ease, color 200ms ease; } address { font-style: normal; } a { color: var(--color-link); text-decoration-thickness: 0.08em; text-underline-offset: 0.18em; transition: color 150ms ease; } a:hover { color: var(--color-link-hover); } a:active { opacity: 0.85; transition: opacity 80ms ease; } :focus-visible { outline: 2px solid var(--color-accent); outline-offset: 3px; } main:focus-visible { outline: 2px solid var(--color-accent); outline-offset: -2px; } ::selection { background: var(--color-selection-bg); color: var(--color-fg); } } /* ========================================================================= Utilities ========================================================================= */ @layer utilities { .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; } } /* ========================================================================= Layout: site shell, header, footer, skip link ========================================================================= */ @layer layout { :where(.site-header, .site-footer, .home-intro, .home-section, .page-shell, .post) { width: min(100% - 2 * var(--gutter), var(--page)); margin-inline: auto; } .post { width: min(100% - 2 * var(--gutter), var(--measure-wide)); } .skip-link { position: absolute; left: calc(var(--gutter) + env(safe-area-inset-left)); top: calc(var(--space-3) + env(safe-area-inset-top)); z-index: 10; transform: translateY(-150%); background: var(--color-fg); color: var(--color-bg); padding: var(--space-3) var(--space-4); min-block-size: 44px; display: inline-flex; align-items: center; text-decoration: none; transition: transform 150ms ease; } .skip-link:focus { transform: translateY(0); outline: 2px solid var(--color-accent); outline-offset: 2px; } .site-header { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap; padding-block: var(--space-8) var(--space-6); border-bottom: 1px solid var(--color-rule); } main { flex: 1 0 auto; } .site-title { color: var(--color-fg); font-size: var(--fs-lg); font-weight: var(--weight-bold); letter-spacing: -0.005em; text-decoration: none; } .header-actions { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2) var(--space-6); min-width: 0; } .site-nav { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-5); } .site-nav a, .site-footer a { min-height: 44px; display: inline-flex; align-items: center; color: var(--color-muted); text-decoration: none; } .site-nav a:hover, .site-footer a:hover { color: var(--color-fg); text-decoration: underline; text-underline-offset: 0.25em; } .site-nav a[aria-current='page'], .site-nav a[aria-current='true'] { color: var(--color-fg); } .site-footer { border-top: 1px solid var(--color-rule); margin-top: var(--space-16); padding-block: var(--space-8) var(--space-10); } .footer-meta { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2) var(--space-5); margin: 0; padding: 0; list-style: none; color: var(--color-muted); font-size: var(--fs-caption); } .footer-meta a, .footer-meta span { min-height: 44px; display: inline-flex; align-items: center; } .footer-meta a { padding-inline: var(--space-1); margin-inline: calc(-1 * var(--space-1)); } .footer-contact { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2) var(--space-5); } /* Page header (shared by .home-intro, .page-header, .post-header) */ .home-intro { padding-block: clamp(2rem, 5vw, 4rem) var(--space-6); } .home-intro h1, .page-header h1, .post-header h1 { max-width: var(--measure-wide); color: var(--color-fg); font-size: var(--fs-3xl); font-weight: var(--weight-semibold); line-height: var(--leading-tight); text-wrap: balance; } .home-intro-name { color: var(--color-accent); } .home-intro p:not(.eyebrow), .page-header p, .dek { max-width: var(--measure); color: var(--color-muted); font-size: var(--fs-dek); } .page-header, .post-header { max-width: var(--measure-wide); padding-block: var(--space-10) var(--space-6); } .post-header .dek { margin-block: var(--space-4) 0; } .post-meta { margin-block: var(--space-3) 0; color: var(--color-muted); font-size: var(--fs-caption); line-height: 1.4; } .home-section, .page-shell { margin-top: var(--space-8); } } /* ========================================================================= Components ========================================================================= */ @layer components { /* -- Eyebrow ---------------------------------------------------------- */ .eyebrow { margin: 0 0 var(--space-3); color: var(--color-muted); font-size: var(--fs-caption); line-height: 1.4; font-weight: var(--weight-semibold); letter-spacing: 0.08em; text-transform: uppercase; } /* -- Section heading -------------------------------------------------- */ .section-heading { width: 100%; display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap; margin-bottom: var(--space-4); padding-top: var(--space-6); } :where(.section-heading, .archive-year, .project-section, .facts, .at-a-glance) h2 { font-size: var(--fs-lg); font-weight: var(--weight-semibold); line-height: var(--leading-snug); } .section-heading a { min-height: 44px; display: inline-flex; align-items: center; color: var(--color-muted); font-size: var(--fs-caption); text-decoration: none; } .section-heading a:hover { color: var(--color-fg); text-decoration: underline; text-underline-offset: 0.25em; } /* -- Breadcrumbs ------------------------------------------------------ */ .breadcrumbs { margin: 0 0 var(--space-3); padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: var(--space-2); color: var(--color-muted); font-size: var(--fs-caption); } .breadcrumbs li { display: inline-flex; align-items: baseline; min-width: 0; } .breadcrumbs li:not(:last-child)::after { content: '›'; margin-left: var(--space-2); color: var(--color-rule-medium); flex: none; } .breadcrumbs a { color: var(--color-muted); text-decoration: none; } .breadcrumbs a:hover { color: var(--color-fg); text-decoration: underline; text-underline-offset: 0.25em; } .breadcrumbs [aria-current='page'] { color: var(--color-fg); } /* -- Tag list + filter ------------------------------------------------ */ .tag-cloud { margin-top: var(--space-2); } .tag-list { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-2); margin: var(--space-2) 0 0; padding: 0; list-style: none; color: var(--color-muted); font-size: var(--fs-caption); } .tag-list a { min-height: 44px; display: inline-flex; align-items: center; padding-inline: var(--space-2); margin-inline: calc(-1 * var(--space-2)); color: var(--color-muted); text-decoration: none; } .tag-list a::before { content: '#'; color: var(--color-rule-medium); } .tag-list .tag-more::before { content: none; } .tag-list .tag-count { margin-inline-start: 0.35em; padding: 0 0.4em; border-radius: var(--radius-pill); background: var(--color-code-bg); color: var(--color-fg); font-size: var(--fs-caption); font-variant-numeric: tabular-nums; } .tag-list a:hover, .tag-list a[aria-current='page'] { color: var(--color-fg); } .tag-filter { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-2) var(--space-4); margin: 0 0 var(--space-8); padding-block: var(--space-3); border-block: 1px solid var(--color-rule); } .tag-filter > span { color: var(--color-muted); font-size: var(--fs-caption); } /* -- Lists: article + project ---------------------------------------- */ .article-list, .project-list { list-style: none; margin: 0; padding: 0; width: 100%; } .project-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); gap: var(--space-4); align-items: stretch; } .article-list > li { display: grid; grid-template-columns: 4.5rem minmax(0, 1fr) minmax(6rem, 8rem); grid-template-areas: 'date content thumb'; align-items: center; gap: var(--space-4); padding-block: var(--space-6); border-top: 1px solid var(--color-rule); } .article-list > li:first-child { border-top: 0; } .article-list time { grid-area: date; color: var(--color-muted); font-size: var(--fs-caption); text-align: end; } .article-list > li > article { grid-area: content; min-width: 0; padding-right: var(--space-3); } .article-list h3, .project-list h3 { font-size: var(--fs-base); line-height: var(--leading-snug); } .article-list .entry-title, .project-list h3 a { display: inline-flex; align-items: center; min-height: 28px; color: var(--color-fg); font-weight: var(--weight-semibold); text-decoration: none; } .article-list .entry-title:hover, .project-list h3 a:hover { color: var(--color-link-hover); } .article-list p, .project-list p { margin: var(--space-1) 0 0; color: var(--color-muted); } /* -- Thumbnail -------------------------------------------------------- */ .entry-thumbnail { display: block; overflow: hidden; border: 1px solid var(--color-rule); border-radius: var(--radius-md); background: var(--color-code-bg); aspect-ratio: 4 / 3; transition: border-color 150ms ease; } .entry-thumbnail img { width: 100%; height: 100%; object-fit: cover; transition: transform 300ms ease; } a.entry-thumbnail { text-decoration: none; } a.entry-thumbnail:hover, a.entry-thumbnail:focus-visible { border-color: var(--color-rule-strong); } .article-list > li:hover .entry-thumbnail img, .article-list > li:focus-within .entry-thumbnail img { transform: scale(1.02); } .article-list > li:focus-within .entry-thumbnail { border-color: var(--color-rule-strong); } .article-thumbnail { grid-area: thumb; align-self: center; } .article-list--timeline { --timeline-date-column: 6.25rem; --timeline-marker-column: 1.125rem; --timeline-marker-offset: 0.5625rem; --timeline-gap: var(--space-4); --timeline-date-offset: 1.125rem; --timeline-dot-top: 50%; --timeline-marker-center: calc( var(--timeline-date-column) + var(--timeline-gap) + var(--timeline-marker-offset) ); } .article-list--timeline > li { position: relative; grid-template-columns: var(--timeline-date-column) var(--timeline-marker-column) minmax(0, 1fr) minmax(6rem, 8rem); grid-template-areas: 'date marker content thumb'; column-gap: var(--timeline-gap); border-top: 0; } .article-list--timeline > li::before, .article-list--timeline > li::after { content: ''; position: absolute; left: var(--timeline-marker-center); z-index: 0; pointer-events: none; } .article-list--timeline > li::before { inset-block: 0; width: 1px; transform: translateX(-50%); background: var(--color-rule-medium); } .article-list--timeline > li:first-child::before { inset-block-start: var(--timeline-dot-top); } .article-list--timeline > li:last-child::before { inset-block-end: calc(100% - var(--timeline-dot-top)); } .article-list--timeline > li:only-child::before { content: none; } .article-list--timeline > li::after { top: var(--timeline-dot-top); width: 0.75rem; height: 0.75rem; border: 2px solid var(--color-bg); border-radius: var(--radius-pill); background: var(--color-accent); box-shadow: 0 0 0 1px var(--color-rule-strong); transform: translate(-50%, -50%); } .article-list--timeline > li > article, .article-list--timeline time, .article-list--timeline .entry-thumbnail { position: relative; z-index: 1; } .article-list--timeline time { justify-self: end; align-self: center; width: max-content; min-block-size: 0; margin-inline-end: calc(-1 * var(--timeline-date-offset)); padding-inline-end: 4px; display: inline-block; color: var(--color-muted); font-size: var(--fs-sm); line-height: 1.2; text-align: center; white-space: nowrap; top: 5px; transform: rotate(-45deg); transform-origin: right center; } /* -- Project card ----------------------------------------------------- */ .project-card { display: grid; grid-template-columns: 1fr; grid-template-areas: 'thumb' 'summary'; min-width: 0; overflow: hidden; border: 1px solid var(--color-rule); border-radius: var(--radius-lg); background: var(--color-bg); transition: border-color 150ms ease; } .project-card:hover, .project-card:focus-within, .project-card:target { border-color: var(--color-rule-strong); } .project-card .project-thumbnail { grid-area: thumb; width: 100%; height: auto; border: 0; border-bottom: 1px solid var(--color-rule); border-radius: 0; aspect-ratio: 4 / 3; } .project-card .project-thumbnail img { transition: transform 300ms ease; } .project-card:hover .project-thumbnail img, .project-card:focus-within .project-thumbnail img { transform: scale(1.02); } .project-card__summary { grid-area: summary; display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; padding: var(--space-3) var(--space-4); } .project-card p { margin: 0; } .project-card .project-description { color: var(--color-fg); font-size: var(--fs-base); line-height: var(--leading-snug); } .project-card .project-meta { color: var(--color-muted); font-size: var(--fs-sm); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .project-essay-badge { display: inline-flex; align-items: center; margin-left: var(--space-2); padding: 0.1em 0.5em; background: var(--color-callout-bg); border: 1px solid var(--color-rule); border-radius: var(--radius-pill); color: var(--color-muted); font-size: var(--fs-xs); font-weight: var(--weight-medium); text-transform: uppercase; letter-spacing: 0.08em; vertical-align: 0.15em; } /* -- Project links ---------------------------------------------------- */ .project-links { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3); margin: var(--space-3) 0 0; padding: 0; list-style: none; } .project-links a { min-height: 44px; min-width: 44px; display: inline-flex; align-items: center; color: var(--color-link); } .project-links a:hover, .project-links a:focus-visible { color: var(--color-link-hover); } .project-links a .download-indicator { margin-left: 0.25em; color: var(--color-muted); font-size: 0.85em; } .project-card .project-links { gap: 0 var(--space-3); margin-top: auto; font-size: var(--fs-caption); } .project-card .project-links a { min-height: 44px; } /* -- Post layout ------------------------------------------------------ */ .post > .post-media, .facts { max-width: var(--measure); margin-inline: auto; } .post > .prose { max-width: var(--measure-wide); max-inline-size: var(--measure-wide); margin-inline: auto; } .post > .at-a-glance, .post > .post-thumbnail, .post > .post-gallery, .post-nav { max-width: var(--measure-wide); margin-inline: auto; } .archive-year, .project-section { width: 100%; } .archive-year + .archive-year, .project-section + .project-section { margin-top: var(--space-10); } .archive-year h2, .project-section h2 { margin-bottom: var(--space-3); } .about-section { width: 100%; margin-top: var(--space-10); } .about-section.facts { max-width: none; } .about-section.facts > .prose { margin-top: var(--space-4); } .starting-points { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--space-3); margin: 0; padding: var(--space-4) 0 0; border-top: 1px solid var(--color-rule); list-style: none; } .starting-points > li { min-width: 0; } .starting-point__thumbnail { aspect-ratio: 4 / 3; } .starting-point__body { min-width: 0; padding-block-start: var(--space-2); } .starting-point__body h3 { font-size: var(--fs-sm); font-weight: var(--weight-semibold); line-height: var(--leading-snug); } .starting-point__body a { color: var(--color-fg); text-decoration: none; } .starting-point__body a:hover { color: var(--color-link-hover); text-decoration: underline; text-underline-offset: 0.2em; } .starting-point__body p { margin-top: var(--space-1); color: var(--color-muted); font-size: var(--fs-xs); line-height: var(--leading-snug); } .starting-points > li:hover .entry-thumbnail img, .starting-points > li:focus-within .entry-thumbnail img { transform: scale(1.02); } .starting-points > li:focus-within .entry-thumbnail { border-color: var(--color-rule-strong); } .about-links { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-4); } .post > .prose { margin-top: var(--space-8); } .post-thumbnail { margin: 0; } .post-thumbnail img { width: 100%; border: 1px solid var(--color-rule); border-radius: var(--radius-md); background: var(--color-code-bg); } .post-thumbnail--iframe { position: relative; aspect-ratio: var(--post-thumbnail-aspect, 16 / 9); overflow: hidden; border: 1px solid var(--color-rule); border-radius: var(--radius-md); background: var(--color-code-bg); } .post-thumbnail--iframe picture, .post-thumbnail--iframe img { width: 100%; height: 100%; } .post-thumbnail--iframe picture { display: block; } .post-thumbnail--iframe img { object-fit: cover; border: 0; border-radius: 0; } .post-thumbnail__play { position: absolute; inset: 0; width: 100%; border: 0; padding: 0; display: grid; place-items: center; background: color-mix(in oklch, #000 22%, transparent); color: var(--color-fg); cursor: pointer; } .post-thumbnail__play:hover, .post-thumbnail__play:focus-visible { background: color-mix(in oklch, #000 30%, transparent); } .post-thumbnail__play-icon { width: clamp(3.25rem, 9vw, 4.75rem); aspect-ratio: 1; display: grid; place-items: center; border: 1px solid var(--color-rule-strong); border-radius: var(--radius-pill); background: color-mix(in oklch, var(--color-bg) 88%, transparent); box-shadow: 0 0.75rem 2rem color-mix(in oklch, #000 28%, transparent); transition: background-color 150ms ease, transform 150ms ease; } .post-thumbnail__play:hover .post-thumbnail__play-icon, .post-thumbnail__play:focus-visible .post-thumbnail__play-icon { background: var(--color-bg); transform: scale(1.04); } .post-thumbnail__play svg { width: 42%; height: 42%; transform: translateX(8%); fill: currentColor; } .post-thumbnail__iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; background: var(--color-code-bg); } .post-thumbnail--iframe.is-active picture, .post-thumbnail--iframe.is-active .post-thumbnail__play { display: none; } .post-thumbnail__noscript { position: absolute; inset-inline: var(--space-3); inset-block-end: var(--space-3); padding: var(--space-2) var(--space-3); border-radius: var(--radius-md); background: var(--color-bg); font-size: var(--fs-caption); } /* -- Prose ------------------------------------------------------------ */ .prose { max-inline-size: var(--measure); line-height: var(--leading-prose); } .prose > * + * { margin-top: 1.05em; } .prose > h2:first-child, .prose > h3:first-child { margin-top: 0; } .prose p { text-wrap: pretty; } .prose h2, .prose h3 { position: relative; color: var(--color-fg); line-height: var(--leading-tight); scroll-margin-top: var(--space-8); } .prose h2 { margin-top: var(--space-12); font-size: var(--fs-xl); font-weight: var(--weight-semibold); } .prose h3 { margin-top: var(--space-8); font-size: var(--fs-lg); font-weight: var(--weight-semibold); } .prose h2:target, .prose h3:target { background: var(--color-callout-bg); border-inline-start: 2px solid var(--color-accent); padding-inline-start: var(--space-3); margin-inline-start: calc(-1 * var(--space-3) - 2px); } .prose .heading-anchor { margin-inline-start: 0.4em; color: var(--color-muted); font-weight: var(--weight-regular); font-size: 0.85em; text-decoration: none; opacity: 0; transition: opacity 150ms ease; } .prose .heading-anchor:focus-visible { opacity: 1; text-decoration: underline; } .prose .heading-anchor::before { content: '#'; } .prose h2:hover .heading-anchor, .prose h3:hover .heading-anchor, .prose .heading-anchor:hover, .prose .heading-anchor:focus-visible { opacity: 1; } @media (hover: none) { .prose .heading-anchor { opacity: 1; } } .prose ul, .prose ol { padding-inline-start: 1.25rem; } .prose li + li { margin-top: var(--space-1); } .prose strong { font-weight: var(--weight-bold); color: var(--color-fg); } .prose em { font-style: italic; } .prose blockquote { margin-inline: 0; padding: var(--space-2) var(--space-4); border-inline-start: 3px solid var(--color-rule-medium); background: var(--color-callout-bg); color: var(--color-muted); font-style: italic; } .prose blockquote > * + * { margin-top: 0.6em; } .prose hr { margin-block: var(--space-8); border: 0; height: 1px; background: var(--color-rule); } .prose table { width: 100%; max-width: 100%; border-collapse: collapse; font-size: 0.95em; display: block; overflow-x: auto; } .prose thead { background: var(--color-code-bg); text-align: start; } .prose th, .prose td { padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--color-rule); vertical-align: top; } .prose th { font-weight: var(--weight-semibold); } .prose code { font-family: var(--font-mono); font-size: 0.88em; background: var(--color-code-bg); border-radius: var(--radius-sm); padding: 0.08em 0.25em; } .prose pre { max-width: 100%; overflow-x: auto; scrollbar-gutter: stable; padding: var(--space-4); background: var(--color-code-bg); border: 1px solid var(--color-rule); border-radius: var(--radius-md); line-height: 1.55; scrollbar-width: thin; scrollbar-color: var(--color-rule-medium) var(--color-code-bg); } .prose pre::-webkit-scrollbar { height: 8px; } .prose pre::-webkit-scrollbar-track { background: var(--color-code-bg); } .prose pre::-webkit-scrollbar-thumb { background: var(--color-rule-medium); border-radius: var(--radius-sm); } .prose pre code { background: transparent; padding: 0; } /* Shiki dual-theme: defaultColor: false emits --shiki-light / --shiki-dark vars on every token; light-dark() picks the active variant from color-scheme on :root. */ .prose pre.astro-code, .prose pre.astro-code code, .prose pre.astro-code span { color: light-dark(var(--shiki-light), var(--shiki-dark)); background-color: light-dark( var(--shiki-light-bg, var(--color-code-bg)), var(--shiki-dark-bg, var(--color-code-bg)) ); } /* -- At-a-glance + facts --------------------------------------------- */ .at-a-glance { margin-top: var(--space-8); padding-block: var(--space-5); border-block: 1px solid var(--color-rule); } .at-a-glance dl, .facts dl { display: flex; flex-direction: column; gap: var(--space-2); margin: var(--space-4) 0 0; } .at-a-glance__row, .facts dl > div { display: grid; grid-template-columns: minmax(6rem, max-content) minmax(0, 1fr); gap: var(--space-4); } .at-a-glance dt, .facts dt { color: var(--color-muted); font-size: var(--fs-caption); } .facts { margin-top: var(--space-8); padding-top: var(--space-6); border-top: 1px solid var(--color-rule); } /* Let prose wrap beside .at-a-glance and continue below it. */ @media (min-width: 1100px) { .post > .at-a-glance { float: right; width: min(18rem, 42%); margin-top: var(--space-8); margin-right: 0; margin-bottom: var(--space-4); margin-left: var(--space-8); } .post > .post-media, .post > .post-gallery, .related-posts, .post-nav { clear: both; } } /* -- Post media ------------------------------------------------------- */ .post-media { max-inline-size: min(100%, var(--measure-wide)); margin: var(--space-8) 0 0; } .post-media img, .post-media video { border: 1px solid var(--color-rule); border-radius: var(--radius-md); background: var(--color-code-bg); width: 100%; } .post-media figcaption, .media-transcript { max-width: var(--measure); margin-top: var(--space-2); color: var(--color-muted); font-size: var(--fs-caption); line-height: 1.45; } .media-transcript strong { color: var(--color-fg); font-weight: var(--weight-semibold); } /* -- Post nav --------------------------------------------------------- */ .post-nav { margin-top: var(--space-12); padding-top: var(--space-6); border-top: 1px solid var(--color-rule); } .post-nav__list { display: grid; grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); gap: var(--space-4); list-style: none; padding: 0; margin: 0; } .post-nav__next { justify-self: end; } .post-nav a { display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-3) var(--space-4); border: 1px solid var(--color-rule); border-radius: var(--radius-md); color: var(--color-fg); text-decoration: none; min-height: 44px; transition: border-color 150ms ease; } .post-nav a:hover, .post-nav a:focus-visible { border-color: var(--color-rule-strong); } .post-nav a.next { text-align: end; } .post-nav .post-nav__label { color: var(--color-muted); font-size: var(--fs-caption); } .post-nav .post-nav__title { color: var(--color-fg); font-weight: var(--weight-semibold); } /* -- Post TOC --------------------------------------------------------- */ .post-toc { margin-top: var(--space-6); padding: var(--space-3) var(--space-4); border-inline-start: 2px solid var(--color-rule); font-size: var(--fs-caption); color: var(--color-muted); max-height: 60vh; overflow-y: auto; } .post-toc .post-nav__title, .post-header h1, .post-nav .post-nav__title, .project-card h3 { overflow-wrap: anywhere; } .project-card h3 a { min-height: 44px; min-width: 44px; } .post-toc ol { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: var(--space-1); } .post-toc a { display: inline-flex; align-items: center; min-block-size: 24px; padding-block: 2px; color: var(--color-muted); text-decoration: none; } .post-toc a:hover, .post-toc a:focus-visible { color: var(--color-fg); } /* -- Post media gallery ----------------------------------------------- */ .post-gallery { list-style: none; padding: 0; margin: var(--space-8) 0 0; display: grid; gap: var(--space-6); } .post > .post-gallery { width: 100%; } .post-gallery .post-media { max-inline-size: 100%; margin: 0; } /* -- External link affordance ----------------------------------------- */ .external-link-icon { display: inline-block; margin-inline-start: 0.25em; vertical-align: -0.125em; opacity: 0.85; } /* -- Related ---------------------------------------------------------- */ .related-posts { margin-top: var(--space-12); padding-top: var(--space-6); border-top: 1px solid var(--color-rule); } .related-posts h2 { font-size: var(--fs-lg); font-weight: var(--weight-semibold); margin-bottom: var(--space-4); } /* -- Empty state (e.g. 404) ----------------------------------------- */ .empty-state { max-width: var(--measure); padding-block: var(--space-6); } /* -- Theme switcher --------------------------------------------------- */ .theme-switcher { --switcher-w: 2.75rem; --switcher-h: 1.5rem; --switcher-icon: 1.05rem; --switcher-gap: 0.22rem; position: relative; display: inline-flex; align-items: center; justify-content: center; width: var(--switcher-w); height: var(--switcher-h); /* Adjacent header targets remain at least 44px apart while the visual track stays compact. */ margin: max(var(--space-2), calc((44px - var(--switcher-h)) / 2)) 0; overflow: hidden; border: 1px solid var(--color-rule-medium); border-radius: var(--radius-pill); appearance: none; cursor: pointer; background: var(--theme-switcher-track); color: inherit; transition: background-color 200ms ease, border-color 150ms ease; box-shadow: inset 0 1px 2px rgb(0 0 0 / 18%); } .theme-switcher:hover { border-color: var(--color-rule-strong); } .no-js .theme-switcher { display: none !important; } .theme-switcher-icon { position: absolute; top: 50%; left: 0; width: var(--switcher-icon); height: var(--switcher-icon); pointer-events: none; transition: transform 180ms ease, opacity 180ms ease, color 180ms ease; } .theme-switcher-icon-sun { color: var(--theme-switcher-icon-light); } .theme-switcher-icon-moon { color: var(--theme-switcher-icon-dark); } .theme-switcher[aria-pressed='false'] .theme-switcher-icon-sun { transform: translateY(-50%) translateX(calc(var(--switcher-w) - var(--switcher-icon) - var(--switcher-gap))); opacity: 1; } .theme-switcher[aria-pressed='false'] .theme-switcher-icon-moon { transform: translateY(-50%) translateX(calc(-1 * var(--switcher-icon))); opacity: 0; } .theme-switcher[aria-pressed='true'] .theme-switcher-icon-sun { transform: translateY(-50%) translateX(var(--switcher-w)); opacity: 0; } .theme-switcher[aria-pressed='true'] .theme-switcher-icon-moon { transform: translateY(-50%) translateX(var(--switcher-gap)); opacity: 1; } /* High-contrast / forced-colors fallback: render a text label. */ @media (forced-colors: active) { .theme-switcher { width: auto; height: auto; min-block-size: 44px; min-inline-size: 44px; display: inline-flex; align-items: center; justify-content: center; padding: var(--space-1) var(--space-2); overflow: visible; background: ButtonFace; color: ButtonText; border: 1px solid ButtonBorder; box-shadow: none; } .theme-switcher-icon { display: none; } .theme-switcher::before { content: 'Light'; position: static; width: auto; height: auto; transform: none; background: transparent; border-radius: 0; } .theme-switcher[aria-pressed='true']::before { content: 'Dark'; transform: none; } } } /* ========================================================================= Responsive: tablet + mobile breakpoints ========================================================================= */ @layer overrides { /* Tablet */ @media (max-width: 960px) { .article-list > li { grid-template-columns: 5rem minmax(0, 1fr) 7rem; gap: var(--space-4); padding-block: var(--space-5); } .article-list--timeline { --timeline-date-column: 5.75rem; --timeline-gap: var(--space-4); --timeline-date-offset: 1.125rem; } .article-list--timeline > li { grid-template-columns: var(--timeline-date-column) var(--timeline-marker-column) minmax(0, 1fr) 7rem; grid-template-areas: 'date marker content thumb'; column-gap: var(--timeline-gap); } .starting-points { grid-template-columns: repeat(3, minmax(0, 1fr)); } } /* Mobile */ @media (max-width: 700px) { .site-header { padding-block: var(--space-6) var(--space-4); } .home-intro { padding-block: var(--space-8) var(--space-6); } .at-a-glance__row, .facts dl > div { grid-template-columns: 1fr; gap: var(--space-1); } .article-list > li { grid-template-columns: clamp(4rem, 22vw, 5rem) minmax(0, 1fr); grid-template-areas: 'thumb content' 'date content'; gap: var(--space-2) var(--space-3); padding-block: var(--space-4); } .article-list--timeline { --timeline-date-column: clamp(3.75rem, 18vw, 4.75rem); --timeline-marker-column: 1rem; --timeline-marker-offset: 0.5rem; --timeline-gap: var(--space-3); --timeline-date-offset: 0.875rem; --timeline-dot-top: 2.1875rem; } .article-list--timeline > li { grid-template-columns: var(--timeline-date-column) var(--timeline-marker-column) minmax(0, 1fr); grid-template-areas: 'date marker content' 'thumb marker content'; gap: var(--space-2) var(--timeline-gap); } .article-list > li > article { padding-right: 0; } .article-list time { text-align: start; white-space: nowrap; } .article-list--timeline time { align-self: start; margin-block-start: var(--space-3); font-size: var(--fs-xs); text-align: center; } .article-list .entry-thumbnail { aspect-ratio: 1; } .starting-points { display: block; padding-top: 0; } .starting-points > li { display: grid; grid-template-columns: 4rem minmax(0, 1fr); align-items: center; gap: var(--space-3); padding-block: var(--space-3); border-top: 1px solid var(--color-rule); } .starting-points > li:first-child { border-top: 0; } .starting-point__thumbnail { aspect-ratio: 1; } .starting-point__body { padding-block-start: 0; } .project-card .project-meta { -webkit-line-clamp: 3; } .project-card__summary { padding: var(--space-2) var(--space-3); } .page-header, .post-header { padding-block: var(--space-8) var(--space-5); } .post > .prose { margin-top: var(--space-6); } :focus-visible { outline-offset: 1px; } /* Preserve the inset outline on
so the post-skip-link focus ring doesn't escape its container. Repeated here because this layer wins over the base rule regardless of selector specificity. */ main:focus-visible { outline-offset: -2px; } .post-nav__list { grid-template-columns: 1fr; } .post-nav__next { justify-self: stretch; } .post-nav a.next { text-align: start; } .header-actions { justify-content: space-between; width: 100%; gap: var(--space-2) var(--space-4); } .site-nav { gap: var(--space-1) var(--space-6); } } /* Reduced motion */ @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none; } } /* Print */ @media print { :root { --color-bg: #fff; --color-fg: #000; --color-muted: #333; --color-link: #000; --color-link-hover: #000; --color-accent: #000; --color-rule: #999; --color-rule-medium: #777; --color-rule-strong: #333; --color-code-bg: #f4f4f4; --color-callout-bg: #f8f8f8; } body { font-size: 11pt; line-height: 1.4; } *, *::before, *::after { print-color-adjust: economy; } .site-header, .site-footer, .skip-link, .theme-switcher, .tag-filter, .post-nav, .related-posts, .heading-anchor { display: none; } main { padding: 0; } a, a:visited { color: var(--color-fg); text-decoration: underline; } .prose a[href]::after { content: ' (' attr(href) ')'; font-size: 0.85em; color: var(--color-muted); } .prose a[href^='#']::after, .prose a[href^='/']::after { content: ''; } .prose pre, .prose code, .post-thumbnail img, .post-media img { page-break-inside: avoid; } .prose h2, .prose h3 { page-break-after: avoid; } } }