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 @@ - + + +
@@ -92,17 +172,12 @@ - - - - - - - - - - + + + + +
@@ -125,7 +200,10 @@

Fan-made tribute page. Not affiliated with the creators of Mob Psycho 100. - All artwork here is original CSS / SVG — no official images are used. + All character artwork here — the portrait, the aura, the salt motifs — is + original CSS / SVG drawn for this page. The photographic backdrops are + CC0 / Unsplash-licensed stock textures, credited in the footer. + No official Mob Psycho 100 images are used.

@@ -134,7 +212,7 @@ - +
@@ -149,20 +227,29 @@ Identity and production details for Reigen Arataka NameReigen Arataka 霊幻新隆 - Age28 + Age28–29 — sources differ + BirthdayOctober 10 + Height179 cm OccupationOwner and operator, Spirits and Such Consultation Office WorkplaceSpirits and Such Consultation Office — one small rented room AffiliationsMentor and employer to Mob (Shigeo Kageyama); works alongside Katsuya Serizawa First appearance (manga)Chapter 1 First appearance (anime)Season 1, Episode 1 — “Self-Proclaimed Psychic: Reigen Arataka ~And Mob~” (July 2016) Japanese voiceTakahiro Sakurai + English voiceChris Niosi
+ +

+ Age is given as 28 by the series wiki and as 29 by MyAnimeList, so both are shown rather + than one being picked. The height comes from MyAnimeList only; no second source was found + for it. +

- -
+ +

Who He Is

@@ -196,10 +283,49 @@
- -
+ +
+

Origin Story

+

Before the office, there was a desk he could not stand.

+
+ +
+
+

+ He started out a salaryman at an ordinary company. Nothing paranormal, nothing spiritual + — regular work at a regular firm. He quit because he was bored, and because he + disliked the job. +

+

+ Roughly four years before the story begins, he rented a small room and put a name on it: + Spirits and Such Consultation Office. There was no plan behind it. He took the room more + or less on impulse and started calling himself a psychic. +

+

+ Then a middle-schooler turned up as a client, looking for help with powers he could not + switch off. Reigen — who has no powers at all — took the job. +

+
+ The business exists because a bored office worker walked out and rented a room. The most + consequential thing he ever did was answer the door to a kid he could not possibly help + in the way advertised. +
+
+ +
+ +

Spirits and Such
Consultation Office

+

One rented room · no business plan · open about four years before the story starts

+
+
+
+ + +
+
+

Personality

Six traits, pulling in different directions at once.

@@ -232,10 +358,67 @@
- + +
+
+ +

The Look

+

+ The manga and the anime do not draw him the same way — and they do not quite write + him the same way either. Both figures below are this page’s own drawing, recoloured. +

+
+ +
+
+

In the manga

+
+ +
+

Brown hair, black tie

+
    + +
  • Hair — brown
  • +
  • Tie — black
  • +
+

+ Written more plainly as a manipulative con artist. The framing is more critical of him. +

+
+ + + +
+

In the anime

+
+ +
+

Reddish-blond hair, pink tie

+
    +
  • Hair — reddish-blond / golden
  • +
  • Tie — pink
  • +
+

+ Warmer and more likeable. The bombast is toned down and the charm is pushed forward. +

+
+
+ +
+

Shared by both versions

+
    +
  • Dark brown eyes
  • +
  • Dark grey business suit
  • +
  • Messy side-swept bangs
  • +
+
+
+ +
- +

What He Can Actually Do

Left column: real, verifiable, unglamorous. Right column: the show. @@ -292,7 +475,7 @@

-
+

Theatre

“Techniques”

Branded, named, and announced at volume. All of it is stagecraft for the client’s benefit.

@@ -327,10 +510,10 @@

- -
+ +
- +

In His Own Words

The fraud gives the best advice in the series.

@@ -346,12 +529,6 @@ -
- -

In the end, you’re the one who’s going to get hurt. When things go - south, it’s ok to run away and save yourself!

-
-

Being confident about your powers is fine, but you mustn’t get conceited. @@ -360,11 +537,23 @@

+ +

In the end, you’re the one who’s going to get hurt. When things go + south, it’s ok to run away and save yourself!

+
+ + +

Your life is your own, and it’s up to you to live it exactly the way you want.

+
+ +

Your life is your own. It’s okay to run away!

+
+

The truth behind one’s charm is kindness.

@@ -378,10 +567,10 @@

- +
- +

The People Around Him

One office, several strays.

