Files
tiennm99 e61e43892c feat: add verified facts, licensed textures and an original portrait
Content: new Origin story, The look and Beyond the series sections, plus
expanded relationships and gated arc detail. Two claims that had been
omitted as disputed are now settled and restored — the October 10
birthday, corroborated by the creator's own birthday artwork, and the
English voice credit. Height added. Age is shown as "28-29 — sources
differ" rather than silently picking a side.

Artwork: the abstract hero silhouette becomes a hand-plotted SVG
portrait whose fills read custom properties, so one drawing re-colours
itself for the manga and anime palettes in The look. Still no image file
of the character — the series is copyrighted, and that does not change
because a picture is redrawn or relicensed.

Textures: three licensed photographic backdrops, one CC0 and two under
the Unsplash License, each credited in the footer with author and
source. Two more were sourced and dropped: their seamless tiles read as
repeating ornament once the blend actually worked. Blending is `screen`
rather than `soft-light`, which collapses toward the backdrop on a
near-black palette and rendered four of the five invisible.

Contrast: magenta text moves to its own lighter token so every textured
surface can carry visible grain and still clear 5:1 on small type. Page
floor is 5.44:1, measured from rendered pixels.

Also: drop background-attachment parallax that cost dropped frames,
stop publishing internal working notes to the live site, add a LICENSE
scoped to exclude the series and the third-party textures, and correct
the artwork-provenance claims now that the page uses photographs.
2026-07-26 00:40:40 +07:00

1669 lines
52 KiB
CSS

