diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml
index fd59dd3..8da5e6d 100644
--- a/.github/workflows/deploy-pages.yml
+++ b/.github/workflows/deploy-pages.yml
@@ -29,10 +29,18 @@ jobs:
- name: Setup Pages
uses: actions/configure-pages@v5
+ # plans/ holds internal working notes. Publishing the repo root would
+ # serve them at /plans/reports/..., so stage only what the site needs.
+ - name: Stage site files
+ run: |
+ mkdir -p _site
+ cp index.html README.md LICENSE .nojekyll _site/
+ cp -r css js assets _site/
+
- name: Upload site artifact
uses: actions/upload-pages-artifact@v3
with:
- path: .
+ path: _site
- name: Deploy
id: deployment
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8cf9fb8
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,47 @@
+MIT License
+
+Copyright (c) 2026 tiennm99
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+SCOPE OF THIS LICENSE
+
+This licence covers only the original work in this repository: the HTML, CSS and
+JavaScript, and the hand-authored SVG artwork (the character portrait, favicon,
+salt motifs and decorative shapes).
+
+It does NOT cover:
+
+1. "Mob Psycho 100", Reigen Arataka, or any other element of the series. Those
+ are the property of ONE, Shogakukan and Bones. This repository is an
+ unofficial, non-commercial fan work, and no official artwork, screenshots or
+ logos are included in it. Short quotations from the series are used for
+ commentary and remain the property of their rights holders.
+
+2. The photographic textures in assets/textures/, which are third-party works
+ under their own licences:
+ - paper-grain.jpg — CC0 1.0, ambientCG / Lennart Demes
+ - city-night.jpg — Unsplash License, The Ordinary Moments
+ - bokeh-dust.jpg — Unsplash License, Rana Mia
+ paper-grain.jpg is a derivative: the CC0 source was tone-subtracted, its
+ detail amplified and highlight-clipped, then mirrored into a seamless tile.
+ CC0 permits this without restriction. Full attribution and source links are
+ in the page footer.
diff --git a/README.md b/README.md
index 43893d9..061a25f 100644
--- a/README.md
+++ b/README.md
@@ -9,14 +9,19 @@ no framework, no dependencies to install.
## What's on it
-Ten sections in one scroll: hero, profile, who he is, personality, an
-abilities panel that sets his four real skills against his four branded fake
-"techniques", quotes, the people around him, story arcs, and the credits.
+Ten numbered sections in one scroll — profile, who he is, origin story,
+personality, the look, an abilities panel that sets his four real skills against
+his four branded fake "techniques", quotes, the people around him, story arcs and
+trivia — then a credits footer.
-- **No anime imagery.** Every visual — the aura fields, ray burst, faceless
- suited figure, salt-splash dividers, favicon — is hand-authored CSS and inline
- SVG. There is not a single `` tag or raster file in the repo, which is
- deliberate: see [Licensing](#licensing-and-credits).
+- **No official artwork of the character, anywhere.** The portrait, aura, salt
+ motifs and favicon are hand-authored SVG and CSS, drawn for this page. The
+ portrait is a single SVG symbol whose fills read custom properties, so one
+ drawing re-colours itself for the manga and anime palettes in *The look*.
+- **Photographic textures are licensed stock**, used as low-opacity backdrops:
+ one CC0, two under the Unsplash License, each credited in the footer with
+ author and source. None of them depict the character or the series — see
+ [Licensing](#licensing-and-credits).
- **Content lives in the HTML**, not injected by JavaScript, so the page reads
fine with scripting off and is indexable.
- **Accessible by construction** — semantic landmarks, skip link, one `
`,
@@ -42,12 +47,13 @@ python -m http.server 8099
```
index.html # the whole page — all content is in the markup
-css/style.css # design tokens, layout, the CSS/SVG artwork
+css/style.css # design tokens, layout, texture machinery, CSS/SVG artwork
js/main.js # entry point; wires up the enhancements
js/scroll-reveal.js # IntersectionObserver reveals (reduced-motion aware)
js/nav-highlight.js # highlights the nav link for the section in view
assets/favicon.svg # original salt-splash mark
-plans/reports/ # research and build notes
+assets/textures/ # 3 licensed photographic backdrops (~84 KB total)
+plans/reports/ # research, build and review notes
.github/workflows/ # GitHub Pages deployment
```
@@ -68,16 +74,32 @@ This is a **fan work**. *Mob Psycho 100* and all its characters are the property
of **ONE, Shogakukan, and studio Bones**. This page is not affiliated with,
authorised by, or endorsed by any of them, and nothing here is for sale.
-No official art, screenshots or logos are used, reproduced or hotlinked — the
-artwork is original to this repo for exactly that reason. Character quotes are
-short excerpts used for commentary and attributed to Reigen.
+No official art, screenshots or logos are used, reproduced or hotlinked. Every
+depiction of the character in this repo is original vector work drawn for it —
+there is deliberately no image *file* of him at all, because a picture of a
+copyrighted character stays copyrighted no matter who draws or licenses it.
+Character quotes are short excerpts used for commentary and attributed to Reigen.
-Factual content was compiled from the sources credited in the page footer,
-chiefly the Mob Psycho 100 Wiki and Wikipedia. Claims that could not be
-corroborated were left out rather than guessed at, and quotes carry no episode
-or chapter citation because their exact provenance could not be verified — an
+The photographic textures in `assets/textures/` are licensed stock that has
+nothing to do with the series — aged paper under **CC0 1.0**
+(ambientCG/Lennart Demes via Wikimedia Commons), plus a
+night skyline and a bokeh/dust abstract under the **Unsplash
+License**. Per-image authors, licenses and source links are in the page footer
+and in `plans/reports/from-researcher-to-ui-designer-cc0-texture-manifest-report.md`.
+
+Factual content was compiled from the sources credited in the page footer — the
+Mob Psycho 100 Wiki and MyAnimeList for character details, Wikipedia and Anime
+News Network for production and cast credits, and a number of anime news and
+listicle sites for the rest. Those last ones are the weakest link, and they are
+why two claims turned out to be contested.
+
+Claims that could not be corroborated were left out rather than guessed at, ones
+that sources genuinely disagree on are shown as disagreeing, and quotes carry no
+episode or chapter citation because their provenance could not be verified — an
invented citation would be worse than none.
-The site's own code and original artwork are free to reuse. If you hold rights
-to the series and want something changed or taken down, open an issue or reach
-me at [github.com/tiennm99](https://github.com/tiennm99).
+The site's own code and original artwork are released under the MIT License (see
+`LICENSE`). That covers this repo's own work only — not the series, and not the
+third-party textures, which keep their own licences as credited above. If you
+hold rights to the series and want something changed or taken down, open an issue
+or reach me at [github.com/tiennm99](https://github.com/tiennm99).
diff --git a/assets/textures/bokeh-dust.jpg b/assets/textures/bokeh-dust.jpg
new file mode 100644
index 0000000..e072deb
Binary files /dev/null and b/assets/textures/bokeh-dust.jpg differ
diff --git a/assets/textures/city-night.jpg b/assets/textures/city-night.jpg
new file mode 100644
index 0000000..0bd9fa9
Binary files /dev/null and b/assets/textures/city-night.jpg differ
diff --git a/assets/textures/paper-grain.jpg b/assets/textures/paper-grain.jpg
new file mode 100644
index 0000000..8254748
Binary files /dev/null and b/assets/textures/paper-grain.jpg differ
diff --git a/css/style.css b/css/style.css
index 9d2067f..06eb579 100644
--- a/css/style.css
+++ b/css/style.css
@@ -1,28 +1,55 @@
/* =========================================================================
Reigen Arataka — unofficial fan tribute
- All visual art in this file is original hand-authored CSS/SVG.
+ 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 */
- --ink: #0c0c11; /* page base */
- --ink-2: #14141c; /* raised surface */
- --ink-3: #1d1d28; /* card surface */
- --line: #2c2c3a; /* hairline borders */
+ /* 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 (11.9:1 on --ink) */
- --paper-dim: #b8b2a6; /* secondary text (7.0:1 on --ink) */
- --paper-mute: #8f8a80; /* tertiary / fine print (4.6:1 on --ink) */
+ --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 / accent (5.6:1 on --ink) */
- --teal: #17d6c6; /* aura / links (10.4:1 on --ink) */
- --acid: #d8f24a; /* highlight (14.9:1 on --ink) */
- --tan: #d9b483; /* the suit (8.4:1 on --ink) */
+ --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;
@@ -54,6 +81,14 @@
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;
@@ -67,6 +102,7 @@
html {
-webkit-text-size-adjust: 100%;
scroll-behavior: smooth;
+ color-scheme: dark;
}
body {
@@ -79,6 +115,8 @@ body {
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. */
@@ -93,8 +131,15 @@ body::after {
opacity: 0.05;
}
-h1, h2, h3, h4 { margin: 0; line-height: 1.08; font-weight: 700; letter-spacing: -0.015em; }
+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; }
@@ -134,6 +179,83 @@ a:hover { color: var(--acid); }
}
.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;
@@ -153,6 +275,32 @@ a:hover { color: var(--acid); }
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;
@@ -171,6 +319,7 @@ a:hover { color: var(--acid); }
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; }
@@ -182,12 +331,13 @@ a:hover { color: var(--acid); }
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 once the
- nav fits on one line at >=56rem. */
+/* 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;
@@ -232,9 +382,12 @@ a:hover { color: var(--acid); }
: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; }
+ .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 { padding: 0; overflow-x: visible; }
+ .site-nav ul { overflow-x: visible; }
}
/* ------------------------------- HERO --------------------------------- */
@@ -249,7 +402,7 @@ a:hover { color: var(--acid); }
align-content: center;
}
-/* --- hero art: aura blobs + rays + abstract suited figure --- */
+/* --- hero art: photo bloom + aura blobs + rays + the portrait --- */
.hero__art {
position: absolute;
inset: 0;
@@ -257,6 +410,28 @@ a:hover { color: var(--acid); }
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 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 {
@@ -266,10 +441,10 @@ a:hover { color: var(--acid); }
background:
/* darkens the copy side */
linear-gradient(90deg,
- rgba(12,12,17,0.85) 0%, rgba(12,12,17,0.60) 32%,
- rgba(12,12,17,0.15) 55%, rgba(12,12,17,0) 70%),
+ 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.35) 0%, rgba(12,12,17,0) 40%);
+ linear-gradient(180deg, rgba(12,12,17,0.45) 0%, rgba(12,12,17,0) 40%);
}
.aura {
@@ -299,35 +474,62 @@ a:hover { color: var(--acid); }
.rays {
position: absolute;
right: -6rem;
- top: 50%;
+ 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: -3rem;
- bottom: -2rem;
- width: 15rem;
+ 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;
}
-.fg-jacket { fill: var(--tan); }
-.fg-lapel { fill: var(--tan-dark); }
-.fg-head { fill: var(--tan); }
-.fg-neck { fill: var(--tan-dark); }
-.fg-hair { fill: #4a3a2e; }
-.fg-shirt { fill: #26222b; }
-.fg-tie { fill: var(--magenta); }
-.fg-tie2 { fill: #c9265d; }
+.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) {
- .figure { width: 20rem; right: 2%; opacity: 0.72; }
- .rays { right: 0; width: 36rem; opacity: 0.3; }
+ .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) {
- .figure { width: 25rem; right: 6%; }
+ .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 --- */
@@ -343,8 +545,9 @@ a:hover { color: var(--acid); }
font-size: var(--fs-xs);
letter-spacing: 0.22em;
text-transform: uppercase;
- color: var(--magenta);
+ color: var(--magenta-txt);
margin-bottom: var(--sp-3);
+ width: fit-content;
}
.kicker--strike span {
position: relative;
@@ -369,7 +572,15 @@ a:hover { color: var(--acid); }
margin-bottom: var(--sp-3);
}
.hero__title span { display: block; }
-.hero__title span:last-child { color: var(--tan); }
+/* 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);
@@ -377,6 +588,7 @@ a:hover { color: var(--acid); }
letter-spacing: 0.28em;
color: var(--paper-dim);
margin-bottom: var(--sp-5);
+ width: fit-content;
}
.hero__claim {
@@ -391,7 +603,7 @@ a:hover { color: var(--acid); }
}
.hero__tagline {
- max-width: 46ch;
+ max-width: var(--hero-measure, 46ch);
color: var(--paper-dim);
margin-bottom: var(--sp-6);
}
@@ -424,19 +636,45 @@ a:hover { color: var(--acid); }
color: var(--acid);
}
.stat-chips li:has(.stat-chips__v--zero) { border-left-color: var(--magenta); }
-.stat-chips__v--zero { color: var(--magenta); }
+.stat-chips__v--zero { color: var(--magenta-txt); }
.hero__note {
- max-width: 52ch;
+ max-width: 56ch;
font-size: var(--fs-sm);
color: var(--paper-dim);
border-top: 1px solid var(--line);
padding-top: var(--sp-4);
}
-/* Below the wide breakpoint the suited figure 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. */
+/* 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;
@@ -453,10 +691,14 @@ a:hover { color: var(--acid); }
}
}
+/* 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: var(--pad);
+ 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);
@@ -505,6 +747,14 @@ a:hover { color: var(--acid); }
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);
@@ -513,11 +763,22 @@ a:hover { color: var(--acid); }
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);
+ color: var(--magenta-txt);
letter-spacing: 0.06em;
margin-bottom: var(--sp-2);
}
@@ -568,6 +829,8 @@ a:hover { color: var(--acid); }
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
@@ -588,8 +851,12 @@ a:hover { color: var(--acid); }
.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); }
+.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); }
@@ -601,7 +868,7 @@ a:hover { color: var(--acid); }
font-size: 3.6em;
line-height: 0.78;
padding: 0.06em 0.12em 0 0;
- color: var(--magenta);
+ color: var(--magenta-txt);
}
.pull {
@@ -615,6 +882,72 @@ a:hover { color: var(--acid); }
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;
@@ -626,11 +959,23 @@ a:hover { color: var(--acid); }
@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);
@@ -638,6 +983,149 @@ a:hover { color: var(--acid); }
}
.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); } }
@@ -648,7 +1136,12 @@ a:hover { color: var(--acid); }
background: var(--ink-2);
}
.panel--real { border-top: 4px solid var(--teal); }
-.panel--fake { border-top: 4px solid var(--magenta); }
+.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);
@@ -658,7 +1151,7 @@ a:hover { color: var(--acid); }
margin-bottom: var(--sp-2);
}
.panel--real .panel__tag { color: var(--teal); }
-.panel--fake .panel__tag { color: var(--magenta); }
+.panel--fake .panel__tag { color: var(--magenta-txt); }
.panel__title {
font-family: var(--f-display);
@@ -688,7 +1181,7 @@ a:hover { color: var(--acid); }
text-transform: uppercase;
color: var(--teal);
}
-.meter--zero .meter__val { color: var(--magenta); }
+.meter--zero .meter__val { color: var(--magenta-txt); }
.meter__track {
position: relative;
@@ -724,7 +1217,7 @@ a:hover { color: var(--acid); }
text-transform: uppercase;
max-width: 40ch;
}
-.punchline::first-line { color: var(--magenta); }
+.punchline::first-line { color: var(--magenta-txt); }
/* -------------------------- FAKE TECHNIQUES ---------------------------- */
.tech-list { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); }
@@ -732,7 +1225,9 @@ a:hover { color: var(--acid); }
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);
@@ -740,61 +1235,107 @@ a:hover { color: var(--acid); }
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);
+ 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:
- radial-gradient(60rem 40rem at 85% 0%, rgba(23,214,198,0.09), transparent 70%),
- radial-gradient(50rem 34rem at 0% 100%, rgba(255,61,127,0.09), transparent 70%);
- max-width: none;
+ 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;
}
-.section--quotes > * { max-width: var(--wrap); margin-inline: auto; }
.quote-stack { display: grid; gap: var(--sp-5); }
@media (min-width: 52rem) {
- .quote-stack { grid-template-columns: 1fr 1fr; }
- .quote--lead { grid-column: 1 / -1; }
+ .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-6) var(--sp-5) var(--sp-5);
- background: var(--ink-2);
+ 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), transform var(--dur) var(--ease);
+ transition:
+ border-color var(--dur) var(--ease),
+ background-color var(--dur) var(--ease),
+ box-shadow var(--dur) var(--ease);
}
-.quote:hover { border-color: var(--line-2); }
+/* 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: calc(var(--sp-5) * -0.1);
+ top: 0;
left: var(--sp-5);
translate: 0 -50%;
- width: 2.1rem;
- height: 1.7rem;
- color: var(--magenta);
- background: var(--ink-2);
- padding-inline: var(--sp-2);
+ 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.45;
+ line-height: 1.5;
color: var(--paper);
}
-.quote--lead blockquote p { font-size: var(--fs-lg); line-height: 1.4; }
-.quote--short blockquote p { font-size: var(--fs-lg); color: var(--acid); }
+.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); }
@@ -834,7 +1375,34 @@ a:hover { color: var(--acid); }
.rel p:last-child { font-size: var(--fs-sm); color: var(--paper-dim); }
/* ------------------------------- ARCS ---------------------------------- */
-.section--arcs { border-top: 1px solid var(--line); }
+/* 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 {
@@ -866,13 +1434,15 @@ a:hover { color: var(--acid); }
color: var(--teal);
margin-bottom: var(--sp-2);
}
-.arc--spoiler .arc__when { color: var(--magenta); }
+.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:last-child { max-width: var(--measure); color: var(--paper-dim); }
+.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: var(--ink-2); }
+.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 {
@@ -920,8 +1490,52 @@ a:hover { color: var(--acid); }
.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: var(--ink-2); }
+.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;
@@ -930,7 +1544,13 @@ a:hover { color: var(--acid); }
gap: var(--sp-7);
}
@media (min-width: 56rem) {
- .site-footer__inner { grid-template-columns: 1.15fr 1fr; gap: var(--sp-8); }
+ /* 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 {
@@ -961,6 +1581,25 @@ a:hover { color: var(--acid); }
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;
@@ -974,7 +1613,7 @@ a:hover { color: var(--acid); }
gap: var(--sp-2) var(--sp-3);
align-items: center;
}
-.site-footer__base > span:first-child { color: var(--magenta); }
+.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
@@ -1009,12 +1648,21 @@ a:hover { color: var(--acid); }
.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 { display: none; }
+ 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; }
}
diff --git a/index.html b/index.html
index e108c96..067570e 100644
--- a/index.html
+++ b/index.html
@@ -4,7 +4,7 @@
Reigen Arataka — Greatest Psychic of the 21st Century (Self-Proclaimed) | Fan Tribute
-
+
@@ -36,8 +36,83 @@
Skip to content
-
+
-
-