@@ -395,10 +584,11 @@ the spine of his whole character.

  • -

    Colleague

    +

    Colleague · deputy director

    Katsuya Serizawa

    -

    Comes to work at Spirits and Such alongside Reigen, as the office — and Reigen — - get put back together on a healthier footing.

    +

    Hired during the rebuild and made deputy director of the office. Reigen cut his hair + himself — a small nudge toward standing on his own. The habitual + “Right, Serizawa?” is Reigen fishing for backup.

  • Uninvited

    @@ -419,17 +609,26 @@ man for the one thing he has that powers can’t buy.

  • -

    Client, then friend

    +

    Never actually hired

    Tome Kurata

    -

    Arrives as business and stays as a fixture in the orbit of the office.

    +

    She was never taken on. She installed herself as unpaid secretary and simply kept turning + up until it counted as a job. By the end the two of them needle each other like colleagues.

    +
  • +
  • +

    Client

    +

    Tsubomi Takane

    +

    Came to the office as a client with a friend and tested his credibility with a trick + question. Reigen spotted Mob’s photo on his own “satisfied customers” board + during the visit. No romantic angle on his side — he simply respected what she meant + to Mob.

  • - -
    + +
    - +

    Story Arcs

    The first two are spoiler-free. Everything after is folded away — open only what you @@ -462,10 +661,13 @@

    Separation

    Rock bottom

    -

    A public scandal drives Reigen and Mob apart. The press and public opinion turn on - him, and he ends up isolated and out of work. Stripped of the act, he has to look at how - hollow his life had become — and how much of it he had been outsourcing to a - fourteen-year-old.

    +

    A public scandal and an online pile-on drive Reigen and Mob apart — the split + following Reigen being dismissive about Mob’s social life. The press and public + opinion turn on him and he ends up isolated and out of work. Stripped of the act, he has + to look at how hollow his life had become without the kid in it, and how much of it he + had been outsourcing to a fourteen-year-old.

    +

    Second season episodes six and seven are widely treated as his arc + specifically rather than Mob’s.

    @@ -479,9 +681,10 @@

    Reunion & rebuild

    Doing it properly

    -

    Reigen and Mob reconcile. He reassesses how he has been living, brings Serizawa in, - and puts Spirits and Such back together on an honest footing — choosing actual - self-improvement over another, better lie.

    +

    Reigen and Mob reconcile. He reassesses how he has been living, brings Serizawa in as + a stabilising presence, and puts Spirits and Such back together on healthier terms + — choosing actual self-improvement over another, better lie. Still a bad boss. + Visibly a caring one.

    @@ -495,25 +698,60 @@

    Finale

    The measure of him

    -

    In the climactic confrontation Reigen is support, not spectacle. He never acquires a - single power. His arc resolves instead into something rarer in this genre: a stable, - caring adult that Mob can actually rely on.

    +

    In the climactic confrontation Reigen is support, not spectacle. While fused with + Mob’s power he briefly gains the ability to see spirits — borrowed, and + temporary. He never acquires a power of his own. His arc resolves instead into something + rarer in this genre: a stable, caring adult that Mob can actually rely on.

    - Arcs are listed in order of occurrence. Exact episode and chapter ranges are not stated here - because public sources did not confirm them consistently. + Arcs are listed in order of occurrence. Chapter ranges are not given, and the only episode + range stated is the one sources agree on — season two, episodes six and seven.

    + +
    +
    + +

    Beyond The Series

    +

    Three things that happened outside the story itself.

    +
    + +
      +
    • +

      Spin-off

      +

      He got a manga of his own

      +

      Reigen: The Man With a MAX Spirituality Level of 131 — a one-volume + anthology of short horror-comedy stories built around him. It ran on the MangaONE app from + 19 March 2018, and Dark Horse Comics released the English edition on + 15 December 2020.

      +
    • +
    • +

      Fandom

      +

      The most-favourited character

      +

      On MyAnimeList he is the most-favourited character in Mob Psycho 100 — + ahead of Mob himself. No count is printed here on purpose: the number moves constantly, and + anything committed to this page would be stale within the week.

      +
    • +
    • +

      Voice

      +

      The actor behind the bluster

      +

      Takahiro Sakurai, who voices him in Japanese, is a heavily awarded performer — the + Overseas Fans Award at the 6th Seiyu Awards in 2012 and the Personality Award at the 16th in + 2022, among others. Those awards belong to the actor, not to the character.

      +
    • +
    +
    +
    -