/* =========================================================================
Reigen Arataka — unofficial fan tribute
All character artwork is original hand-authored CSS/SVG. The only raster
assets are three licensed photographic textures used as low-opacity
backdrops (see assets/textures/ and the Image credits block in the footer).
Layout: single long scroll, mobile-first, no build step.
========================================================================= */
/* ------------------------------- TOKENS ------------------------------- */
:root {
/* Colour — "poster paint on newsprint" reading of the series' palette.
Contrast ratios are measured against --ink from rendered pixels. Text over
a textured surface measures lower, because the texture lightens the real
backdrop under the glyphs. Magenta is the darkest accent and so sets the
ceiling on how far any texture may lighten a surface — which is why magenta
*text* uses its own lighter token (see --magenta-txt) while the saturated
hue stays for fills, borders and auras. */
--ink: #0c0c11; /* page base */
--ink-2: #14141c; /* raised surface */
--ink-3: #1d1d28; /* card surface */
--line: #2c2c3a; /* hairline borders */
--line-2: #3d3d50;
--paper: #f2ede2; /* primary text (16.71:1) */
--paper-dim: #b8b2a6; /* secondary text (9.25:1) */
--paper-mute: #a5a096; /* tertiary / fine print (7.50:1) */
--magenta: #ff3d7f; /* aura / fills / borders (5.62:1 on ink) */
/* Magenta as *text*. #ff3d7f measured 4.95:1 over the textured panel — on the
AA line with no headroom left for the texture layer. This lighter mix keeps
the hue and buys ~1.3 stops, so every textured surface can carry visible
grain and still clear 5:1 on small magenta type. */
--magenta-txt: #ff5c94; /* magenta text (6.50:1 on ink) */
--teal: #17d6c6; /* aura / links (10.66:1) */
--acid: #d8f24a; /* highlight (15.53:1) */
--tan: #d9b483; /* warm neutral (10.04:1) */
--tan-dark: #b18f66;
/* Portrait palette. Overridden per instance for the manga/anime compare. */
--fig-skin: #e8c69e;
--fig-skin-2: #c69a6b;
--fig-skin-3: #a3763f;
--fig-line: #47331f;
--fig-hair: #c9884a;
--fig-hair-2: #e0aa72;
--fig-suit: #3b3b47;
--fig-suit-2: #26262f;
--fig-shirt: #ece7dd;
--fig-shirt-2: #c8c2b5;
--fig-tie: #ef5f96;
--fig-tie-2: #c9265d;
/* Type */
--f-display: 'Archivo Black', 'Archivo', 'Helvetica Neue', Arial, sans-serif;
--f-body: 'Archivo', 'Helvetica Neue', Arial, sans-serif;
--f-quote: 'Newsreader', Georgia, 'Times New Roman', serif;
--f-mono: ui-monospace, 'Cascadia Mono', 'SF Mono', Menlo, Consolas, monospace;
/* Latin webfonts carry no kanji — Japanese text needs its own stack. */
--f-ja: 'Hiragino Kaku Gothic ProN', 'Hiragino Sans', 'Yu Gothic UI', 'Yu Gothic',
'Meiryo', 'MS PGothic', 'Noto Sans JP', 'Noto Sans CJK JP', sans-serif;
--fs-xs: 0.75rem;
--fs-sm: clamp(0.8125rem, 0.79rem + 0.12vw, 0.9375rem);
--fs-base: clamp(1rem, 0.96rem + 0.2vw, 1.125rem);
--fs-md: clamp(1.0625rem, 1rem + 0.35vw, 1.3125rem);
--fs-lg: clamp(1.3125rem, 1.16rem + 0.75vw, 1.875rem);
--fs-xl: clamp(1.625rem, 1.32rem + 1.5vw, 2.75rem);
--fs-2xl: clamp(2rem, 1.5rem + 2.6vw, 3.75rem);
--fs-hero: clamp(2.75rem, 1.4rem + 9.4vw, 8.5rem);
/* Space */
--sp-1: 0.25rem; --sp-2: 0.5rem; --sp-3: 0.75rem; --sp-4: 1rem;
--sp-5: 1.5rem; --sp-6: 2rem; --sp-7: 3rem; --sp-8: 4rem;
--sp-9: clamp(3.5rem, 2rem + 7vw, 7rem);
--measure: 62ch;
--wrap: 74rem;
--pad: clamp(1rem, 0.5rem + 2.5vw, 3rem);
--radius: 2px;
/* Anchor offset for the sticky header. Mobile-first: the header stacks the
brand over the nav strip (~98px). Overridden at >=56rem where it is one row. */
--header-h: 6.25rem;
/* Texture paths — relative, so the project subpath on GitHub Pages works.
--tex-paper is a grain *plate*, not a photograph: the source paper shot was
nearly flat (sigma 4.6 of 255), so its flat tone was subtracted and only the
detail kept, amplified and highlight-clipped. See the .tex block. */
--tex-paper: url('../assets/textures/paper-grain.jpg');
--tex-city: url('../assets/textures/city-night.jpg');
--tex-bokeh: url('../assets/textures/bokeh-dust.jpg');
/* Motion */
--dur-fast: 150ms;
--dur: 240ms;
--dur-reveal: 560ms;
--ease: cubic-bezier(0.2, 0.7, 0.3, 1);
}
/* ------------------------------- RESET -------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html {
-webkit-text-size-adjust: 100%;
scroll-behavior: smooth;
color-scheme: dark;
}
body {
margin: 0;
background: var(--ink);
color: var(--paper);
font-family: var(--f-body);
font-size: var(--fs-base);
line-height: 1.6;
font-synthesis-weight: none;
-webkit-font-smoothing: antialiased;
overflow-wrap: break-word;
touch-action: manipulation;
-webkit-tap-highlight-color: rgba(216, 242, 74, 0.2);
}
/* Subtle halftone texture — decorative, never intercepts input. */
body::after {
content: "";
position: fixed;
inset: 0;
z-index: 60;
pointer-events: none;
background-image: radial-gradient(circle at center, rgba(242,237,226,0.5) 0.5px, transparent 0.6px);
background-size: 4px 4px;
opacity: 0.05;
}
h1, h2, h3, h4 {
margin: 0;
line-height: 1.08;
font-weight: 700;
letter-spacing: -0.015em;
text-wrap: balance;
}
p, ul, ol, figure, table, blockquote { margin: 0; }
p { text-wrap: pretty; }
ul, ol { padding: 0; list-style: none; }
img, svg { max-width: 100%; }
table { border-collapse: collapse; }
cite { font-style: italic; }
a { color: var(--teal); text-decoration-thickness: 1px; text-underline-offset: 0.18em; }
a:hover { color: var(--acid); }
:focus-visible {
outline: 3px solid var(--acid);
outline-offset: 3px;
border-radius: var(--radius);
}
[lang="ja"] { font-family: var(--f-ja); }
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.visually-hidden {
position: absolute; width: 1px; height: 1px;
margin: -1px; padding: 0; border: 0;
clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}
/* ---------------------------- SKIP LINK -------------------------------- */
.skip-link {
position: absolute;
left: var(--sp-4);
top: -6rem;
z-index: 100;
padding: var(--sp-3) var(--sp-5);
background: var(--acid);
color: var(--ink);
font-weight: 700;
text-decoration: none;
transition: top var(--dur) var(--ease);
}
.skip-link:focus { top: var(--sp-3); color: var(--ink); }
/* -------------------------- TEXTURE BACKDROPS -------------------------- */
/* Shared machinery. A textured surface paints an opaque background, isolates
itself, then layers a texture above that background but below its own content
(negative z-index inside the isolated group). None of these backdrops has
ever shown up as an LCP candidate in a trace — not because pseudo-elements
are exempt (they are not), but because each is stretched over a large area
and so falls under Chromium's low-entropy threshold for background images.
The hero's bokeh plate does qualify; see the note on .hero__art::before.
Two rules make this layer behave, both learned the hard way:
1. `screen`, never `soft-light`. Per the W3C compositing formula soft-light
collapses toward the backdrop as the backdrop goes to black, so on this
near-black palette it discards the source almost entirely — four textures
decoded and composited for a change measuring under 2.5 of 765. `screen`
over a dark backdrop is effectively additive, so it survives; it also
never darkens, which keeps the coloured gradients under .section--quotes
intact instead of washing them flat.
2. `.tex` declares its own background-color. A blend mode is only defined
against a known backdrop, and two sections here never set one — so their
blend degenerated to plain source-over and the texture landed at full
alpha while every other section got a near-no-op. Same declaration, two
completely different renderings, decided by whether the consumer happened
to declare a background. Consumers may still override this; they must not
have to.
Because the blend is additive, the *brightest* texture pixel sets the worst
contrast case under a glyph. The plates are therefore highlight-clipped at
source (max ~115 of 255) so `opacity` alone bounds the worst case: peak lift
is about opacity x 115. Raising any --tex-op means re-measuring. */
.tex {
position: relative;
isolation: isolate;
overflow: hidden;
background-color: var(--ink);
}
.tex::before {
content: "";
position: absolute;
inset: 0;
z-index: -1;
pointer-events: none;
background-image: var(--tex-img);
background-position: var(--tex-pos, center);
background-size: var(--tex-size, cover);
background-repeat: var(--tex-repeat, no-repeat);
opacity: var(--tex-op, 0.2);
mix-blend-mode: var(--tex-blend, screen);
/* No grayscale() pass: the two grain plates already ship as 8-bit Greyscale.
Only city-night and bokeh-dust are sRGB, and both desaturate themselves. */
filter: var(--tex-filter, none);
}
/* Every grain surface uses the one paper plate, as a seamless 256px tile painted
1:1 — never `cover`. Upscaling a grain tile averages the grain away: the
section that did that measured under one RGB step of variation across a whole
band, a flat grey wash rather than texture. Only the opacity varies per
surface, set beside each section below.
A second plate (worn concrete) used to back .section--origin and .site-footer
and has been deleted. Its source photo was flatter still (sigma 3.3 of 255),
so the 12x amplification needed to make it visible also amplified its coarse
trowel streaks — and at any tile size small enough to be worth shipping those
streaks repeated legibly, as a mirror motif or a seam depending on how the
tile was closed. It could be visible, or it could be seamless, but not both. */
.section--who,
.section--beyond,
.section--quotes,
.section--origin,
.panel--fake,
.site-footer {
--tex-img: var(--tex-paper);
--tex-size: 256px 256px;
--tex-repeat: repeat;
}
/* ------------------------------ HEADER -------------------------------- */
.site-header {
position: sticky;
top: 0;
z-index: 50;
background: rgba(12, 12, 17, 0.9);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--line);
}
.site-header__inner {
max-width: var(--wrap);
margin-inline: auto;
padding: 0 var(--pad);
display: flex;
flex-direction: column;
gap: 0;
}
/* Reading-position bar. Gated behind @supports so browsers without
scroll-driven animations get nothing rather than a permanently full bar. */
.scroll-progress { display: none; }
@supports (animation-timeline: scroll()) {
.scroll-progress {
display: block;
position: absolute;
left: 0; right: 0; bottom: -1px;
height: 2px;
pointer-events: none;
}
.scroll-progress span {
display: block;
height: 100%;
background: linear-gradient(90deg, var(--magenta), var(--tan), var(--acid));
transform: scaleX(0);
transform-origin: 0 50%;
animation: read-progress linear both;
animation-timeline: scroll(root block);
}
@keyframes read-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
}
.brand {
display: flex;
align-items: center;
gap: var(--sp-3);
padding: var(--sp-3) 0 var(--sp-2);
color: var(--paper);
text-decoration: none;
min-height: 44px;
}
.brand__mark {
flex: none;
width: 2rem; height: 2rem;
display: grid; place-items: center;
background: var(--magenta);
color: var(--ink);
font-size: 1.25rem; /* 霊 is a dense glyph — needs the extra size to read */
font-weight: 500;
line-height: 1;
transition: background var(--dur) var(--ease);
}
.brand__text { display: flex; flex-direction: column; line-height: 1.15; }
.brand__text b { font-size: var(--fs-sm); letter-spacing: 0.01em; }
.brand__text small {
font-family: var(--f-mono);
font-size: 0.625rem;
letter-spacing: 0.09em;
text-transform: uppercase;
color: var(--paper-mute);
}
.brand:hover .brand__text b { color: var(--acid); }
.brand:hover .brand__mark { background: var(--acid); }
/* Nav: horizontal scroller on narrow screens, never widens the page. */
.site-nav { position: relative; margin-inline: calc(var(--pad) * -1); }
/* Fade at the right edge hints that the strip scrolls. Removed at >=72rem,
the width at which all ten chips fit inside the wrapper. */
.site-nav::after {
content: "";
position: absolute;
right: 0; top: 0; bottom: 0;
width: 2.5rem;
pointer-events: none;
background: linear-gradient(90deg, rgba(12,12,17,0), rgba(12,12,17,0.95));
}
.site-nav ul {
display: flex;
gap: var(--sp-1);
overflow-x: auto;
scrollbar-width: none;
padding: 0 var(--pad) var(--sp-2);
scroll-snap-type: x proximity;
}
.site-nav ul::-webkit-scrollbar { display: none; }
.site-nav li { flex: none; scroll-snap-align: start; }
.site-nav a {
display: block;
padding: var(--sp-2) var(--sp-3);
color: var(--paper-dim);
font-family: var(--f-mono);
font-size: var(--fs-xs);
letter-spacing: 0.06em;
text-transform: uppercase;
text-decoration: none;
white-space: nowrap;
border: 1px solid transparent;
transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.site-nav a:hover { color: var(--paper); border-color: var(--line-2); }
.site-nav a[aria-current="true"] {
color: var(--ink);
background: var(--acid);
border-color: var(--acid);
font-weight: 600;
}
@media (min-width: 56rem) {
/* Brand and nav share one row from here, so the header is shorter. */
:root { --header-h: 4.25rem; }
.site-header__inner { flex-direction: row; align-items: center; justify-content: space-between; gap: var(--sp-5); }
.brand { padding: var(--sp-4) 0; }
.site-nav { margin-inline: 0; min-width: 0; }
.site-nav ul { padding: 0; }
}
@media (min-width: 72rem) {
.site-nav::after { display: none; }
.site-nav ul { overflow-x: visible; }
}
/* ------------------------------- HERO --------------------------------- */
.hero {
position: relative;
isolation: isolate;
overflow: hidden;
max-width: 100%;
padding: var(--sp-8) var(--pad) var(--sp-9);
min-height: min(92svh, 54rem);
display: grid;
align-content: center;
}
/* --- hero art: photo bloom + aura blobs + rays + the portrait --- */
.hero__art {
position: absolute;
inset: 0;
z-index: -1;
pointer-events: none;
overflow: hidden;
}
/* Bokeh/dust plate. Blurred and desaturated to near-abstraction so it reads
as depth rather than as a photograph. ~10 KB.
Known and accepted: this plate IS the page's LCP element, reported against
its originating element. Moving it from a real <span> to this pseudo-element
did NOT remove candidacy — a background-image is an LCP candidate either way;
only Chromium's low-entropy exclusion keeps the .tex backdrops out, and this
plate carries too much detail per painted pixel to qualify. It stays because
it is 10 KB and paints alongside the heading, so the metric names a
decorative layer without actually delaying anything a reader waits for. If
that trade stops being acceptable, replace the photo with CSS gradients —
no image, no candidate. */
.hero__art::before {
content: "";
position: absolute;
inset: -12%;
background: var(--tex-bokeh) center / cover no-repeat;
filter: blur(7px) saturate(0.4) brightness(0.9);
opacity: 0.3;
}
/* Scrim: keeps the art bright on the right, guarantees text contrast on the
left where the copy sits. Without this the aura undercuts AA on the kicker. */
.hero__art::after {
content: "";
position: absolute;
inset: 0;
background:
/* darkens the copy side */
linear-gradient(90deg,
rgba(12,12,17,0.90) 0%, rgba(12,12,17,0.68) 32%,
rgba(12,12,17,0.18) 55%, rgba(12,12,17,0) 70%),
/* tames the aura glare behind the kicker on narrow screens */
linear-gradient(180deg, rgba(12,12,17,0.45) 0%, rgba(12,12,17,0) 40%);
}
.aura {
position: absolute;
border-radius: 50%;
filter: blur(46px);
opacity: 0.5;
}
.aura--a {
width: 34rem; height: 34rem;
right: -12rem; top: -10rem;
background: radial-gradient(circle at 50% 50%, var(--magenta) 0%, rgba(255,61,127,0) 68%);
}
.aura--b {
width: 30rem; height: 30rem;
left: -12rem; bottom: -8rem;
background: radial-gradient(circle at 50% 50%, var(--teal) 0%, rgba(23,214,198,0) 68%);
opacity: 0.4;
}
.aura--c {
width: 22rem; height: 22rem;
right: 8%; bottom: 6%;
background: radial-gradient(circle at 50% 50%, var(--acid) 0%, rgba(216,242,74,0) 66%);
opacity: 0.16;
}
.rays {
position: absolute;
right: -6rem;
top: 46%;
translate: 0 -50%;
width: 30rem;
color: var(--tan);
opacity: 0.22;
}
/* The portrait. Halo and outer ring live outside the reusable symbol because
they only belong to the hero composition. */
.figure {
position: absolute;
right: -4.5rem;
bottom: -3rem;
width: 18rem;
opacity: 0.62;
}
.fg-halo {
fill: none;
stroke: var(--tan);
stroke-width: 26;
opacity: 0.16;
}
.fg-ring {
fill: none;
stroke: var(--acid);
stroke-width: 1.5;
stroke-dasharray: 2 12;
opacity: 0.5;
}
.hero__salt {
position: absolute;
right: 46%;
bottom: 16%;
width: 9rem;
color: var(--tan);
opacity: 0.5;
rotate: -14deg;
}
/* 48-68rem: the portrait becomes a subject again, so the copy measure is
pulled in to keep text and artwork from ever overlapping. Measured at 768px:
tagline 8.0:1, fine print 6.5:1. */
@media (min-width: 48rem) {
.hero { --hero-measure: 36ch; }
.figure { width: 20rem; right: -3rem; bottom: -3.5rem; opacity: 0.95; }
.rays { right: -4rem; width: 30rem; opacity: 0.26; }
.hero__salt { width: 10rem; right: 40%; bottom: 24%; }
.hero__note { max-width: 44ch; }
}
@media (min-width: 68rem) {
.hero { --hero-measure: 46ch; }
.figure { width: 31rem; right: 4%; }
.rays { right: -1rem; width: 36rem; opacity: 0.28; }
.hero__salt { width: 15rem; right: 32%; }
.hero__note { max-width: 56ch; }
}
/* --- hero text --- */
.hero__body {
position: relative;
max-width: var(--wrap);
width: 100%;
margin-inline: auto;
}
.kicker {
font-family: var(--f-mono);
font-size: var(--fs-xs);
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--magenta-txt);
margin-bottom: var(--sp-3);
width: fit-content;
}
.kicker--strike span {
position: relative;
display: inline-block;
padding-right: 0.2em;
}
.kicker--strike span::after {
content: "";
position: absolute;
left: 0; right: 0; top: 52%;
height: 2px;
background: var(--magenta);
transform-origin: left center;
}
.hero__title {
font-family: var(--f-display);
font-size: var(--fs-hero);
line-height: 0.86;
letter-spacing: -0.03em;
text-transform: uppercase;
margin-bottom: var(--sp-3);
}
.hero__title span { display: block; }
/* Second word outlined rather than filled: poster device, and it lets the
portrait read through the letterforms. */
.hero__title span:last-child {
color: transparent;
-webkit-text-stroke: clamp(1.5px, 0.28vw, 3px) var(--tan);
}
@supports not ((-webkit-text-stroke: 1px #000)) {
.hero__title span:last-child { color: var(--tan); }
}
.hero__ja {
font-size: var(--fs-lg);
font-weight: 500;
letter-spacing: 0.28em;
color: var(--paper-dim);
margin-bottom: var(--sp-5);
width: fit-content;
}
.hero__claim {
font-family: var(--f-quote);
font-style: italic;
font-size: var(--fs-md);
color: var(--acid);
border-left: 3px solid var(--acid);
padding-left: var(--sp-4);
margin-bottom: var(--sp-5);
max-width: 34ch;
}
.hero__tagline {
max-width: var(--hero-measure, 46ch);
color: var(--paper-dim);
margin-bottom: var(--sp-6);
}
.stat-chips {
display: flex;
flex-wrap: wrap;
gap: var(--sp-3);
margin-bottom: var(--sp-6);
}
.stat-chips li {
display: flex;
align-items: baseline;
gap: var(--sp-3);
padding: var(--sp-2) var(--sp-4);
background: var(--ink-2);
border: 1px solid var(--line);
border-left: 3px solid var(--teal);
}
.stat-chips__k {
font-family: var(--f-mono);
font-size: var(--fs-xs);
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--paper-dim);
}
.stat-chips__v {
font-family: var(--f-display);
font-size: var(--fs-md);
color: var(--acid);
}
.stat-chips li:has(.stat-chips__v--zero) { border-left-color: var(--magenta); }
.stat-chips__v--zero { color: var(--magenta-txt); }
.hero__note {
max-width: 56ch;
font-size: var(--fs-sm);
color: var(--paper-dim);
border-top: 1px solid var(--line);
padding-top: var(--sp-4);
}
/* Below 48rem the copy fills the hero, so there is no honest room for the
portrait as a subject. It drops back to a watermark behind the type —
measured at 9:1 for the title and 5.1:1 for the tagline over its brightest
area, both clear of AA. */
@media (max-width: 47.9375rem) {
.figure {
width: 21rem;
right: -5.5rem;
top: 2rem;
bottom: auto;
opacity: 0.2;
}
.hero__salt { top: 24rem; bottom: auto; right: 56%; width: 7rem; opacity: 0.4; }
/* The copy runs the full width here, so the aura and the scrim both have to
work harder. Sampled: the Japanese name went from 3.46:1 to 5.4:1. */
.aura--a { opacity: 0.34; }
.aura--b { opacity: 0.26; }
.hero__art::after {
background:
linear-gradient(180deg, rgba(12,12,17,0.66) 0%, rgba(12,12,17,0.46) 40%,
rgba(12,12,17,0.34) 72%, rgba(12,12,17,0.52) 100%),
linear-gradient(90deg, rgba(12,12,17,0.5) 0%, rgba(12,12,17,0.28) 62%,
rgba(12,12,17,0.18) 100%);
}
}
/* Below the wide breakpoint the portrait sits directly behind this fine print
and dropped it under AA (measured 3.35:1 at 320px). A solid backdrop takes
the art out from behind the text entirely. */
@media (max-width: 47.9375rem) {
.hero__note {
position: relative;
z-index: 1;
/* Solid behind every glyph, fading only across the bottom padding so the
backdrop's edge doesn't slice visibly through the figure behind it. */
background: linear-gradient(
to bottom,
var(--ink) 0,
var(--ink) calc(100% - var(--sp-3)),
rgba(12, 12, 17, 0) 100%
);
padding-bottom: var(--sp-3);
}
}
/* Sits over the teal aura, which pushed 10px mute text to 3.1:1. A scrim that
fades out to the right takes the glow out from behind the glyphs. */
.scroll-cue {
position: absolute;
left: 0;
bottom: var(--sp-4);
padding: var(--sp-1) 4rem var(--sp-1) var(--pad);
background: linear-gradient(90deg, rgba(12,12,17,0.92) 0%, rgba(12,12,17,0.9) 55%, rgba(12,12,17,0) 100%);
display: flex;
align-items: center;
gap: var(--sp-3);
font-family: var(--f-mono);
font-size: 0.625rem;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--paper-mute);
}
.scroll-cue span {
width: 2.5rem;
height: 1px;
background: var(--paper-mute);
animation: cue 2.4s var(--ease) infinite;
transform-origin: left center;
}
@keyframes cue {
0%, 100% { transform: scaleX(0.4); opacity: 0.4; }
50% { transform: scaleX(1); opacity: 1; }
}
/* ------------------------- SALT-SPLASH RULE ---------------------------- */
/* Hand-placed dot field standing in for a thrown handful of salt. */
.salt-rule {
height: 3.5rem;
background-repeat: no-repeat;
background-color: transparent;
background-image:
radial-gradient(circle at 4% 60%, var(--tan) 0 2.6px, transparent 2.7px),
radial-gradient(circle at 9% 35%, var(--tan) 0 1.6px, transparent 1.7px),
radial-gradient(circle at 14% 72%, var(--tan) 0 2.1px, transparent 2.2px),
radial-gradient(circle at 20% 44%, var(--paper-dim) 0 1.2px, transparent 1.3px),
radial-gradient(circle at 26% 66%, var(--tan) 0 2.8px, transparent 2.9px),
radial-gradient(circle at 32% 30%, var(--tan) 0 1.4px, transparent 1.5px),
radial-gradient(circle at 38% 58%, var(--paper-dim) 0 2px, transparent 2.1px),
radial-gradient(circle at 44% 76%, var(--tan) 0 1.3px, transparent 1.4px),
radial-gradient(circle at 50% 40%, var(--tan) 0 2.4px, transparent 2.5px),
radial-gradient(circle at 56% 64%, var(--paper-dim) 0 1.5px, transparent 1.6px),
radial-gradient(circle at 62% 32%, var(--tan) 0 2.2px, transparent 2.3px),
radial-gradient(circle at 68% 70%, var(--tan) 0 1.7px, transparent 1.8px),
radial-gradient(circle at 74% 48%, var(--paper-dim) 0 2.6px, transparent 2.7px),
radial-gradient(circle at 80% 26%, var(--tan) 0 1.2px, transparent 1.3px),
radial-gradient(circle at 86% 62%, var(--tan) 0 2px, transparent 2.1px),
radial-gradient(circle at 92% 38%, var(--paper-dim) 0 1.6px, transparent 1.7px),
radial-gradient(circle at 97% 68%, var(--tan) 0 2.3px, transparent 2.4px);
opacity: 0.6;
}
/* The photographic salt band that used to sit behind this rule is gone. Its
asset had been made "seamless" by mirroring the whole frame, which folded the
rim of the salt jar into a four-way kaleidoscope. soft-light hid that; once
the blend actually rendered it read as repeating ornament, not as salt, and
no crop of the surviving 520px derivative tiles without either that pattern
or a visible seam. The hand-placed dot field above already tells the story in
pure CSS, so the 28 KB photograph was deleted rather than left invisible. */
/* ----------------------------- SECTIONS -------------------------------- */
.section {
max-width: var(--wrap);
margin-inline: auto;
padding: var(--sp-9) var(--pad);
scroll-margin-top: calc(var(--header-h) + 1rem);
}
/* Full-bleed variant: the section paints edge to edge while its children stay
on the same measure as every other section. A single centred grid column
does the work, so children narrower than the wrapper still align left
instead of drifting to the middle of the page. */
.section--bleed {
max-width: none;
display: grid;
grid-template-columns: minmax(0, var(--wrap));
justify-content: center;
}
.section__head { margin-bottom: var(--sp-7); max-width: var(--measure); }
.section__num {
font-family: var(--f-display);
font-size: var(--fs-md);
color: var(--magenta-txt);
letter-spacing: 0.06em;
margin-bottom: var(--sp-2);
}
.section h2 {
font-family: var(--f-display);
font-size: var(--fs-2xl);
text-transform: uppercase;
letter-spacing: -0.025em;
line-height: 0.95;
}
.section__lede {
margin-top: var(--sp-4);
font-size: var(--fs-md);
color: var(--paper-dim);
}
.fine-print {
margin-top: var(--sp-4);
font-family: var(--f-mono);
font-size: var(--fs-xs);
line-height: 1.7;
color: var(--paper-mute);
}
.fine-print--wide { max-width: var(--measure); margin-top: var(--sp-6); }
@media (min-width: 56rem) {
.section__head { display: grid; grid-template-columns: 4rem 1fr; column-gap: var(--sp-5); max-width: none; }
.section__num { grid-row: 1 / span 2; margin: 0; padding-top: 0.35em; }
.section__lede { max-width: var(--measure); }
}
/* --------------------------- PROFILE TABLE ----------------------------- */
.table-scroll {
overflow-x: auto;
border: 1px solid var(--line);
background: var(--ink-2);
}
.data-table { width: 100%; font-size: var(--fs-sm); }
.data-table td { color: var(--paper); }
.data-table th {
font-family: var(--f-mono);
font-size: var(--fs-xs);
font-weight: 500;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--paper-mute);
text-align: left;
}
.t-ja { color: var(--tan); letter-spacing: 0.12em; margin-left: 0.4em; white-space: nowrap; }
/* Marks a value the sources disagree on, in words as well as in style. */
.t-note { color: var(--magenta-txt); font-family: var(--f-mono); font-size: var(--fs-xs); }
/* Narrow screens: label above value, one block per row. Reads far better at
320px than a horizontally scrolling grid. ARIA roles in the markup keep the
table semantics intact. */
@media (max-width: 43.999rem) {
.data-table tr { display: block; padding: var(--sp-4) var(--sp-4); }
.data-table tr + tr { border-top: 1px solid var(--line); }
.data-table th,
.data-table td { display: block; padding: 0; }
.data-table th { margin-bottom: var(--sp-1); }
}
@media (min-width: 44rem) {
.data-table th,
.data-table td { padding: var(--sp-4) var(--sp-5); vertical-align: top; }
.data-table th { width: 32%; white-space: nowrap; }
.data-table tr + tr th,
.data-table tr + tr td { border-top: 1px solid var(--line); }
.data-table tbody tr:hover td { background: var(--ink-3); }
}
/* ------------------------------ PROSE ---------------------------------- */
.section--who {
border-top: 1px solid var(--line);
--tex-op: 0.26;
}
.prose { max-width: var(--measure); font-size: var(--fs-md); }
.prose p + p { margin-top: var(--sp-5); }
.prose em { color: var(--acid); font-style: italic; }
.prose__drop::first-letter {
float: left;
font-family: var(--f-display);
font-size: 3.6em;
line-height: 0.78;
padding: 0.06em 0.12em 0 0;
color: var(--magenta-txt);
}
.pull {
margin-top: var(--sp-6);
padding: var(--sp-5) 0 var(--sp-5) var(--sp-5);
border-left: 3px solid var(--teal);
font-family: var(--f-quote);
font-style: italic;
font-size: var(--fs-lg);
line-height: 1.35;
color: var(--paper);
}
/* ---------------------------- ORIGIN STORY ----------------------------- */
/* The shabby rented room the business started in: a raised surface, and the
heaviest grain on the page bar the full-bleed prose sections. */
.section--origin {
background: var(--ink-2);
border-block: 1px solid var(--line);
--tex-op: 0.20;
}
.section--origin .pull { border-left-color: var(--tan); }
.origin { display: grid; gap: var(--sp-6); }
@media (min-width: 62rem) {
.origin { grid-template-columns: minmax(0, 1fr) 17rem; gap: var(--sp-8); align-items: start; }
}
/* Door plate for the office. Original CSS, no photograph. */
.plaque {
position: relative;
padding: var(--sp-6) var(--sp-5);
background:
linear-gradient(150deg, rgba(217,180,131,0.14), rgba(217,180,131,0.03) 60%),
var(--ink-3);
border: 1px solid var(--tan-dark);
box-shadow: inset 0 0 0 1px rgba(12,12,17,0.8), 0 14px 30px -18px rgba(0,0,0,0.9);
text-align: center;
}
/* Two screw heads, because it is screwed to a door. */
.plaque::before,
.plaque::after {
content: "";
position: absolute;
top: var(--sp-3);
width: 6px; height: 6px;
border-radius: 50%;
background: var(--tan-dark);
opacity: 0.7;
}
.plaque::before { left: var(--sp-3); }
.plaque::after { right: var(--sp-3); }
.plaque__mark {
display: block;
font-size: 1.75rem;
line-height: 1;
color: var(--tan);
margin-bottom: var(--sp-3);
}
.plaque__name {
font-family: var(--f-display);
font-size: var(--fs-sm);
line-height: 1.35;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--paper);
overflow-wrap: normal;
}
.plaque__meta {
margin-top: var(--sp-4);
padding-top: var(--sp-3);
border-top: 1px solid var(--tan-dark);
font-family: var(--f-mono);
font-size: var(--fs-xs);
line-height: 1.6;
color: var(--paper-dim);
}
/* ----------------------------- TRAITS ---------------------------------- */
.trait-grid {
display: grid;
gap: 1px;
background: var(--line);
border: 1px solid var(--line);
}
@media (min-width: 40rem) { .trait-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 64rem) { .trait-grid { grid-template-columns: repeat(3, 1fr); } }
.trait {
position: relative;
background: var(--ink-2);
padding: var(--sp-5);
transition: background var(--dur) var(--ease);
}
.trait::after {
content: "";
position: absolute;
left: 0; top: 0; bottom: 0;
width: 3px;
background: var(--magenta);
transform: scaleY(0);
transform-origin: 50% 0;
transition: transform var(--dur) var(--ease);
}
.trait:hover { background: var(--ink-3); }
.trait:hover::after { transform: scaleY(1); }
.trait h3 {
font-size: var(--fs-md);
margin-bottom: var(--sp-3);
color: var(--tan);
}
.trait p { font-size: var(--fs-sm); color: var(--paper-dim); }
/* ------------------------------ THE LOOK ------------------------------- */
.section--look {
background:
linear-gradient(90deg, rgba(107,70,48,0.16) 0%, rgba(12,12,17,0) 42%,
rgba(12,12,17,0) 58%, rgba(239,95,150,0.14) 100%),
var(--ink);
border-block: 1px solid var(--line);
}
.compare { display: grid; gap: var(--sp-5); }
@media (min-width: 54rem) {
.compare { grid-template-columns: 1fr auto 1fr; align-items: stretch; gap: var(--sp-6); }
}
.ver {
display: flex;
flex-direction: column;
padding: var(--sp-5);
background: var(--ink-2);
border: 1px solid var(--line);
border-top: 4px solid var(--ver-accent, var(--line-2));
transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.ver:hover { background: var(--ink-3); }
.ver--manga {
--ver-accent: #a8714c;
--fig-hair: #5f3d28;
--fig-hair-2: #7d5438;
--fig-tie: #1b1b22;
--fig-tie-2: #101015;
}
.ver--anime {
--ver-accent: var(--magenta);
--fig-hair: #cf8b48;
--fig-hair-2: #ecb677;
--fig-tie: #ef5f96;
--fig-tie-2: #cf4278;
}
.ver__tag {
font-family: var(--f-mono);
font-size: var(--fs-xs);
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ver-accent);
margin-bottom: var(--sp-4);
}
.ver__art {
display: grid;
place-items: center;
height: 12rem;
margin-bottom: var(--sp-5);
background:
radial-gradient(9rem 7rem at 50% 42%, color-mix(in srgb, var(--ver-accent) 22%, transparent), transparent 70%),
var(--ink);
border: 1px solid var(--line);
overflow: hidden;
}
@media (min-width: 54rem) { .ver__art { height: 16rem; } }
/* The svg viewBox crops to head-and-tie: the hair colour and the tie colour
are the whole point of this section. */
.ver__figure {
width: auto;
height: 104%;
max-width: none;
transition: translate var(--dur) var(--ease);
}
.ver__figure--flip { scale: -1 1; }
.ver:hover .ver__figure { translate: 0 -3%; }
.ver h3 { font-size: var(--fs-md); margin-bottom: var(--sp-4); }
.swatches { display: grid; gap: var(--sp-2); margin-bottom: var(--sp-4); }
.swatches li {
display: flex;
align-items: center;
gap: var(--sp-3);
font-family: var(--f-mono);
font-size: var(--fs-xs);
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--paper-dim);
}
/* The chip is a second cue only — every swatch is also named in words. */
.sw {
flex: none;
width: 1.5rem; height: 1.5rem;
background: var(--sw);
border: 1px solid var(--line-2);
}
.ver__note { font-size: var(--fs-sm); color: var(--paper-dim); margin-top: auto; }
.compare__vs {
display: grid;
place-items: center;
font-family: var(--f-display);
font-size: var(--fs-md);
text-transform: uppercase;
color: var(--paper-mute);
}
.compare__vs span {
padding: var(--sp-2) var(--sp-3);
border: 1px solid var(--line);
background: var(--ink);
}
@media (min-width: 54rem) {
.compare__vs { position: relative; width: 3.5rem; }
.compare__vs::before {
content: "";
position: absolute;
top: 0; bottom: 0; left: 50%;
width: 1px;
background: linear-gradient(180deg, transparent, var(--line-2), transparent);
}
.compare__vs span { position: relative; }
}
.shared {
margin-top: var(--sp-6);
padding-top: var(--sp-5);
border-top: 1px solid var(--line);
}
.shared h3 {
font-family: var(--f-mono);
font-size: var(--fs-xs);
font-weight: 500;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--acid);
margin-bottom: var(--sp-4);
}
.tag-list { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.tag-list li {
padding: var(--sp-2) var(--sp-4);
border: 1px dashed var(--line-2);
font-size: var(--fs-sm);
color: var(--paper-dim);
}
/* --------------------------- ABILITIES SPLIT --------------------------- */
.split { display: grid; gap: var(--sp-5); }
@media (min-width: 58rem) { .split { grid-template-columns: 1fr 1fr; gap: var(--sp-6); } }
.panel {
padding: var(--sp-5);
border: 1px solid var(--line);
background: var(--ink-2);
}
.panel--real { border-top: 4px solid var(--teal); }
.panel--fake {
border-top: 4px solid var(--magenta);
/* The page's tightest contrast case (small magenta headings on a raised
surface), so this panel runs the grain lower than the full-bleed sections. */
--tex-op: 0.17;
}
.panel__tag {
font-family: var(--f-mono);
font-size: var(--fs-xs);
letter-spacing: 0.18em;
text-transform: uppercase;
margin-bottom: var(--sp-2);
}
.panel--real .panel__tag { color: var(--teal); }
.panel--fake .panel__tag { color: var(--magenta-txt); }
.panel__title {
font-family: var(--f-display);
font-size: var(--fs-lg);
text-transform: uppercase;
margin-bottom: var(--sp-5);
}
.panel__intro { font-size: var(--fs-sm); color: var(--paper-dim); margin-bottom: var(--sp-5); }
.skill-list li + li { margin-top: var(--sp-5); }
.skill-list__note { margin-top: var(--sp-2); font-size: var(--fs-sm); color: var(--paper-mute); }
/* "100%"-style segmented meter */
.meter__head {
display: flex;
flex-wrap: wrap;
align-items: baseline;
justify-content: space-between;
gap: var(--sp-2);
margin-bottom: var(--sp-2);
}
.meter__label { font-weight: 600; font-size: var(--fs-sm); }
.meter__val {
font-family: var(--f-mono);
font-size: var(--fs-xs);
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--teal);
}
.meter--zero .meter__val { color: var(--magenta-txt); }
.meter__track {
position: relative;
display: block;
height: 0.75rem;
background: var(--ink);
border: 1px solid var(--line-2);
overflow: hidden;
}
.meter__fill {
display: block;
height: 100%;
width: var(--pct, 0%);
background-image:
repeating-linear-gradient(90deg, var(--teal) 0 8px, transparent 8px 11px);
background-size: 11px 100%;
transform-origin: left center;
}
.meter--zero .meter__fill { background-image: none; }
/* Empty-state hatching so the 0% bar still reads as intentional. */
.meter--zero .meter__track {
border-color: var(--magenta);
background-image: repeating-linear-gradient(135deg, rgba(255,61,127,0.28) 0 4px, transparent 4px 9px);
}
.punchline {
margin-top: var(--sp-7);
padding-top: var(--sp-5);
border-top: 1px solid var(--line);
font-family: var(--f-display);
font-size: var(--fs-lg);
line-height: 1.2;
text-transform: uppercase;
max-width: 40ch;
}
.punchline::first-line { color: var(--magenta-txt); }
/* -------------------------- FAKE TECHNIQUES ---------------------------- */
.tech-list { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); }
.tech {
position: relative;
background: var(--ink-3);
padding: var(--sp-5) var(--sp-5) var(--sp-5) 4.25rem;
transition: background var(--dur) var(--ease);
}
.tech:hover { background: #24242f; }
.tech__icon {
position: absolute;
left: var(--sp-3);
top: calc(var(--sp-5) - 0.1rem);
width: 2.75rem;
height: 1.75rem;
color: var(--tan);
transition: color var(--dur) var(--ease), rotate var(--dur) var(--ease);
}
.tech:hover .tech__icon { color: var(--acid); rotate: -6deg; }
.tech h4 {
font-size: var(--fs-md);
margin-bottom: var(--sp-2);
color: var(--magenta-txt);
}
.tech p { font-size: var(--fs-sm); color: var(--paper-dim); }
/* ------------------------------ QUOTES --------------------------------- */
/* The emotional core of the page: aged paper, serif at scale, generous air. */
.section--quotes {
border-block: 1px solid var(--line);
background-color: var(--ink);
background-image:
radial-gradient(60rem 40rem at 85% 0%, rgba(23,214,198,0.10), transparent 70%),
radial-gradient(50rem 34rem at 0% 100%, rgba(255,61,127,0.10), transparent 70%);
--tex-op: 0.24;
}
.quote-stack { display: grid; gap: var(--sp-5); }
@media (min-width: 52rem) {
.quote-stack { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-6); }
.quote--lead,
.quote--short { grid-column: 1 / -1; }
}
.quote {
position: relative;
padding: var(--sp-7) var(--sp-5) var(--sp-6);
background: linear-gradient(180deg, rgba(29,29,40,0.9), rgba(20,20,28,0.9));
border: 1px solid var(--line);
transition:
border-color var(--dur) var(--ease),
background-color var(--dur) var(--ease),
box-shadow var(--dur) var(--ease);
}
/* Hairline corner marks — a printed-page register mark, not a border. */
.quote::before,
.quote::after {
content: "";
position: absolute;
width: 1.25rem; height: 1.25rem;
border: 1px solid var(--tan-dark);
opacity: 0.45;
transition: opacity var(--dur) var(--ease);
}
.quote::before { top: 6px; left: 6px; border-width: 1px 0 0 1px; }
.quote::after { bottom: 6px; right: 6px; border-width: 0 1px 1px 0; }
.quote:hover {
border-color: var(--tan-dark);
box-shadow: 0 18px 40px -28px rgba(0,0,0,0.9);
}
.quote:hover::before,
.quote:hover::after { opacity: 1; }
.quote__glyph {
position: absolute;
top: 0;
left: var(--sp-5);
translate: 0 -50%;
width: 2.4rem;
height: 1.9rem;
color: var(--magenta-txt);
background: var(--ink-3);
padding-inline: var(--sp-3);
box-sizing: content-box;
transition: color var(--dur) var(--ease);
}
.quote:hover .quote__glyph { color: var(--acid); }
.quote blockquote p {
font-family: var(--f-quote);
font-size: var(--fs-md);
font-style: italic;
line-height: 1.5;
color: var(--paper);
}
.quote--lead { padding-block: var(--sp-8) var(--sp-7); }
.quote--lead blockquote p {
font-size: var(--fs-lg);
line-height: 1.4;
max-width: 46ch;
}
/* Two lines that say the same thing, years apart. Marked as a matched pair. */
.quote--pair { border-top: 3px solid var(--teal); }
.quote--short {
padding-block: var(--sp-8) var(--sp-7);
text-align: center;
background: var(--ink);
}
.quote--short .quote__glyph { left: 50%; translate: -50% -50%; background: var(--ink); }
.quote--short blockquote p {
font-size: var(--fs-xl);
font-style: normal;
line-height: 1.25;
color: var(--acid);
max-width: 24ch;
margin-inline: auto;
}
/* --------------------------- RELATIONSHIPS ----------------------------- */
.rel-list { display: grid; gap: var(--sp-4); }
@media (min-width: 44rem) { .rel-list { grid-template-columns: 1fr 1fr; } }
@media (min-width: 66rem) { .rel-list { grid-template-columns: repeat(3, 1fr); } }
.rel {
padding: var(--sp-5);
background: var(--ink-2);
border: 1px solid var(--line);
border-left: 3px solid var(--line-2);
transition: border-left-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.rel:hover { border-left-color: var(--teal); background: var(--ink-3); }
.rel--primary { border-left-color: var(--magenta); }
@media (min-width: 44rem) { .rel--primary { grid-column: 1 / -1; } }
.rel__role {
font-family: var(--f-mono);
font-size: var(--fs-xs);
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--paper-mute);
margin-bottom: var(--sp-2);
}
.rel h3 { font-size: var(--fs-md); margin-bottom: var(--sp-3); }
.rel--primary h3 { font-size: var(--fs-lg); }
.rel__alt {
display: block;
font-family: var(--f-body);
font-size: var(--fs-sm);
font-weight: 400;
letter-spacing: 0;
color: var(--tan);
margin-top: var(--sp-1);
}
.rel p:last-child { font-size: var(--fs-sm); color: var(--paper-dim); }
/* ------------------------------- ARCS ---------------------------------- */
/* Night city behind the arc where he loses everything. Two layers: the photo,
feathered top and bottom, then a scrim that guarantees text contrast. */
.section--arcs {
border-top: 1px solid var(--line);
background-color: var(--ink);
--tex-img: var(--tex-city);
--tex-pos: 50% 100%;
--tex-op: 0.62;
--tex-blend: normal;
--tex-filter: saturate(0.5) brightness(0.78) contrast(1.05);
}
.section--arcs::before {
-webkit-mask-image: linear-gradient(180deg, transparent, #000 22%, #000 72%, transparent);
mask-image: linear-gradient(180deg, transparent, #000 22%, #000 72%, transparent);
}
.section--arcs::after {
content: "";
position: absolute;
inset: 0;
z-index: -1;
pointer-events: none;
background: linear-gradient(180deg,
rgba(12,12,17,0.70) 0%, rgba(12,12,17,0.55) 42%, rgba(12,12,17,0.88) 100%);
}
/* No background-attachment: fixed here. It repaints the whole section every
frame — measured 4 dropped frames and a 79ms worst frame across a scroll of
this section, versus none with scroll attachment — for a parallax effect
subtle enough that nobody misses it. */
.timeline { position: relative; display: grid; gap: var(--sp-4); padding-left: var(--sp-5); }
.timeline::before {
content: "";
position: absolute;
left: 3px; top: 0.75rem; bottom: 0.75rem;
width: 1px;
background: linear-gradient(180deg, var(--magenta), var(--teal));
opacity: 0.6;
}
.arc { position: relative; }
.arc::before {
content: "";
position: absolute;
left: calc(var(--sp-5) * -1);
top: 0.7rem;
width: 7px; height: 7px;
background: var(--teal);
border-radius: 50%;
}
.arc--spoiler::before { background: var(--magenta); }
.arc__when {
font-family: var(--f-mono);
font-size: var(--fs-xs);
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--teal);
margin-bottom: var(--sp-2);
}
.arc--spoiler .arc__when { color: var(--magenta-txt); }
.arc h3 { font-size: var(--fs-lg); margin-bottom: var(--sp-3); }
.arc > p:last-child,
.spoiler__body p { max-width: var(--measure); color: var(--paper-dim); }
.spoiler__body p + p { margin-top: var(--sp-4); }
.spoiler__body em { color: var(--acid); font-style: italic; }
/* Spoiler disclosure */
.spoiler { border: 1px dashed var(--line-2); background: rgba(20,20,28,0.88); }
.spoiler[open] { border-style: solid; border-color: var(--line); }
.spoiler > summary {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--sp-3);
padding: var(--sp-4) var(--sp-5);
min-height: 44px;
cursor: pointer;
list-style: none;
transition: background var(--dur-fast) var(--ease);
}
.spoiler > summary::-webkit-details-marker { display: none; }
.spoiler > summary:hover { background: var(--ink-3); }
.spoiler__badge {
flex: none;
padding: 0.2rem var(--sp-3);
background: var(--magenta);
color: var(--ink);
font-family: var(--f-mono);
font-size: 0.625rem;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.spoiler__label {
font-family: var(--f-mono);
font-size: var(--fs-xs);
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--paper-dim);
}
.spoiler > summary::after {
content: "reveal";
margin-left: auto;
font-family: var(--f-mono);
font-size: 0.625rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--acid);
}
.spoiler[open] > summary::after { content: "hide"; }
.spoiler__body { padding: 0 var(--sp-5) var(--sp-5); border-top: 1px solid var(--line); padding-top: var(--sp-5); }
/* -------------------------- BEYOND THE SERIES -------------------------- */
.section--beyond {
--tex-op: 0.26;
}
.notes { display: grid; gap: var(--sp-4); counter-reset: note; }
@media (min-width: 60rem) { .notes { grid-template-columns: repeat(3, 1fr); } }
.note {
position: relative;
padding: var(--sp-6) var(--sp-5) var(--sp-5);
background: var(--ink-2);
border: 1px solid var(--line);
counter-increment: note;
transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.note::before {
content: counter(note, decimal-leading-zero);
position: absolute;
top: var(--sp-3);
right: var(--sp-4);
font-family: var(--f-display);
font-size: var(--fs-lg);
line-height: 1;
color: var(--line-2);
transition: color var(--dur) var(--ease);
}
.note:hover { background: var(--ink-3); border-color: var(--line-2); }
.note:hover::before { color: var(--tan-dark); }
.note__tag {
font-family: var(--f-mono);
font-size: var(--fs-xs);
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--acid);
margin-bottom: var(--sp-3);
}
.note h3 { font-size: var(--fs-md); margin-bottom: var(--sp-3); color: var(--tan); max-width: 22ch; }
.note p { font-size: var(--fs-sm); color: var(--paper-dim); }
/* ------------------------------ FOOTER --------------------------------- */
.site-footer {
border-top: 1px solid var(--line);
background-color: var(--ink-2);
--tex-op: 0.18;
}
.site-footer__inner {
max-width: var(--wrap);
margin-inline: auto;
padding: var(--sp-8) var(--pad) var(--sp-7);
display: grid;
gap: var(--sp-7);
}
@media (min-width: 56rem) {
/* Sources is the tallest block, so it takes the full-height column and the
two shorter blocks stack beside it. Keeps the footer from ending in a
large empty quarter. */
.site-footer__inner { grid-template-columns: 1.05fr 1fr; gap: var(--sp-7) var(--sp-8); align-items: start; }
.disclaimer { grid-column: 1; grid-row: 1; }
.credits { grid-column: 1; grid-row: 2; }
.sources { grid-column: 2; grid-row: 1 / span 2; }
}
.site-footer h2 {
font-family: var(--f-mono);
font-size: var(--fs-xs);
font-weight: 500;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--acid);
margin-bottom: var(--sp-4);
}
.disclaimer p { font-size: var(--fs-sm); color: var(--paper-dim); max-width: 60ch; }
.disclaimer p + p { margin-top: var(--sp-3); }
.disclaimer strong { color: var(--paper); }
.sources__intro { font-size: var(--fs-sm); color: var(--paper-mute); margin-bottom: var(--sp-3); }
.sources ul { display: grid; }
.sources li { font-size: var(--fs-sm); padding-left: var(--sp-4); position: relative; }
/* Padded to keep each link's tap target at least 24px tall. */
.sources a { display: inline-block; padding-block: var(--sp-2); }
.sources li::before {
content: "";
position: absolute;
/* Link padding-top plus half the 1.6 line-height, so the dash centres on the
first text line at any font size rather than at a fixed offset. */
left: 0; top: calc(var(--sp-2) + 0.8em);
width: 5px; height: 1px;
background: var(--paper-mute);
}
/* Image credits: one texture per row, subject first so the list scans. */
.credits ul { display: grid; gap: var(--sp-3); }
.credits li {
display: grid;
gap: var(--sp-1);
padding-top: var(--sp-3);
border-top: 1px solid var(--line);
font-size: var(--fs-xs);
line-height: 1.7;
}
.credits__what {
font-family: var(--f-mono);
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--tan);
}
.credits__who { color: var(--paper-mute); }
.credits a { display: inline-block; padding-block: 2px; }
.site-footer__base {
max-width: var(--wrap);
margin-inline: auto;
padding: var(--sp-4) var(--pad) var(--sp-6);
border-top: 1px solid var(--line);
font-family: var(--f-mono);
font-size: var(--fs-xs);
color: var(--paper-mute);
display: flex;
flex-wrap: wrap;
gap: var(--sp-2) var(--sp-3);
align-items: center;
}
.site-footer__base > span:first-child { color: var(--magenta-txt); }
/* --------------------------- SCROLL REVEAL ----------------------------- */
/* Hidden state applies ONLY when JS is running (html.js) — a no-JS visitor
and any browser that never fires the observer sees fully visible content. */
.js [data-reveal] {
opacity: 0;
transform: translateY(18px);
transition:
opacity var(--dur-reveal) var(--ease),
transform var(--dur-reveal) var(--ease);
transition-delay: var(--reveal-delay, 0ms);
}
.js [data-reveal].is-revealed { opacity: 1; transform: none; }
.js [data-reveal] .meter__fill {
transform: scaleX(0);
transition: transform 900ms var(--ease) 180ms;
}
.js [data-reveal].is-revealed .meter__fill { transform: scaleX(1); }
/* --------------------------- REDUCED MOTION ---------------------------- */
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
*, *::before, *::after {
animation-duration: 1ms !important;
animation-iteration-count: 1 !important;
transition-duration: 1ms !important;
transition-delay: 0ms !important;
scroll-behavior: auto !important;
}
.js [data-reveal] { opacity: 1; transform: none; }
.js [data-reveal] .meter__fill,
.js [data-reveal].is-revealed .meter__fill { transform: none; }
.scroll-cue span { animation: none; transform: none; opacity: 0.7; }
/* The bar is driven by a scroll timeline, which the duration override above
would break. Decorative, so it simply goes away. */
.scroll-progress { display: none; }
.trait::after { transform: scaleY(1); opacity: 0; }
.trait:hover::after { opacity: 1; }
.ver:hover .ver__figure { translate: none; }
.tech:hover .tech__icon { rotate: none; }
}
/* ------------------------------ PRINT ---------------------------------- */
@media print {
body { background: #fff; color: #000; }
body::after, .hero__art, .site-nav, .scroll-cue, .salt-rule,
.scroll-progress, .ver__art { display: none; }
.tex::before, .section--arcs::after { display: none; }
.js [data-reveal] { opacity: 1 !important; transform: none !important; }
a { color: #000; }
}