mirror of
https://github.com/tiennm99/reigen.git
synced 2026-08-04 05:22:51 +00:00
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.
1669 lines
52 KiB
CSS
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; }
|
|
}
|