2026-07-04 22:53:10 +00:00
|
|
|
|
@import "tailwindcss" source(none);
|
|
|
|
|
|
@source "../src";
|
|
|
|
|
|
@import "tw-animate-css";
|
|
|
|
|
|
|
|
|
|
|
|
@font-face {
|
|
|
|
|
|
font-family: "Inter";
|
|
|
|
|
|
font-style: normal;
|
|
|
|
|
|
font-weight: 400 700;
|
|
|
|
|
|
font-display: optional;
|
|
|
|
|
|
src: url("/fonts/inter-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: "Fraunces";
|
|
|
|
|
|
font-style: normal;
|
|
|
|
|
|
font-weight: 400 700;
|
|
|
|
|
|
font-display: optional;
|
|
|
|
|
|
src: url("/fonts/fraunces-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: "JetBrains Mono";
|
|
|
|
|
|
font-style: normal;
|
|
|
|
|
|
font-weight: 400 500;
|
|
|
|
|
|
font-display: optional;
|
|
|
|
|
|
src: url("/fonts/jetbrains-mono-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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@custom-variant dark (&:is(.dark *));
|
|
|
|
|
|
|
|
|
|
|
|
@theme inline {
|
|
|
|
|
|
--radius-sm: calc(var(--radius) - 4px);
|
|
|
|
|
|
--radius-md: calc(var(--radius) - 2px);
|
|
|
|
|
|
--radius-lg: var(--radius);
|
|
|
|
|
|
--radius-xl: calc(var(--radius) + 4px);
|
|
|
|
|
|
--color-background: var(--background);
|
|
|
|
|
|
--color-foreground: var(--foreground);
|
|
|
|
|
|
--color-card: var(--card);
|
|
|
|
|
|
--color-card-foreground: var(--card-foreground);
|
|
|
|
|
|
--color-popover: var(--popover);
|
|
|
|
|
|
--color-popover-foreground: var(--popover-foreground);
|
|
|
|
|
|
--color-primary: var(--primary);
|
|
|
|
|
|
--color-primary-foreground: var(--primary-foreground);
|
|
|
|
|
|
--color-secondary: var(--secondary);
|
|
|
|
|
|
--color-secondary-foreground: var(--secondary-foreground);
|
|
|
|
|
|
--color-muted: var(--muted);
|
|
|
|
|
|
--color-muted-foreground: var(--muted-foreground);
|
|
|
|
|
|
--color-accent: var(--accent);
|
|
|
|
|
|
--color-accent-foreground: var(--accent-foreground);
|
|
|
|
|
|
--color-destructive: var(--destructive);
|
|
|
|
|
|
--color-destructive-foreground: var(--destructive-foreground);
|
|
|
|
|
|
--color-border: var(--border);
|
|
|
|
|
|
--color-input: var(--input);
|
|
|
|
|
|
--color-ring: var(--ring);
|
|
|
|
|
|
--font-serif: "Fraunces", ui-serif, Georgia, serif;
|
|
|
|
|
|
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
|
|
|
|
|
--font-mono: "JetBrains Mono", ui-monospace, monospace;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Baseline: full site build (brand, pages, DB, agent-editable)
Establishes the deploy baseline on main so the admin agent's publish/rollback
has a clean starting point. Everything built to date: sumi-e brand system,
homepage, projects (DB-driven case studies), resume, about, services +
website-design detail, contact form + DB, changelog, favicon + share card.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoFYZY9gkGPNDqZ7NuEa9a
2026-07-23 20:21:28 +00:00
|
|
|
|
/* ============================================================
|
|
|
|
|
|
Carlos Arias — sumi-e brand palette
|
|
|
|
|
|
Ink on paper, vermillion as seal.
|
|
|
|
|
|
|
|
|
|
|
|
Written as hex so these values are eyeball-verifiable against
|
|
|
|
|
|
brand/BRAND.md, which is the source of truth. Do not "improve"
|
|
|
|
|
|
a colour here — change it in the brand guide first.
|
|
|
|
|
|
|
|
|
|
|
|
Two choices worth preserving:
|
|
|
|
|
|
· The ground is never pure white. Washi-toned.
|
|
|
|
|
|
· The near-black is blue-black, not neutral grey — real sumi
|
|
|
|
|
|
ink has a cool cast, and the whole ramp inherits it.
|
|
|
|
|
|
============================================================ */
|
2026-07-04 22:53:10 +00:00
|
|
|
|
:root {
|
Baseline: full site build (brand, pages, DB, agent-editable)
Establishes the deploy baseline on main so the admin agent's publish/rollback
has a clean starting point. Everything built to date: sumi-e brand system,
homepage, projects (DB-driven case studies), resume, about, services +
website-design detail, contact form + DB, changelog, favicon + share card.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoFYZY9gkGPNDqZ7NuEa9a
2026-07-23 20:21:28 +00:00
|
|
|
|
--radius: 2px; /* near-square, not rounded */
|
|
|
|
|
|
|
|
|
|
|
|
--background: #f6f5f2; /* washi paper */
|
|
|
|
|
|
--foreground: #14151a; /* sumi ink */
|
|
|
|
|
|
--card: #f6f5f2;
|
|
|
|
|
|
--card-foreground: #14151a;
|
|
|
|
|
|
--popover: #efeeea;
|
|
|
|
|
|
--popover-foreground: #14151a;
|
|
|
|
|
|
|
|
|
|
|
|
/* Primary is INK, not red. The seal is never a button ground. */
|
|
|
|
|
|
--primary: #14151a;
|
|
|
|
|
|
--primary-foreground: #f6f5f2;
|
|
|
|
|
|
|
|
|
|
|
|
--secondary: #efeeea;
|
|
|
|
|
|
--secondary-foreground: #3d3f47;
|
|
|
|
|
|
--muted: #efeeea;
|
|
|
|
|
|
--muted-foreground: #6e7078;
|
|
|
|
|
|
|
|
|
|
|
|
/* Shuniku 朱肉 — seal ink. Target 1–2% coverage per screen. */
|
|
|
|
|
|
--accent: #d0342c;
|
|
|
|
|
|
--accent-foreground: #ffffff;
|
|
|
|
|
|
|
|
|
|
|
|
--destructive: #9b1c17; /* deeper than the seal, so
|
|
|
|
|
|
errors never read as brand */
|
|
|
|
|
|
--destructive-foreground: #ffffff;
|
|
|
|
|
|
|
|
|
|
|
|
--border: #dfddd7;
|
|
|
|
|
|
--input: #dfddd7;
|
|
|
|
|
|
--ring: #d0342c;
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- brand extras the base theme has no slot for ---- */
|
|
|
|
|
|
--ca-ink-2: #3d3f47;
|
|
|
|
|
|
--ca-ink-3: #6e7078;
|
|
|
|
|
|
--ca-ink-4: #9a9ca3;
|
|
|
|
|
|
--ca-seal: #d0342c;
|
|
|
|
|
|
|
|
|
|
|
|
/* Motion. Wabi-sabi motion is slow and never demands attention.
|
|
|
|
|
|
Nothing bounces, nothing pulses, nothing loops in the viewport.
|
|
|
|
|
|
If an effect asks to be watched, it belongs to another brand. */
|
|
|
|
|
|
--ca-dur-quick: 180ms;
|
|
|
|
|
|
--ca-dur-base: 280ms;
|
|
|
|
|
|
--ca-dur-slow: 620ms;
|
|
|
|
|
|
--ca-dur-ink: 1500ms;
|
|
|
|
|
|
--ca-ease: cubic-bezier(0.33, 0.1, 0.25, 1);
|
|
|
|
|
|
--ca-ease-press: cubic-bezier(0.2, 0.9, 0.3, 1);
|
|
|
|
|
|
--ca-reveal-y: 12px;
|
|
|
|
|
|
--ca-stagger: 70ms;
|
2026-07-04 22:53:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.dark {
|
Baseline: full site build (brand, pages, DB, agent-editable)
Establishes the deploy baseline on main so the admin agent's publish/rollback
has a clean starting point. Everything built to date: sumi-e brand system,
homepage, projects (DB-driven case studies), resume, about, services +
website-design detail, contact form + DB, changelog, favicon + share card.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoFYZY9gkGPNDqZ7NuEa9a
2026-07-23 20:21:28 +00:00
|
|
|
|
--background: #121317;
|
|
|
|
|
|
--foreground: #edece8;
|
|
|
|
|
|
--card: #121317;
|
|
|
|
|
|
--card-foreground: #edece8;
|
|
|
|
|
|
--popover: #191a1f;
|
|
|
|
|
|
--popover-foreground: #edece8;
|
|
|
|
|
|
|
|
|
|
|
|
--primary: #edece8;
|
|
|
|
|
|
--primary-foreground: #121317;
|
|
|
|
|
|
|
|
|
|
|
|
--secondary: #191a1f;
|
|
|
|
|
|
--secondary-foreground: #c2c1bd;
|
|
|
|
|
|
--muted: #191a1f;
|
|
|
|
|
|
--muted-foreground: #8b8d95;
|
|
|
|
|
|
|
|
|
|
|
|
/* Lifted on dark — #d0342c muddies against near-black.
|
|
|
|
|
|
Not a naive inversion. */
|
|
|
|
|
|
--accent: #e8564a;
|
|
|
|
|
|
--accent-foreground: #121317;
|
|
|
|
|
|
|
|
|
|
|
|
--destructive: #e8564a;
|
|
|
|
|
|
--destructive-foreground: #121317;
|
|
|
|
|
|
|
|
|
|
|
|
--border: #2a2c33;
|
|
|
|
|
|
--input: #2a2c33;
|
|
|
|
|
|
--ring: #e8564a;
|
|
|
|
|
|
|
|
|
|
|
|
--ca-ink-2: #c2c1bd;
|
|
|
|
|
|
--ca-ink-3: #8b8d95;
|
|
|
|
|
|
--ca-ink-4: #5e606a;
|
|
|
|
|
|
--ca-seal: #e8564a;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Ink-surface values exposed globally, so anything that has to sit on a dark
|
|
|
|
|
|
ground (the header while the mobile menu is open, an overlay, a badge) can
|
|
|
|
|
|
reach them without hardcoding a hex or relying on being nested inside
|
|
|
|
|
|
.ca-ink-surface. Mirrors brand/tokens.css — keep the two in step.
|
|
|
|
|
|
|
|
|
|
|
|
Note: an undefined var() invalidates the whole declaration silently, so a
|
|
|
|
|
|
missing name here shows up as "the colour just didn't apply", not an error. */
|
|
|
|
|
|
:root {
|
|
|
|
|
|
--ca-ink-surface-bg: #121317;
|
|
|
|
|
|
--ca-ink-surface-bg-2: #191a1f;
|
|
|
|
|
|
--ca-ink-surface-rule: #2a2c33;
|
|
|
|
|
|
--ca-ink-surface-fg: #edece8;
|
|
|
|
|
|
--ca-ink-surface-fg-2: #c2c1bd;
|
|
|
|
|
|
--ca-ink-surface-fg-3: #8b8d95;
|
|
|
|
|
|
--ca-ink-surface-fg-4: #5e606a;
|
|
|
|
|
|
--ca-ink-surface-seal: #e8564a;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- Ink surface ----
|
|
|
|
|
|
A section that stays ink-ground in BOTH themes, for committed dark
|
|
|
|
|
|
moments where the content itself demands darkness. Remaps the
|
|
|
|
|
|
semantic tokens locally, so anything nested inside needs no changes.
|
|
|
|
|
|
Use sparingly — one per page at most. */
|
|
|
|
|
|
/* IMPORTANT: this overrides the --color-* layer, NOT --background/--foreground.
|
|
|
|
|
|
The theme declares `--color-background: var(--background)` on :root, and a
|
|
|
|
|
|
custom property is substituted where it is DECLARED — so --color-background
|
|
|
|
|
|
resolved to paper at :root and only inherits from there. Redefining
|
|
|
|
|
|
--background on a descendant cannot retroactively change it, and the section
|
|
|
|
|
|
silently renders paper-on-paper.
|
|
|
|
|
|
|
|
|
|
|
|
(.dark works only because it sits on <html>, the same element as :root, so
|
|
|
|
|
|
the declaration re-resolves there. A nested scope has no such luck.)
|
|
|
|
|
|
|
|
|
|
|
|
Every utility — bg-background, text-muted-foreground, border-border — reads
|
|
|
|
|
|
the --color-* names, so overriding those is both correct and sufficient. */
|
|
|
|
|
|
.ca-ink-surface {
|
|
|
|
|
|
/* BOTH names are required, and for different consumers:
|
|
|
|
|
|
--foreground ← what Tailwind utilities compile to.
|
|
|
|
|
|
`@theme inline` emits `.text-foreground {
|
|
|
|
|
|
color: var(--foreground) }`, referencing the
|
|
|
|
|
|
underlying variable, not the alias.
|
|
|
|
|
|
--color-foreground ← what hand-written CSS in this file uses, and
|
|
|
|
|
|
what cannot be reached by overriding the raw
|
|
|
|
|
|
name alone (it was already substituted at :root).
|
|
|
|
|
|
Set one and half the section is wrong in a way that only shows up as
|
|
|
|
|
|
dark-on-dark text. Keep them in lockstep. */
|
|
|
|
|
|
--background: #121317;
|
|
|
|
|
|
--color-background: #121317;
|
|
|
|
|
|
--foreground: #edece8;
|
|
|
|
|
|
--color-foreground: #edece8;
|
|
|
|
|
|
--card: #121317;
|
|
|
|
|
|
--color-card: #121317;
|
|
|
|
|
|
--card-foreground: #edece8;
|
|
|
|
|
|
--color-card-foreground: #edece8;
|
|
|
|
|
|
--popover: #191a1f;
|
|
|
|
|
|
--color-popover: #191a1f;
|
|
|
|
|
|
--popover-foreground: #edece8;
|
|
|
|
|
|
--color-popover-foreground: #edece8;
|
|
|
|
|
|
--primary: #edece8;
|
|
|
|
|
|
--color-primary: #edece8;
|
|
|
|
|
|
--primary-foreground: #121317;
|
|
|
|
|
|
--color-primary-foreground: #121317;
|
|
|
|
|
|
--secondary: #191a1f;
|
|
|
|
|
|
--color-secondary: #191a1f;
|
|
|
|
|
|
--secondary-foreground: #c2c1bd;
|
|
|
|
|
|
--color-secondary-foreground: #c2c1bd;
|
|
|
|
|
|
--muted: #191a1f;
|
|
|
|
|
|
--color-muted: #191a1f;
|
|
|
|
|
|
--muted-foreground: #8b8d95;
|
|
|
|
|
|
--color-muted-foreground: #8b8d95;
|
|
|
|
|
|
--accent: #e8564a;
|
|
|
|
|
|
--color-accent: #e8564a;
|
|
|
|
|
|
--accent-foreground: #121317;
|
|
|
|
|
|
--color-accent-foreground: #121317;
|
|
|
|
|
|
--border: #2a2c33;
|
|
|
|
|
|
--color-border: #2a2c33;
|
|
|
|
|
|
--input: #2a2c33;
|
|
|
|
|
|
--color-input: #2a2c33;
|
|
|
|
|
|
--ring: #e8564a;
|
|
|
|
|
|
--color-ring: #e8564a;
|
|
|
|
|
|
|
|
|
|
|
|
--ca-ink-2: #c2c1bd;
|
|
|
|
|
|
--ca-ink-3: #8b8d95;
|
|
|
|
|
|
--ca-ink-4: #5e606a;
|
|
|
|
|
|
--ca-seal: #e8564a;
|
|
|
|
|
|
|
|
|
|
|
|
background-color: var(--color-background);
|
|
|
|
|
|
color: var(--color-foreground);
|
2026-07-04 22:53:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@layer base {
|
|
|
|
|
|
* {
|
|
|
|
|
|
border-color: var(--color-border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
html {
|
|
|
|
|
|
scroll-behavior: smooth;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
body {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
background-color: var(--color-background);
|
|
|
|
|
|
color: var(--color-foreground);
|
|
|
|
|
|
font-family: var(--font-sans);
|
|
|
|
|
|
font-feature-settings: "ss01", "cv11";
|
|
|
|
|
|
-webkit-font-smoothing: antialiased;
|
|
|
|
|
|
text-rendering: optimizeLegibility;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
::selection {
|
|
|
|
|
|
background-color: color-mix(in oklab, var(--color-primary) 25%, transparent);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:where(a, button, input, textarea, select, summary):focus-visible {
|
|
|
|
|
|
outline: 2px solid var(--color-ring) !important;
|
|
|
|
|
|
outline-offset: 3px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
|
|
|
|
html {
|
|
|
|
|
|
scroll-behavior: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
*,
|
|
|
|
|
|
*::before,
|
|
|
|
|
|
*::after {
|
|
|
|
|
|
animation-duration: 0.01ms !important;
|
|
|
|
|
|
animation-iteration-count: 1 !important;
|
|
|
|
|
|
scroll-behavior: auto !important;
|
|
|
|
|
|
transition-duration: 0.01ms !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@layer components {
|
Baseline: full site build (brand, pages, DB, agent-editable)
Establishes the deploy baseline on main so the admin agent's publish/rollback
has a clean starting point. Everything built to date: sumi-e brand system,
homepage, projects (DB-driven case studies), resume, about, services +
website-design detail, contact form + DB, changelog, favicon + share card.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoFYZY9gkGPNDqZ7NuEa9a
2026-07-23 20:21:28 +00:00
|
|
|
|
/* ============================================================
|
|
|
|
|
|
Brand primitives — defined ONCE, used everywhere.
|
|
|
|
|
|
|
|
|
|
|
|
Anything reused across more than one page belongs here, not in a
|
|
|
|
|
|
page's <style> block. If you find yourself pasting a .ca-* rule
|
|
|
|
|
|
into a second page, it was always meant to live here.
|
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
/* Section band. Divided by a hairline whose break is filled with
|
|
|
|
|
|
vermillion — kintsugi. Wabi-sabi's actual philosophy is that the
|
|
|
|
|
|
repair is the beautiful part, not that the crack is hidden.
|
|
|
|
|
|
Offsets vary so no two seams sit in the same place. */
|
|
|
|
|
|
.ca-band {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
border-top: 1px solid var(--color-border);
|
|
|
|
|
|
padding-block: clamp(4rem, 8vw, 6.5rem);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-band::before {
|
|
|
|
|
|
content: "";
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: -1px;
|
|
|
|
|
|
left: 12%;
|
|
|
|
|
|
height: 1px;
|
|
|
|
|
|
width: 28px;
|
|
|
|
|
|
background: var(--ca-seal);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-band:nth-of-type(even)::before {
|
|
|
|
|
|
left: 63%;
|
|
|
|
|
|
width: 18px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-band:nth-of-type(3n)::before {
|
|
|
|
|
|
left: 34%;
|
|
|
|
|
|
width: 40px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Index row. Work and writing are typographic indexes, not cards.
|
|
|
|
|
|
The seal rule becomes an interaction: exactly one red element on
|
|
|
|
|
|
screen at any moment, and only while pointed at. */
|
|
|
|
|
|
.ca-entry {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: 1.5rem minmax(0, 1fr) auto;
|
|
|
|
|
|
align-items: baseline;
|
|
|
|
|
|
gap: 0 1.5rem;
|
|
|
|
|
|
padding-block: 1.6rem;
|
|
|
|
|
|
border-bottom: 1px solid var(--color-border);
|
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
|
transition: border-color var(--ca-dur-base) var(--ca-ease);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-entry:hover,
|
|
|
|
|
|
.ca-entry:focus-visible {
|
|
|
|
|
|
border-bottom-color: var(--ca-seal);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-tick {
|
|
|
|
|
|
color: var(--ca-seal);
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transform: translateX(-0.4rem);
|
|
|
|
|
|
transition:
|
|
|
|
|
|
opacity var(--ca-dur-base) var(--ca-ease),
|
|
|
|
|
|
transform var(--ca-dur-base) var(--ca-ease);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-entry:hover .ca-tick,
|
|
|
|
|
|
.ca-entry:focus-visible .ca-tick {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
transform: translateX(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Theme-transition CSS is injected at runtime by the ThemeToggle React
|
|
|
|
|
|
component (src/components/ui/be-ui-theme-toggle.tsx), scoped to
|
|
|
|
|
|
html[data-beui-vt]. Do not add unconditional ::view-transition rules
|
|
|
|
|
|
here — they apply to every transition and fight the component's. */
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- full-page mobile menu ----
|
|
|
|
|
|
The menu deliberately does NOT reach into the header. It used to lift the
|
|
|
|
|
|
header above the portal and strip its ground, which meant its correctness
|
|
|
|
|
|
depended on which header variant the page rendered — and the homepage has
|
|
|
|
|
|
its own [data-home-header] rules carrying !important that outrank anything
|
|
|
|
|
|
written here. Same component, two behaviours, every fix needed twice.
|
|
|
|
|
|
|
|
|
|
|
|
Instead the panel carries its own close control inside the portal, so it
|
|
|
|
|
|
behaves identically on every page by construction. */
|
|
|
|
|
|
.ca-menu-seal {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
width: 2rem;
|
|
|
|
|
|
height: 2rem;
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
background: var(--ca-seal);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
font-family: var(--font-serif);
|
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-menu-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: baseline;
|
|
|
|
|
|
gap: 1rem;
|
|
|
|
|
|
padding-block: 0.7rem;
|
|
|
|
|
|
font-family: var(--font-serif);
|
|
|
|
|
|
font-size: clamp(2.1rem, 11vw, 3.2rem);
|
|
|
|
|
|
line-height: 1.1;
|
|
|
|
|
|
letter-spacing: -0.02em;
|
|
|
|
|
|
color: var(--color-foreground);
|
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-menu-index {
|
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
letter-spacing: 0.12em;
|
|
|
|
|
|
color: var(--ca-ink-4);
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-menu-mail {
|
|
|
|
|
|
font-size: 1.05rem;
|
|
|
|
|
|
color: var(--color-foreground);
|
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
|
border-bottom: 1px solid var(--ca-ink-4);
|
|
|
|
|
|
padding-bottom: 3px;
|
|
|
|
|
|
align-self: flex-start;
|
|
|
|
|
|
transition: border-color var(--ca-dur-base) var(--ca-ease);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-menu-mail:hover {
|
|
|
|
|
|
border-bottom-color: var(--color-foreground);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- hero portrait ----
|
|
|
|
|
|
Masked with a radial gradient so the frame dissolves into the surrounding
|
|
|
|
|
|
ink rather than ending at a hard edge. That is the whole reason the hero
|
|
|
|
|
|
is a dark surface and not a dark rectangle on paper — there is no seam to
|
|
|
|
|
|
hide if the photograph never visibly stops. */
|
|
|
|
|
|
.ca-hero-portrait {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
max-width: 22rem;
|
|
|
|
|
|
justify-self: end;
|
|
|
|
|
|
aspect-ratio: 4 / 5;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
-webkit-mask: radial-gradient(125% 100% at 50% 42%, #000 48%, transparent 94%);
|
|
|
|
|
|
mask: radial-gradient(125% 100% at 50% 42%, #000 48%, transparent 94%);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-hero-portrait img {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
|
object-position: 50% 22%;
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
filter: contrast(1.04) saturate(0.92);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Film grain, so the photograph shares the paper stock used everywhere else. */
|
|
|
|
|
|
.ca-hero-portrait::after {
|
|
|
|
|
|
content: "";
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
inset: 0;
|
|
|
|
|
|
z-index: 1;
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
opacity: 0.1;
|
|
|
|
|
|
mix-blend-mode: overlay;
|
|
|
|
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* The artist's seal, pressed on the work. */
|
|
|
|
|
|
.ca-hero-seal {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
z-index: 2;
|
|
|
|
|
|
right: 1rem;
|
|
|
|
|
|
bottom: 1rem;
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
width: 1.7rem;
|
|
|
|
|
|
height: 1.7rem;
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
background: var(--ca-seal);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
font-family: var(--font-serif);
|
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 63.99rem) {
|
|
|
|
|
|
.ca-hero-portrait {
|
|
|
|
|
|
justify-self: start;
|
|
|
|
|
|
max-width: 15rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- portrait ----
|
|
|
|
|
|
The frame reserves its space whether or not a photo exists, so dropping
|
|
|
|
|
|
the real image in later cannot reflow the page. 4:5 because a portrait
|
|
|
|
|
|
crop wants more height than a square and less than a full body shot. */
|
|
|
|
|
|
.ca-portrait {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
aspect-ratio: 4 / 5;
|
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
|
object-position: 50% 20%;
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
/* Left in colour deliberately. Earlier drafts graded portraits to
|
|
|
|
|
|
greyscale so a snapshot would sit inside the palette — but these were
|
|
|
|
|
|
shot for the page, and the warm practical lights behind the subject are
|
|
|
|
|
|
most of what makes the frame work. Greyscale would throw that away.
|
|
|
|
|
|
A light contrast lift is all it needs. */
|
|
|
|
|
|
filter: contrast(1.04) saturate(0.94);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Two portraits ship and CSS picks one. The theme is class-based, so a
|
|
|
|
|
|
<picture media="(prefers-color-scheme: dark)"> would follow the OS and
|
|
|
|
|
|
ignore the site's own toggle. */
|
|
|
|
|
|
.ca-portrait-dark {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.dark .ca-portrait-light {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.dark .ca-portrait-dark {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-portrait-empty {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
gap: 0.6rem;
|
|
|
|
|
|
background: var(--color-secondary);
|
|
|
|
|
|
border: 1px dashed var(--color-border);
|
|
|
|
|
|
color: var(--ca-ink-4);
|
|
|
|
|
|
filter: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-portrait-empty span {
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
|
letter-spacing: 0.18em;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- about: Instagram reel rail ----
|
|
|
|
|
|
A 9:16 frame that sticks while the essay scrolls past it, so the media
|
|
|
|
|
|
stays in view alongside the reading. Ink-surface treatment because a Reel
|
|
|
|
|
|
is video and reads better on a dark ground than washi. */
|
|
|
|
|
|
.ca-reel-col {
|
|
|
|
|
|
align-self: start;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (min-width: 64rem) {
|
|
|
|
|
|
.ca-reel-col {
|
|
|
|
|
|
position: sticky;
|
|
|
|
|
|
top: 5.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-reel {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
max-width: 20rem;
|
|
|
|
|
|
aspect-ratio: 9 / 16;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
background: var(--ca-ink-surface-bg);
|
|
|
|
|
|
border: 1px solid var(--ca-ink-surface-rule);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-reel-inner {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
inset: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
gap: 0.75rem;
|
|
|
|
|
|
color: var(--ca-ink-surface-fg-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-reel-glyph {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
width: 3rem;
|
|
|
|
|
|
height: 3rem;
|
|
|
|
|
|
border: 1px solid var(--ca-ink-surface-fg-4);
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
|
padding-left: 0.2rem; /* optically centre the play triangle */
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-reel-label {
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.68rem;
|
|
|
|
|
|
letter-spacing: 0.18em;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
color: var(--ca-ink-surface-fg-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-reel-handle {
|
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
|
color: var(--ca-ink-surface-fg-4);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* wider measure for the about essay than default prose */
|
|
|
|
|
|
.ca-about-prose {
|
|
|
|
|
|
max-width: 62ch;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- about: timeline ---- */
|
|
|
|
|
|
.ca-timeline {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
list-style: none;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-timeline li {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: 5.5rem minmax(0, 1fr);
|
|
|
|
|
|
gap: 1.5rem;
|
|
|
|
|
|
align-items: baseline;
|
|
|
|
|
|
padding: 1rem 0;
|
|
|
|
|
|
border-top: 1px solid var(--color-border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-timeline li:last-child {
|
|
|
|
|
|
border-bottom: 1px solid var(--color-border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-timeline-year {
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
|
letter-spacing: 0.06em;
|
|
|
|
|
|
color: var(--ca-seal);
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-timeline-note {
|
|
|
|
|
|
font-size: 0.95rem;
|
|
|
|
|
|
color: var(--ca-ink-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- portfolio (browser-frame mockups) ---- */
|
|
|
|
|
|
.ca-folio {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: minmax(0, 1fr);
|
|
|
|
|
|
gap: 2.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (min-width: 52rem) {
|
|
|
|
|
|
.ca-folio {
|
|
|
|
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
|
|
|
gap: 2rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-folio-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
|
color: inherit;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-folio-frame {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
border: 1px solid var(--color-border);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
background: var(--color-secondary);
|
|
|
|
|
|
transition:
|
|
|
|
|
|
border-color var(--ca-dur-base) var(--ca-ease),
|
|
|
|
|
|
transform var(--ca-dur-base) var(--ca-ease);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-folio-item:hover .ca-folio-frame,
|
|
|
|
|
|
.ca-folio-item:focus-visible .ca-folio-frame {
|
|
|
|
|
|
border-color: var(--ca-ink-3);
|
|
|
|
|
|
transform: translateY(-3px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* a small browser chrome bar above the screenshot */
|
|
|
|
|
|
.ca-folio-chrome {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 0.4rem;
|
|
|
|
|
|
padding: 0.55rem 0.7rem;
|
|
|
|
|
|
border-bottom: 1px solid var(--color-border);
|
|
|
|
|
|
background: var(--color-background);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-folio-chrome span {
|
|
|
|
|
|
width: 0.5rem;
|
|
|
|
|
|
height: 0.5rem;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
background: var(--ca-ink-4);
|
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-folio-frame img {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
aspect-ratio: 1440 / 1024;
|
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
|
object-position: top center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-folio-empty {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
|
aspect-ratio: 1440 / 1024;
|
|
|
|
|
|
border: 1px dashed var(--color-border);
|
|
|
|
|
|
margin: 0.75rem;
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
color: var(--ca-ink-4);
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
|
letter-spacing: 0.12em;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-folio-glyph {
|
|
|
|
|
|
font-family: var(--font-serif);
|
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-folio-meta {
|
|
|
|
|
|
padding-top: 1rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-folio-meta h3 {
|
|
|
|
|
|
font-family: var(--font-serif);
|
|
|
|
|
|
font-size: 1.25rem;
|
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
|
letter-spacing: -0.01em;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-folio-practice {
|
|
|
|
|
|
margin: 0.3rem 0 0;
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.68rem;
|
|
|
|
|
|
letter-spacing: 0.06em;
|
|
|
|
|
|
color: var(--ca-ink-4);
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-folio-note {
|
|
|
|
|
|
margin: 0.7rem 0 0;
|
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
color: var(--ca-ink-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-folio-link {
|
|
|
|
|
|
display: inline-block;
|
|
|
|
|
|
margin-top: 0.7rem;
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.68rem;
|
|
|
|
|
|
letter-spacing: 0.06em;
|
|
|
|
|
|
color: var(--ca-seal);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-folio-item.is-empty {
|
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- feature grid (what's included) ---- */
|
|
|
|
|
|
.ca-feature-grid {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
|
|
|
|
|
|
gap: 0 3.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-feature {
|
|
|
|
|
|
padding: 1.5rem 0;
|
|
|
|
|
|
border-top: 1px solid var(--color-border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-feature h3 {
|
|
|
|
|
|
font-family: var(--font-serif);
|
|
|
|
|
|
font-size: 1.35rem;
|
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
|
letter-spacing: -0.01em;
|
|
|
|
|
|
margin: 0 0 0.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-feature p {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
|
line-height: 1.55;
|
|
|
|
|
|
color: var(--ca-ink-3);
|
|
|
|
|
|
max-width: 42ch;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- numbered process ---- */
|
|
|
|
|
|
.ca-process {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
list-style: none;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-process li {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: 3.5rem minmax(0, 1fr);
|
|
|
|
|
|
gap: 1.5rem;
|
|
|
|
|
|
padding: 1.5rem 0;
|
|
|
|
|
|
border-top: 1px solid var(--color-border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-process li:last-child {
|
|
|
|
|
|
border-bottom: 1px solid var(--color-border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-process-step {
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
|
color: var(--ca-seal);
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
padding-top: 0.3rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-process h3 {
|
|
|
|
|
|
font-family: var(--font-serif);
|
|
|
|
|
|
font-size: 1.35rem;
|
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
|
margin: 0 0 0.35rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-process p {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
|
line-height: 1.55;
|
|
|
|
|
|
color: var(--ca-ink-3);
|
|
|
|
|
|
max-width: 54ch;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- services: verticals ---- */
|
|
|
|
|
|
.ca-verticals {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
|
|
|
|
|
|
gap: 0;
|
|
|
|
|
|
border-top: 1px solid var(--color-border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-vertical {
|
|
|
|
|
|
padding: 1.5rem 1.75rem 1.5rem 0;
|
|
|
|
|
|
border-bottom: 1px solid var(--color-border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-vertical h3 {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 0.6rem;
|
|
|
|
|
|
font-family: var(--font-serif);
|
|
|
|
|
|
font-size: var(--ca-s5);
|
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
|
letter-spacing: -0.01em;
|
|
|
|
|
|
margin: 0 0 0.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* The primary vertical gets the seal — the one place the eye should land. */
|
|
|
|
|
|
.ca-vertical.is-primary h3 {
|
|
|
|
|
|
color: var(--ca-seal);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-vertical-tag {
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.55rem;
|
|
|
|
|
|
letter-spacing: 0.12em;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
background: var(--ca-seal);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
padding: 0.15rem 0.4rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-vertical p {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
|
line-height: 1.55;
|
|
|
|
|
|
color: var(--ca-ink-3);
|
|
|
|
|
|
max-width: 40ch;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- changelog timeline ----
|
|
|
|
|
|
A single vermillion hairline runs down the left; each entry hangs a small
|
|
|
|
|
|
node off it. The node is the only red on the page per entry, which keeps
|
|
|
|
|
|
the timeline reading as a seam of marks rather than a rainbow of statuses.
|
|
|
|
|
|
Type is carried by a mono label, not by colour — except "bug found", which
|
|
|
|
|
|
takes the seal, because a bug is the one thing that should catch the eye. */
|
|
|
|
|
|
.ca-log {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-log-group {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-log-date {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: baseline;
|
|
|
|
|
|
gap: 0.75rem;
|
|
|
|
|
|
margin: 2.5rem 0 1.25rem;
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.72rem;
|
|
|
|
|
|
letter-spacing: 0.14em;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
color: var(--ca-ink-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-log-group:first-child .ca-log-date {
|
|
|
|
|
|
margin-top: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-log-count {
|
|
|
|
|
|
font-size: 0.62rem;
|
|
|
|
|
|
color: var(--ca-ink-4);
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-log-list {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
list-style: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-log-entry {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
padding: 0 0 1.6rem 1.75rem;
|
|
|
|
|
|
/* the vertical line, drawn per-entry so it stops cleanly at the last one */
|
|
|
|
|
|
border-left: 1px solid var(--color-border);
|
|
|
|
|
|
margin-left: 0.3rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-log-entry:last-child {
|
|
|
|
|
|
border-left-color: transparent;
|
|
|
|
|
|
padding-bottom: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-log-node {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: -0.3rem;
|
|
|
|
|
|
top: 0.3rem;
|
|
|
|
|
|
width: 0.55rem;
|
|
|
|
|
|
height: 0.55rem;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
background: var(--color-background);
|
|
|
|
|
|
border: 1px solid var(--ca-ink-4);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* a bug is the one status that earns the seal */
|
|
|
|
|
|
.ca-log-node[data-type="bug"] {
|
|
|
|
|
|
background: var(--ca-seal);
|
|
|
|
|
|
border-color: var(--ca-seal);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-log-node[data-type="fixed"],
|
|
|
|
|
|
.ca-log-node[data-type="added"] {
|
|
|
|
|
|
background: var(--ca-ink-3);
|
|
|
|
|
|
border-color: var(--ca-ink-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-log-head {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: baseline;
|
|
|
|
|
|
gap: 0.75rem;
|
|
|
|
|
|
margin-bottom: 0.35rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-log-type {
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.62rem;
|
|
|
|
|
|
letter-spacing: 0.12em;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
color: var(--ca-ink-4);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-log-type[data-type="bug"] {
|
|
|
|
|
|
color: var(--ca-seal);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-log-time {
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.62rem;
|
|
|
|
|
|
color: var(--ca-ink-4);
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-log-summary {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
font-size: 0.95rem;
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
color: var(--color-foreground);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-log-detail {
|
|
|
|
|
|
margin: 0.3rem 0 0;
|
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
|
line-height: 1.55;
|
|
|
|
|
|
color: var(--ca-ink-3);
|
|
|
|
|
|
max-width: 60ch;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- contact form ----
|
|
|
|
|
|
Underline fields, not boxes — a hairline that takes the seal on focus,
|
|
|
|
|
|
consistent with the rest of the site's ink-on-paper restraint. */
|
|
|
|
|
|
.ca-form {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 1.75rem;
|
|
|
|
|
|
max-width: 40rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-field {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-field label {
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.68rem;
|
|
|
|
|
|
letter-spacing: 0.14em;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
color: var(--ca-ink-4);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-field input,
|
|
|
|
|
|
.ca-field textarea,
|
|
|
|
|
|
.ca-select select {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
border: 0;
|
|
|
|
|
|
border-bottom: 1px solid var(--color-border);
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
padding: 0.6rem 0;
|
|
|
|
|
|
font-family: var(--font-sans);
|
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
|
color: var(--color-foreground);
|
|
|
|
|
|
border-radius: 0;
|
|
|
|
|
|
transition: border-color var(--ca-dur-base) var(--ca-ease);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-field textarea {
|
|
|
|
|
|
resize: vertical;
|
|
|
|
|
|
min-height: 8rem;
|
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-field input:focus,
|
|
|
|
|
|
.ca-field textarea:focus,
|
|
|
|
|
|
.ca-select select:focus {
|
|
|
|
|
|
outline: none;
|
|
|
|
|
|
border-bottom-color: var(--ca-seal);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-field input::placeholder,
|
|
|
|
|
|
.ca-field textarea::placeholder {
|
|
|
|
|
|
color: var(--ca-ink-4);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Native select restyled to match — caret is the ink arrow used elsewhere. */
|
|
|
|
|
|
.ca-select {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-select select {
|
|
|
|
|
|
appearance: none;
|
|
|
|
|
|
-webkit-appearance: none;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
padding-right: 2rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-select-caret {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
right: 0.25rem;
|
|
|
|
|
|
top: 50%;
|
|
|
|
|
|
transform: translateY(-50%);
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
color: var(--ca-ink-4);
|
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-select select option {
|
|
|
|
|
|
color: #14151a; /* option lists inherit the OS control colour, not ours */
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-field-error {
|
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
|
color: var(--ca-seal);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-honeypot {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: -9999px;
|
|
|
|
|
|
width: 1px;
|
|
|
|
|
|
height: 1px;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-form-foot {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 1.5rem;
|
|
|
|
|
|
margin-top: 0.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-submit {
|
|
|
|
|
|
border: 1px solid var(--color-primary);
|
|
|
|
|
|
background: var(--color-primary);
|
|
|
|
|
|
color: var(--color-primary-foreground);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
padding: 0.8rem 1.6rem;
|
|
|
|
|
|
font-family: var(--font-sans);
|
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition:
|
|
|
|
|
|
background-color var(--ca-dur-base) var(--ca-ease),
|
|
|
|
|
|
color var(--ca-dur-base) var(--ca-ease),
|
|
|
|
|
|
opacity var(--ca-dur-base) var(--ca-ease);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-submit:hover:not(:disabled) {
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-submit:disabled {
|
|
|
|
|
|
opacity: 0.55;
|
|
|
|
|
|
cursor: default;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-form-status {
|
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
|
color: var(--ca-ink-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-form-status[data-state="ok"] {
|
|
|
|
|
|
color: var(--ca-seal);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-form-status[data-state="error"] {
|
|
|
|
|
|
color: var(--color-destructive);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- project detail ---- */
|
|
|
|
|
|
.ca-back {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
letter-spacing: 0.12em;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
color: var(--ca-ink-4);
|
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
|
transition: color var(--ca-dur-base) var(--ca-ease);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-back:hover {
|
|
|
|
|
|
color: var(--color-foreground);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-status {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 0.4rem;
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
padding: 0.25rem 0.6rem;
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
|
letter-spacing: 0.12em;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
border: 1px solid var(--color-border);
|
|
|
|
|
|
color: var(--ca-ink-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Only a shipped thing gets the seal. "In development" is honest, not a badge. */
|
|
|
|
|
|
.ca-status.is-live {
|
|
|
|
|
|
border-color: var(--ca-seal);
|
|
|
|
|
|
color: var(--ca-seal);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-chip {
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
border: 1px solid var(--color-border);
|
|
|
|
|
|
padding: 0.25rem 0.6rem;
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
|
letter-spacing: 0.1em;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
color: var(--ca-ink-4);
|
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
|
transition:
|
|
|
|
|
|
color var(--ca-dur-base) var(--ca-ease),
|
|
|
|
|
|
border-color var(--ca-dur-base) var(--ca-ease);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-chip:hover {
|
|
|
|
|
|
color: var(--color-foreground);
|
|
|
|
|
|
border-color: var(--ca-ink-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-facts {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
|
|
|
|
|
|
gap: 0;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
border-top: 1px solid var(--color-border);
|
|
|
|
|
|
border-bottom: 1px solid var(--color-border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-facts > div {
|
|
|
|
|
|
padding: 1.1rem 1.5rem 1.1rem 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-facts dt {
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
|
letter-spacing: 0.16em;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
color: var(--ca-ink-4);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-facts dd {
|
|
|
|
|
|
margin: 0.4rem 0 0;
|
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
|
color: var(--color-foreground);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Numbers get the serif at display size — on a case study the result is the
|
|
|
|
|
|
headline, so it should be set like one. */
|
|
|
|
|
|
.ca-metrics {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
|
|
|
|
|
|
gap: 2.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-metric-value {
|
|
|
|
|
|
font-family: var(--font-serif);
|
|
|
|
|
|
font-size: clamp(2.2rem, 5vw, 3.4rem);
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
letter-spacing: -0.02em;
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-metric-label {
|
|
|
|
|
|
margin-top: 0.6rem;
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.68rem;
|
|
|
|
|
|
letter-spacing: 0.16em;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
color: var(--ca-ink-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-metric-note {
|
|
|
|
|
|
margin-top: 0.3rem;
|
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
|
color: var(--ca-ink-4);
|
|
|
|
|
|
max-width: 26ch;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- gallery ---- */
|
|
|
|
|
|
.ca-gallery {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
|
gap: 1rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (min-width: 48rem) {
|
|
|
|
|
|
.ca-gallery {
|
|
|
|
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-gallery-item {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
aspect-ratio: 4 / 3;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
border: 1px solid var(--color-border);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
background: var(--color-secondary);
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-gallery-item img {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
|
transition: transform var(--ca-dur-slow) var(--ca-ease);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-gallery-item:hover img,
|
|
|
|
|
|
.ca-gallery-item:focus-visible img {
|
|
|
|
|
|
transform: scale(1.03);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Empty placeholder tile — a dashed frame with its caption, so the gallery
|
|
|
|
|
|
reads as intentional structure rather than a broken image. */
|
|
|
|
|
|
.ca-gallery-item.is-empty {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
|
border-style: dashed;
|
|
|
|
|
|
color: var(--ca-ink-4);
|
|
|
|
|
|
cursor: default;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-gallery-glyph {
|
|
|
|
|
|
font-family: var(--font-serif);
|
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-gallery-cap {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
padding: 0.5rem 0.7rem;
|
|
|
|
|
|
background: linear-gradient(transparent, rgba(18, 19, 23, 0.72));
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
|
letter-spacing: 0.08em;
|
|
|
|
|
|
text-align: left;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-gallery-item.is-empty .ca-gallery-cap {
|
|
|
|
|
|
position: static;
|
|
|
|
|
|
background: none;
|
|
|
|
|
|
color: var(--ca-ink-4);
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- lightbox ---- */
|
|
|
|
|
|
.ca-lightbox {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
inset: 0;
|
|
|
|
|
|
z-index: 1000;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
padding: 2rem;
|
|
|
|
|
|
background: rgba(18, 19, 23, 0.92);
|
|
|
|
|
|
backdrop-filter: blur(4px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-lightbox-fig {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
max-width: 90vw;
|
|
|
|
|
|
max-height: 88vh;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 0.75rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-lightbox-fig img {
|
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
|
max-height: 82vh;
|
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-lightbox-fig figcaption {
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.72rem;
|
|
|
|
|
|
letter-spacing: 0.08em;
|
|
|
|
|
|
color: #c2c1bd;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-lightbox-close {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 1rem;
|
|
|
|
|
|
right: 1.25rem;
|
|
|
|
|
|
width: 2.75rem;
|
|
|
|
|
|
height: 2.75rem;
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
border: 0;
|
|
|
|
|
|
color: #edece8;
|
|
|
|
|
|
font-size: 2rem;
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- skills applied (grouped disciplines) ---- */
|
|
|
|
|
|
.ca-skills {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
|
|
|
|
|
|
gap: 2.5rem 3.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-skill-group h3 {
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.68rem;
|
|
|
|
|
|
letter-spacing: 0.16em;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
color: var(--ca-seal);
|
|
|
|
|
|
margin: 0 0 1rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-skill-group ul {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
list-style: none;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-skill-group li {
|
|
|
|
|
|
padding: 0.55rem 0;
|
|
|
|
|
|
border-top: 1px solid var(--color-border);
|
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
|
color: var(--ca-ink-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-skill-group li:last-child {
|
|
|
|
|
|
border-bottom: 1px solid var(--color-border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- case-study body + social rail ---- */
|
|
|
|
|
|
.ca-body-grid {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
gap: 2.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (min-width: 64rem) {
|
|
|
|
|
|
.ca-body-grid {
|
|
|
|
|
|
grid-template-columns: minmax(0, 16rem) minmax(0, 1fr);
|
|
|
|
|
|
gap: 4rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-body-rail {
|
|
|
|
|
|
align-self: start;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (min-width: 64rem) {
|
|
|
|
|
|
/* Sticks alongside the case study as it scrolls. */
|
|
|
|
|
|
.ca-body-rail {
|
|
|
|
|
|
position: sticky;
|
|
|
|
|
|
top: 5.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Mobile: the reel leads the case study (it sits below the gallery, above
|
|
|
|
|
|
the body text). DOM order is already rail-then-body, so no order swap is
|
|
|
|
|
|
needed — just give the reel breathing room and cap its height so a tall
|
|
|
|
|
|
9:16 frame does not dominate the screen. */
|
|
|
|
|
|
@media (max-width: 63.99rem) {
|
|
|
|
|
|
.ca-body-rail {
|
|
|
|
|
|
margin-bottom: 1.75rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Beat the base .ca-reel max-width (20rem) with the rail scope + !important
|
|
|
|
|
|
is unnecessary here since two class selectors already outrank one. */
|
|
|
|
|
|
.ca-body-rail .ca-reel {
|
|
|
|
|
|
max-width: 15rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* The prose column now defines its own measure via the grid, so it should
|
|
|
|
|
|
fill the track rather than cap at 68ch and leave the rail stranded. */
|
|
|
|
|
|
.ca-body-main {
|
|
|
|
|
|
max-width: 68ch;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- long-form body ---- */
|
|
|
|
|
|
.ca-prose {
|
|
|
|
|
|
max-width: 68ch;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-prose h2 {
|
|
|
|
|
|
font-family: var(--font-serif);
|
|
|
|
|
|
font-size: clamp(1.5rem, 3vw, 2rem);
|
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
|
letter-spacing: -0.01em;
|
|
|
|
|
|
margin: 3rem 0 1rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-prose h2:first-child {
|
|
|
|
|
|
margin-top: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-prose h3 {
|
|
|
|
|
|
font-family: var(--font-serif);
|
|
|
|
|
|
font-size: 1.3rem;
|
|
|
|
|
|
margin: 2.25rem 0 0.75rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-prose p {
|
|
|
|
|
|
margin: 0 0 1.2rem;
|
|
|
|
|
|
line-height: 1.7;
|
|
|
|
|
|
color: var(--ca-ink-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-prose ul,
|
|
|
|
|
|
.ca-prose ol {
|
|
|
|
|
|
margin: 0 0 1.4rem;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
list-style: none;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-prose li {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
padding-left: 1.1rem;
|
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
|
color: var(--ca-ink-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Same vermillion rule used as a marker in the CV — one list idiom, not two. */
|
|
|
|
|
|
.ca-prose li::before {
|
|
|
|
|
|
content: "";
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
top: 0.72em;
|
|
|
|
|
|
width: 0.5rem;
|
|
|
|
|
|
height: 1px;
|
|
|
|
|
|
background: var(--ca-seal);
|
|
|
|
|
|
opacity: 0.75;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-prose a {
|
|
|
|
|
|
color: var(--color-foreground);
|
|
|
|
|
|
border-bottom: 1px solid var(--ca-ink-4);
|
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-prose strong {
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: var(--color-foreground);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-prose blockquote {
|
|
|
|
|
|
margin: 2rem 0;
|
|
|
|
|
|
padding-left: 1.5rem;
|
|
|
|
|
|
border-left: 2px solid var(--ca-seal);
|
|
|
|
|
|
font-family: var(--font-serif);
|
|
|
|
|
|
font-size: 1.25rem;
|
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-prose code {
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.85em;
|
|
|
|
|
|
background: var(--color-secondary);
|
|
|
|
|
|
padding: 0.1em 0.35em;
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- filters ----
|
|
|
|
|
|
Chips, not buttons — each is a real href so a filtered view can be shared.
|
|
|
|
|
|
The active one takes the seal, which keeps the 1–2% rule intact: only ever
|
|
|
|
|
|
one filter is active, so only ever one red element. */
|
|
|
|
|
|
.ca-filters {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
|
padding-bottom: 1.25rem;
|
|
|
|
|
|
border-bottom: 1px solid var(--color-border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-filter {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: baseline;
|
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
|
padding: 0.4rem 0.75rem;
|
|
|
|
|
|
border: 1px solid var(--color-border);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.72rem;
|
|
|
|
|
|
letter-spacing: 0.08em;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
color: var(--ca-ink-3);
|
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
|
transition:
|
|
|
|
|
|
border-color var(--ca-dur-base) var(--ca-ease),
|
|
|
|
|
|
color var(--ca-dur-base) var(--ca-ease),
|
|
|
|
|
|
background-color var(--ca-dur-base) var(--ca-ease);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-filter:hover {
|
|
|
|
|
|
border-color: var(--ca-ink-3);
|
|
|
|
|
|
color: var(--color-foreground);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-filter[aria-current="true"] {
|
|
|
|
|
|
border-color: var(--ca-seal);
|
|
|
|
|
|
color: var(--ca-seal);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-filter-count {
|
|
|
|
|
|
font-size: 0.62rem;
|
|
|
|
|
|
color: var(--ca-ink-4);
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-filter[aria-current="true"] .ca-filter-count {
|
|
|
|
|
|
color: var(--ca-seal);
|
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-filter-empty {
|
|
|
|
|
|
margin-top: 2.5rem;
|
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
|
color: var(--ca-ink-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- filter transition ----
|
|
|
|
|
|
Scoped to [data-ca-filtering] so it cannot collide with the theme toggle,
|
|
|
|
|
|
which drives its own ::view-transition rules under html[data-beui-vt].
|
|
|
|
|
|
|
|
|
|
|
|
Rows that survive a filter glide to their new position; rows leaving fade
|
|
|
|
|
|
and settle downward, rows arriving rise into place. Nothing scales or
|
|
|
|
|
|
bounces — this is the same restraint as the rest of the motion here, just
|
|
|
|
|
|
applied to a list. */
|
|
|
|
|
|
html[data-ca-filtering]::view-transition-group(*) {
|
|
|
|
|
|
animation-duration: 420ms;
|
|
|
|
|
|
animation-timing-function: cubic-bezier(0.33, 0.1, 0.25, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
html[data-ca-filtering]::view-transition-old(*) {
|
|
|
|
|
|
animation:
|
|
|
|
|
|
ca-filter-out 260ms cubic-bezier(0.33, 0.1, 0.25, 1) both;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
html[data-ca-filtering]::view-transition-new(*) {
|
|
|
|
|
|
animation:
|
|
|
|
|
|
ca-filter-in 380ms cubic-bezier(0.33, 0.1, 0.25, 1) 80ms both;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@keyframes ca-filter-out {
|
|
|
|
|
|
to {
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transform: translateY(6px);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@keyframes ca-filter-in {
|
|
|
|
|
|
from {
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transform: translateY(10px);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* The root snapshot would otherwise cross-fade the entire page underneath
|
|
|
|
|
|
the row animations, which reads as a flicker. Let the named groups do the
|
|
|
|
|
|
work and leave the rest alone. */
|
|
|
|
|
|
html[data-ca-filtering]::view-transition-old(root),
|
|
|
|
|
|
html[data-ca-filtering]::view-transition-new(root) {
|
|
|
|
|
|
animation: none;
|
|
|
|
|
|
mix-blend-mode: normal;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
|
|
|
|
html[data-ca-filtering]::view-transition-group(*),
|
|
|
|
|
|
html[data-ca-filtering]::view-transition-old(*),
|
|
|
|
|
|
html[data-ca-filtering]::view-transition-new(*) {
|
|
|
|
|
|
animation: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- CV rows ----
|
|
|
|
|
|
Two columns on desktop: dates parked left, substance right. Stacks on
|
|
|
|
|
|
mobile so the date never squeezes the role into a two-word column. */
|
|
|
|
|
|
.ca-cv-row {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: minmax(0, 1fr);
|
|
|
|
|
|
gap: 0.75rem 2.5rem;
|
|
|
|
|
|
padding-block: 2.25rem;
|
|
|
|
|
|
border-bottom: 1px solid var(--color-border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (min-width: 48rem) {
|
|
|
|
|
|
.ca-cv-row {
|
|
|
|
|
|
grid-template-columns: 11rem minmax(0, 1fr);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-cv-when {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 0.6rem;
|
|
|
|
|
|
padding-top: 0.4rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-cv-now {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
background: var(--ca-seal);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
padding: 0.1rem 0.4rem;
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.62rem;
|
|
|
|
|
|
letter-spacing: 0.1em;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-cv-points {
|
|
|
|
|
|
margin: 1.1rem 0 0;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
list-style: none;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 0.45rem;
|
|
|
|
|
|
max-width: 64ch;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-cv-points li {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
padding-left: 1.1rem;
|
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
|
line-height: 1.55;
|
|
|
|
|
|
color: var(--ca-ink-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* A short vermillion rule as the marker — the same kintsugi seam idea at
|
|
|
|
|
|
list scale, rather than a bullet character. */
|
|
|
|
|
|
.ca-cv-points li::before {
|
|
|
|
|
|
content: "";
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
top: 0.68em;
|
|
|
|
|
|
width: 0.5rem;
|
|
|
|
|
|
height: 1px;
|
|
|
|
|
|
background: var(--ca-seal);
|
|
|
|
|
|
opacity: 0.75;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- navigation ----
|
|
|
|
|
|
The same sumi stroke used under headings, painted in left-to-right
|
|
|
|
|
|
on hover — the direction of writing, and the same gesture as the
|
|
|
|
|
|
signature. Reuses /brand/brush.svg rather than inventing a second
|
|
|
|
|
|
underline treatment.
|
|
|
|
|
|
|
|
|
|
|
|
Still obeys the seal rule: only one item can be hovered, so at most
|
|
|
|
|
|
one stroke plus the current-page stroke are ever on screen. */
|
|
|
|
|
|
.ca-nav-link {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
padding-bottom: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-nav-link::after {
|
|
|
|
|
|
content: "";
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: -0.1em;
|
|
|
|
|
|
right: -0.14em;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
height: 0.26em;
|
|
|
|
|
|
background-color: var(--ca-seal);
|
|
|
|
|
|
-webkit-mask: url("/brand/brush.svg") no-repeat left center / 100% 100%;
|
|
|
|
|
|
mask: url("/brand/brush.svg") no-repeat left center / 100% 100%;
|
|
|
|
|
|
clip-path: inset(0 100% 0 0);
|
|
|
|
|
|
transition: clip-path var(--ca-dur-slow) cubic-bezier(0.31, 0.06, 0.2, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-nav-link:hover::after,
|
|
|
|
|
|
.ca-nav-link:focus-visible::after,
|
|
|
|
|
|
.ca-nav-link[aria-current="page"]::after {
|
|
|
|
|
|
clip-path: inset(0 0 0 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Over the ink hero the seal needs the lifted red, same as the mark. */
|
|
|
|
|
|
[data-home-header]:not([data-scrolled="true"]):not([data-panel-open="true"])
|
|
|
|
|
|
.ca-nav-link::after {
|
|
|
|
|
|
background-color: var(--ca-ink-surface-seal, #e8564a);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
|
|
|
|
.ca-nav-link::after {
|
|
|
|
|
|
transition: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Understated link — an ink rule that darkens rather than a colour change. */
|
|
|
|
|
|
.ca-quiet {
|
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
|
border-bottom: 1px solid var(--ca-ink-4);
|
|
|
|
|
|
padding-bottom: 3px;
|
|
|
|
|
|
transition: border-color var(--ca-dur-base) var(--ca-ease);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-quiet:hover {
|
|
|
|
|
|
border-bottom-color: var(--color-foreground);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* A real sumi stroke: blunt where the brush lands, tapering as it
|
|
|
|
|
|
lifts, with kasure (dry-brush breaks) trailing the tail. Drawn as a
|
|
|
|
|
|
mask so the colour comes from the token and follows the theme.
|
|
|
|
|
|
Emphasis only — brushing every heading is the same as brushing none. */
|
|
|
|
|
|
.ca-brushed {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
display: inline-block;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-brushed::after {
|
|
|
|
|
|
content: "";
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: -0.12em;
|
|
|
|
|
|
right: -0.18em;
|
|
|
|
|
|
bottom: -0.26em;
|
|
|
|
|
|
height: 0.3em;
|
|
|
|
|
|
background-color: var(--ca-seal);
|
|
|
|
|
|
-webkit-mask: url("/brand/brush.svg") no-repeat left center / 100% 100%;
|
|
|
|
|
|
mask: url("/brand/brush.svg") no-repeat left center / 100% 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Delayed entrance, sequenced behind the signature writing itself. */
|
|
|
|
|
|
.ca-seq {
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transform: translateY(var(--ca-reveal-y));
|
|
|
|
|
|
animation: ca-settle var(--ca-dur-slow) var(--ca-ease) var(--ca-seq-delay, 1.45s)
|
|
|
|
|
|
forwards;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@keyframes ca-settle {
|
|
|
|
|
|
to {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
transform: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
|
|
|
|
.ca-seq {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
transform: none;
|
|
|
|
|
|
animation: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-tick {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
transform: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-04 22:53:10 +00:00
|
|
|
|
.skip-link {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
left: 1rem;
|
|
|
|
|
|
top: 1rem;
|
|
|
|
|
|
z-index: 100;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
background: var(--color-foreground);
|
|
|
|
|
|
color: var(--color-background);
|
|
|
|
|
|
padding: 0.65rem 0.85rem;
|
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
transform: translateY(-160%);
|
|
|
|
|
|
transition: transform 150ms ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.skip-link:focus-visible {
|
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Baseline: full site build (brand, pages, DB, agent-editable)
Establishes the deploy baseline on main so the admin agent's publish/rollback
has a clean starting point. Everything built to date: sumi-e brand system,
homepage, projects (DB-driven case studies), resume, about, services +
website-design detail, contact form + DB, changelog, favicon + share card.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoFYZY9gkGPNDqZ7NuEa9a
2026-07-23 20:21:28 +00:00
|
|
|
|
/* ---- brand mark in the header ----
|
|
|
|
|
|
The signature is a single-colour black SVG, so it is inverted rather
|
|
|
|
|
|
than shipped twice. Three states to satisfy:
|
|
|
|
|
|
|
|
|
|
|
|
light theme, on paper → black (no filter)
|
|
|
|
|
|
dark theme, anywhere → white (invert)
|
|
|
|
|
|
home + not scrolled → white (invert) — it floats over the ink
|
|
|
|
|
|
hero, which is dark in BOTH themes
|
|
|
|
|
|
|
|
|
|
|
|
The last rule has to beat the first in light mode, so it is declared
|
|
|
|
|
|
after it and matched on the header's own data attributes. */
|
|
|
|
|
|
/* On the homepage the hero already carries the signature at full size,
|
|
|
|
|
|
so the header mark stays out of the way until you scroll past it.
|
|
|
|
|
|
Two signatures on one screen is redundancy, not branding. */
|
|
|
|
|
|
.ca-header-brand {
|
|
|
|
|
|
transition:
|
|
|
|
|
|
opacity var(--ca-dur-slow) var(--ca-ease),
|
|
|
|
|
|
transform var(--ca-dur-slow) var(--ca-ease);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-home-header]:not([data-scrolled="true"]):not([data-panel-open="true"])
|
|
|
|
|
|
.ca-header-brand {
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transform: translateY(-6px);
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-header-logo {
|
|
|
|
|
|
filter: none;
|
|
|
|
|
|
transition: filter var(--ca-dur-base) var(--ca-ease);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.dark .ca-header-logo {
|
|
|
|
|
|
filter: invert(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-home-header]:not([data-scrolled="true"]):not([data-panel-open="true"])
|
|
|
|
|
|
.ca-header-logo {
|
|
|
|
|
|
filter: invert(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.ca-header-seal {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
width: 1.35rem;
|
|
|
|
|
|
height: 1.35rem;
|
|
|
|
|
|
flex: none;
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
background: var(--ca-seal);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
font-family: var(--font-serif);
|
|
|
|
|
|
font-size: 0.72rem;
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
transition: background-color var(--ca-dur-base) var(--ca-ease);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Over the ink hero the seal needs the lifted red — #d0342c muddies
|
|
|
|
|
|
against near-black. Same reason .dark uses a different value. */
|
|
|
|
|
|
[data-home-header]:not([data-scrolled="true"]):not([data-panel-open="true"])
|
|
|
|
|
|
.ca-header-seal {
|
|
|
|
|
|
background: var(--ca-ink-surface-seal, #e8564a);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-04 22:53:10 +00:00
|
|
|
|
[data-home-header] {
|
|
|
|
|
|
box-shadow: none;
|
|
|
|
|
|
backdrop-filter: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-home-header][data-scrolled="true"],
|
|
|
|
|
|
[data-home-header][data-panel-open="true"] {
|
|
|
|
|
|
background-color: var(--color-background) !important;
|
|
|
|
|
|
border-color: var(--color-border) !important;
|
|
|
|
|
|
color: var(--color-foreground);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-home-header] .home-header-link {
|
|
|
|
|
|
color: rgb(255 255 255 / 0.78);
|
|
|
|
|
|
transition: color 150ms ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-home-header] .home-header-brand {
|
|
|
|
|
|
color: rgb(255 255 255);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-home-header] .home-header-link:hover,
|
|
|
|
|
|
[data-home-header] .home-header-link.is-active {
|
|
|
|
|
|
color: rgb(255 255 255);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-home-header][data-scrolled="true"] .home-header-link,
|
|
|
|
|
|
[data-home-header][data-panel-open="true"] .home-header-link {
|
|
|
|
|
|
color: var(--color-muted-foreground);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-home-header][data-scrolled="true"] .home-header-brand,
|
|
|
|
|
|
[data-home-header][data-panel-open="true"] .home-header-brand {
|
|
|
|
|
|
color: var(--color-foreground);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-home-header][data-scrolled="true"] .home-header-link:hover,
|
|
|
|
|
|
[data-home-header][data-scrolled="true"] .home-header-link.is-active,
|
|
|
|
|
|
[data-home-header][data-panel-open="true"] .home-header-link:hover,
|
|
|
|
|
|
[data-home-header][data-panel-open="true"] .home-header-link.is-active {
|
|
|
|
|
|
color: var(--color-foreground);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-home-header] .home-header-action {
|
|
|
|
|
|
color: rgb(255 255 255 / 0.78);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-home-header] .home-header-action:hover,
|
|
|
|
|
|
[data-home-header] .home-header-action:focus-visible {
|
|
|
|
|
|
background-color: rgb(255 255 255 / 0.12);
|
|
|
|
|
|
color: rgb(255 255 255);
|
|
|
|
|
|
outline: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-home-header][data-scrolled="true"] .home-header-action,
|
|
|
|
|
|
[data-home-header][data-panel-open="true"] .home-header-action {
|
|
|
|
|
|
color: var(--color-muted-foreground);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[data-home-header][data-scrolled="true"] .home-header-action:hover,
|
|
|
|
|
|
[data-home-header][data-scrolled="true"] .home-header-action:focus-visible,
|
|
|
|
|
|
[data-home-header][data-panel-open="true"] .home-header-action:hover,
|
|
|
|
|
|
[data-home-header][data-panel-open="true"] .home-header-action:focus-visible {
|
|
|
|
|
|
background-color: var(--color-muted);
|
|
|
|
|
|
color: var(--color-foreground);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@utility font-serif {
|
|
|
|
|
|
font-family: var(--font-serif);
|
|
|
|
|
|
font-optical-sizing: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@utility prose-article {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
max-width: 68ch;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
font-size: 1.0625rem;
|
|
|
|
|
|
line-height: 1.75;
|
|
|
|
|
|
color: var(--color-foreground);
|
|
|
|
|
|
|
|
|
|
|
|
& h2 {
|
|
|
|
|
|
font-family: var(--font-serif);
|
|
|
|
|
|
font-size: 1.75rem;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
margin-top: 2.5rem;
|
|
|
|
|
|
margin-bottom: 1rem;
|
|
|
|
|
|
letter-spacing: -0.01em;
|
|
|
|
|
|
scroll-margin-top: 6rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
& h3 {
|
|
|
|
|
|
font-family: var(--font-serif);
|
|
|
|
|
|
font-size: 1.35rem;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
margin-top: 2rem;
|
|
|
|
|
|
margin-bottom: 0.75rem;
|
|
|
|
|
|
scroll-margin-top: 6rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
& p {
|
|
|
|
|
|
margin-bottom: 1.25rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
& a {
|
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
|
text-underline-offset: 3px;
|
|
|
|
|
|
text-decoration-thickness: 1px;
|
|
|
|
|
|
}
|
|
|
|
|
|
& a:hover {
|
|
|
|
|
|
text-decoration-thickness: 2px;
|
|
|
|
|
|
}
|
|
|
|
|
|
& blockquote {
|
|
|
|
|
|
border-left: 2px solid var(--color-primary);
|
|
|
|
|
|
padding-left: 1.25rem;
|
|
|
|
|
|
margin: 1.5rem 0;
|
|
|
|
|
|
font-style: italic;
|
|
|
|
|
|
color: var(--color-muted-foreground);
|
|
|
|
|
|
font-family: var(--font-serif);
|
|
|
|
|
|
font-size: 1.15rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
& ul,
|
|
|
|
|
|
& ol {
|
|
|
|
|
|
margin: 1.25rem 0;
|
|
|
|
|
|
padding-left: 1.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
& ul {
|
|
|
|
|
|
list-style: disc;
|
|
|
|
|
|
}
|
|
|
|
|
|
& ol {
|
|
|
|
|
|
list-style: decimal;
|
|
|
|
|
|
}
|
|
|
|
|
|
& li {
|
|
|
|
|
|
margin-bottom: 0.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
& code {
|
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
|
font-size: 0.9em;
|
|
|
|
|
|
color: oklch(0.3 0.08 35);
|
|
|
|
|
|
background: oklch(0.95 0.012 85);
|
|
|
|
|
|
padding: 0.15rem 0.4rem;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.dark & code {
|
|
|
|
|
|
color: oklch(0.88 0.08 55);
|
|
|
|
|
|
background: oklch(0.23 0.006 80);
|
|
|
|
|
|
}
|
|
|
|
|
|
& .code-block {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
margin: 1.5rem 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
& pre {
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
color: oklch(0.22 0.025 70) !important;
|
|
|
|
|
|
background: oklch(0.975 0.006 95) !important;
|
|
|
|
|
|
border: 1px solid oklch(0.88 0.008 80);
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
padding: 1rem 3.5rem 1rem 1.25rem;
|
|
|
|
|
|
overflow-x: auto !important;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
|
box-shadow: inset 0 1px 0 oklch(1 0 0 / 0.75);
|
|
|
|
|
|
}
|
|
|
|
|
|
.dark & pre {
|
|
|
|
|
|
color: oklch(0.91 0.012 85) !important;
|
|
|
|
|
|
background: oklch(0.18 0.006 80) !important;
|
|
|
|
|
|
border-color: oklch(0.33 0.006 80);
|
|
|
|
|
|
box-shadow: inset 0 1px 0 oklch(1 0 0 / 0.06);
|
|
|
|
|
|
}
|
|
|
|
|
|
& pre code {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
min-width: max-content;
|
|
|
|
|
|
color: inherit;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.dark & pre code {
|
|
|
|
|
|
color: inherit;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
}
|
|
|
|
|
|
& pre span {
|
|
|
|
|
|
color: oklch(0.28 0.035 70) !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
.dark & pre span {
|
|
|
|
|
|
color: oklch(0.89 0.014 85) !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
& .code-copy-button {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 0.55rem;
|
|
|
|
|
|
right: 0.55rem;
|
|
|
|
|
|
z-index: 1;
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
width: 2rem;
|
|
|
|
|
|
height: 2rem;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
border: 1px solid oklch(0.83 0.008 80);
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
color: oklch(0.46 0.015 70);
|
|
|
|
|
|
background: oklch(0.995 0.002 95 / 0.94);
|
|
|
|
|
|
transition:
|
|
|
|
|
|
color 150ms ease,
|
|
|
|
|
|
border-color 150ms ease,
|
|
|
|
|
|
background-color 150ms ease,
|
|
|
|
|
|
opacity 150ms ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
& .code-copy-button:hover,
|
|
|
|
|
|
& .code-copy-button:focus-visible {
|
|
|
|
|
|
color: var(--color-foreground);
|
|
|
|
|
|
border-color: var(--color-primary);
|
|
|
|
|
|
outline: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
& .code-copy-button::after {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
bottom: calc(100% + 0.4rem);
|
|
|
|
|
|
content: attr(data-tooltip);
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
|
background: oklch(0.2 0.01 70);
|
|
|
|
|
|
color: oklch(0.98 0.002 90);
|
|
|
|
|
|
padding: 0.25rem 0.45rem;
|
|
|
|
|
|
font-family: var(--font-sans);
|
|
|
|
|
|
font-size: 0.72rem;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transform: translateY(3px);
|
|
|
|
|
|
transition:
|
|
|
|
|
|
opacity 150ms ease,
|
|
|
|
|
|
transform 150ms ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
& .code-copy-button:hover::after,
|
|
|
|
|
|
& .code-copy-button:focus-visible::after,
|
|
|
|
|
|
& .code-copy-button.is-copied::after {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
.dark & .code-copy-button {
|
|
|
|
|
|
color: oklch(0.73 0.012 85);
|
|
|
|
|
|
background: oklch(0.24 0.006 80 / 0.94);
|
|
|
|
|
|
border-color: oklch(0.38 0.006 80);
|
|
|
|
|
|
}
|
|
|
|
|
|
.dark & .code-copy-button:hover,
|
|
|
|
|
|
.dark & .code-copy-button:focus-visible {
|
|
|
|
|
|
color: var(--color-foreground);
|
|
|
|
|
|
border-color: var(--color-primary);
|
|
|
|
|
|
}
|
|
|
|
|
|
& hr {
|
|
|
|
|
|
border: 0;
|
|
|
|
|
|
border-top: 1px solid var(--color-border);
|
|
|
|
|
|
margin: 2.5rem 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
& img {
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
margin: 1.5rem 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@utility callout {
|
|
|
|
|
|
border-left: 3px solid var(--color-primary);
|
|
|
|
|
|
background: color-mix(in oklab, var(--color-primary) 6%, transparent);
|
|
|
|
|
|
padding: 1rem 1.25rem;
|
|
|
|
|
|
border-radius: 0 8px 8px 0;
|
|
|
|
|
|
margin: 1.5rem 0;
|
|
|
|
|
|
}
|