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