mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-20 18:16:30 +03:00
* Give DESIGN.md a real type ramp so the design hook stops crying wolf The design hook fired on nearly every CSS file we touched. The cause was DESIGN.md's typography block: it declared seven named roles rather than a scale, and two of those roles used clamp(), which the extractor skipped outright. That left an allowlist of five sizes standing against the 86 distinct font sizes actually in use, so design-system-font-size flagged roughly 500 declarations. Editing any .astro page made it worse, because the companion-stylesheet scan re-reported the whole backlog. Extractor (cli/engine/design-system.mjs): - Read a typography.scale map as the enumerated ramp. - Read both clamp() endpoints as allowed sizes. These stay additive on purpose: clamp endpoints alone cannot switch the rule on, because a fully fluid system enumerates no discrete ramp and inferring one from its endpoints would flag every intermediate size. The existing abstention test still passes, and three new tests cover the added behavior. DESIGN.md: - Document a 19-step ramp, 8px through 72px at a 16px root. - Snap the five discrete role sizes onto ramp steps. This also fixes real drift. DESIGN.md claims to mirror kinpaku-tokens.css verbatim, but wordmark was 1.15rem in the CSS against 1.3rem documented, with tracking at 0.42em against 0.15em. Both are re-synced. Standardization, 64 declarations: - Six near-identical steps between 13.7px and 15.4px collapse onto 14 and 15. - .foundation-card-label, .designing-lane-mock-title and .designing-iterate-name each existed at two different sizes in two files. Now unified. - The wordmark rendered at four sizes (20.8, 18.4, 17, 16.8px). Now 18px, plus one deliberate smaller nav variant. Exemptions, for designs that are foreign on purpose: the antipattern-example fixtures, the neo-mirai case-study build, the periodic-table cell annotations in framework-viz.js (5 to 7px diagram geometry sitting at 2 to 3px offsets), and the .why-slop-* before-state card's Inter and gradient text. Verified by computed style across ten rendered pages: every element lands on a ramp step except clamp() values mid-interpolation, which is what fluid means. Full test suite and build validators pass. Generated provider output is deliberately left out; the sync workflow owns it. Prepared with AI assistance (Claude Code). Co-Authored-By: Claude <noreply@anthropic.com> * Validate clamp() endpoints in usage, not just when reading DESIGN.md Reading clamp endpoints as documented steps without also checking them in source left an asymmetry: `isAllowedFontSizeRaw` returned true for anything failing the px/rem literal test, so `clamp(99rem, 1vw, 200rem)` passed. That is how `.ptable-symbol` at `clamp(1.45rem, 1.8vw, 1.8rem)` stayed invisible until someone measured computed styles, which is not a check the hook can run. Fluid values are now judged on their min and max. The viewport term interpolates between them and is never a fixed step, so it is left alone. Endpoints that cannot be resolved, such as var() or calc() or em, abstain rather than guess. Findings name the offending endpoint and use it as the ignore-value, because the whole clamp string is not actionable on its own. Turning the check on surfaced 22 fluid declarations that had never been looked at. Three used hero sizes above the ramp's 72px cap (80, 83.2 and 88px) alongside the display role's documented 89.6px max, so the top of the ramp was genuinely incomplete. Added the 80 and 88 steps, which gives the display end consistent 8px increments instead of 48/56/64/72 plus an orphan at 89.6, and fixes two declarations outright. The other 20 are snapped by a stated rule: nearest step, ties toward the smaller step, endpoints already matching a documented fluid role left as-is, and where nearest-step would make a breakpoint override meet or exceed its base, the next smaller step so the override still reduces. That last case applies once, to .designing-page-title. Also narrows the framework-viz.js waiver. The periodic-table cell annotations now carry two `impeccable-disable-line` comments naming the reason, instead of a config entry wildcarding the whole file for the rule. Inline waivers travel with the code and cannot silence future drift elsewhere in that file. Verified at 420px, 900px and 1600px across seven pages. The pinned ends are fully on-ramp; the only off-ramp values at 900px are the vw term mid-interpolation, which is what fluid means. Prepared with AI assistance (Claude Code). Co-Authored-By: Claude <noreply@anthropic.com> * Address review: wordmark tracking picked the wrong side, stale ramp count Two review findings, both fair. Wordmark tracking (greptile, bugbot). This PR moved DESIGN.md's wordmark letterSpacing from 0.15em to 0.42em on the grounds that DESIGN.md claims to mirror kinpaku-tokens.css and the token read 0.42em. That was the wrong side to trust. `--ks-type-wordmark-track` has exactly one consumer, design-system.css:570, which is the specimen page. Every production lockup (.ks-wordmark, .kinpaku-chrome .site-header-brand-name, .footer-logo) hardcodes 0.15em, so 0.15em is what every visitor actually sees and what DESIGN.md already documented correctly before this PR touched it. Reverted the doc to 0.15em and moved the token to 0.15em as well, so the specimen now renders the same lockup as production instead of a wider one nothing else uses. Verified by computed style: header and specimen both report 18px with 2.7px tracking. No production visual change. Stale ramp count (copilot). The sidecar described an "18-step ramp, 8px through 72px". It went stale twice inside this PR, once when the 8 step was added and again when 80 and 88 were added for the hero display sizes. It is 21 steps, 8px through 88px. Prepared with AI assistance (Claude Code). Co-Authored-By: Claude <noreply@anthropic.com> * Strip !important from the font-size ignore value Follow-on from the waiver wiring in the hook branch. The ignoreValue is what a `hooks ignore-value` waiver has to match, and `font-size: 1.4rem !important` emitted `1.4rem !important` while a plain declaration emitted `1.4rem`. Once font-size is a direct-value rule, that means the same size needs two different waivers depending on whether it carries a priority marker. font-family already strips the marker before matching, and there is a test for that. font-size now does the same. The snippet still shows the declaration as authored. Prepared with AI assistance (Claude Code). Co-Authored-By: Claude <noreply@anthropic.com> * Have the wordmark rules consume their tokens instead of copying the values Follow-up to the tracking fix, and the residual half of what the reviewers were pointing at. `.ks-wordmark` and the kinpaku chrome lockup each repeated `1.125rem` and `0.15em` literally rather than reading `--ks-type-wordmark-size` and `--ks-type-wordmark-track`. That duplication is exactly how the token drifted to 0.42em while every production lockup stayed at 0.15em and nobody noticed, which is the confusion that started this thread. The values already agree, so this is a no-op visually and is verified as such: computed styles across the home, design-system, docs and changelog pages all still report 18px with 2.7px tracking. What changes is that there is now one place to edit, so the next tracking change cannot silently apply to the specimen page alone. Prepared with AI assistance (Claude Code). Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
2204 lines
64 KiB
CSS
2204 lines
64 KiB
CSS
/* ============================================================================
|
||
Homepage rebuild: hero + slop teaser
|
||
Built to match the /design-system aesthetic (neo kinpaku).
|
||
Loaded after home-kinpaku.css so these rules win on the rebuilt sections.
|
||
|
||
All brand tokens (--ks-kinpaku, --ks-kinpaku-pale, --ks-patina, etc.) come
|
||
from kinpaku-tokens.css via home-kinpaku.css's import — no duplicate
|
||
declarations here.
|
||
============================================================================ */
|
||
|
||
|
||
/* ============================================================================
|
||
HERO REBUILD
|
||
============================================================================ */
|
||
|
||
/* The header overlays the hero (see .home-kinpaku .site-header below), so the
|
||
hero fills the full viewport and its art runs to the very top, behind the
|
||
transparent nav — the /design-system topbar treatment. */
|
||
.hero-rebuild {
|
||
position: relative;
|
||
/* Leave ~115px of the testimonials section visible above the fold
|
||
(dotted divider + first row's top edge). Without this the hero
|
||
fills the entire viewport and the testimonials read as out of sight. */
|
||
min-height: calc(100svh - 115px);
|
||
display: grid;
|
||
align-items: center;
|
||
/* Top padding clears the fixed (overlay) header (97px). On short viewports the
|
||
hero grows to fit its content and pins it at this padding-top, so the demo
|
||
never tucks under the nav; on tall viewports the content still centers. */
|
||
padding: 113px 0 96px;
|
||
overflow: hidden;
|
||
/* Hairline divider into the testimonials below, matching the testimonial
|
||
card border color. */
|
||
border-bottom: 1px solid oklch(0.64 0 0 / 0.22);
|
||
}
|
||
|
||
/* Header sits on top of the hero image like the /design-system topbar:
|
||
transparent over the hero, then glassing in as you scroll. Pinned (fixed) so
|
||
it overlays the hero without taking flow space. Overrides the sticky light
|
||
bar from sub-pages.css.
|
||
|
||
--hp is the scroll progress (0 at the top → 1 once past the hero top), set
|
||
px-by-px from app.js, so the glass interpolates continuously rather than
|
||
snapping. */
|
||
.home-kinpaku .site-header {
|
||
--hp: 0;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background: oklch(8% 0.006 95 / calc(0.72 * var(--hp)));
|
||
border-bottom: 1px solid oklch(78% 0.12 82 / calc(0.18 * var(--hp)));
|
||
-webkit-backdrop-filter: saturate(calc(1 + 0.3 * var(--hp))) blur(calc(16px * var(--hp)));
|
||
backdrop-filter: saturate(calc(1 + 0.3 * var(--hp))) blur(calc(16px * var(--hp)));
|
||
}
|
||
|
||
/* The wide-viewport seam treatment is light-only and lives in light-mode.css.
|
||
Dark mode needs none: the cream nav text reads fine over the darker seam. */
|
||
|
||
.hero-rebuild-art {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 0;
|
||
background:
|
||
url("/assets/neo-kinpaku/candidates/finalists/m-01-v2-01.png") center / cover no-repeat;
|
||
filter: saturate(1.2) contrast(1.08);
|
||
}
|
||
|
||
/* Mirror .site-content's container math so the hero text aligns horizontally
|
||
with every other section's content edge. */
|
||
.hero-rebuild-container {
|
||
position: relative;
|
||
z-index: 1;
|
||
max-width: 1500px;
|
||
margin: 0 auto;
|
||
padding: 0 56px;
|
||
width: 100%;
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr) minmax(0, 0.95fr);
|
||
gap: 72px;
|
||
align-items: center;
|
||
}
|
||
|
||
.hero-rebuild-left {
|
||
display: grid;
|
||
gap: 28px;
|
||
align-content: center;
|
||
max-width: 620px;
|
||
}
|
||
|
||
.home-kinpaku .hero-rebuild-title {
|
||
margin: 0;
|
||
color: var(--ks-champagne);
|
||
font-family: var(--ks-font-display);
|
||
font-weight: 100;
|
||
font-size: clamp(3.4rem, 6.5vw, 5.6rem);
|
||
line-height: 1.02;
|
||
letter-spacing: -0.01em;
|
||
}
|
||
|
||
.hero-rebuild-body {
|
||
margin: 0;
|
||
color: var(--ks-text);
|
||
font-size: 1rem;
|
||
line-height: 1.6;
|
||
font-weight: 300;
|
||
max-width: 56ch;
|
||
-webkit-font-smoothing: antialiased;
|
||
-moz-osx-font-smoothing: grayscale;
|
||
text-rendering: optimizeLegibility;
|
||
}
|
||
|
||
.hero-rebuild-actions {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 14px;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
/* Hero CTAs migrated to .ks-button.ks-button-primary / .ks-button-secondary
|
||
(see kinpaku-kit.css). The .hero-rebuild-arrow class is gone too — the
|
||
arrow now lives in .ks-button-arrow inside the button.
|
||
"How it works" is the kit ghost variant: plain champagne (white) text, no
|
||
border or fill, hovering to gold. */
|
||
|
||
.hero-rebuild-meta {
|
||
margin: 24px 0 0;
|
||
color: var(--ks-muted);
|
||
font-size: 0.86rem;
|
||
line-height: 1.5;
|
||
max-width: 52ch;
|
||
}
|
||
|
||
.hero-rebuild-logos {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 14px;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.home-kinpaku .hero-rebuild-logo {
|
||
display: inline-grid;
|
||
place-items: center;
|
||
width: 24px;
|
||
height: 24px;
|
||
border: 0;
|
||
border-radius: 0;
|
||
background: transparent;
|
||
opacity: 0.72;
|
||
transition: opacity 180ms var(--ks-ease), transform 180ms var(--ks-ease);
|
||
}
|
||
|
||
.home-kinpaku .hero-rebuild-logo img {
|
||
width: 22px;
|
||
height: 22px;
|
||
object-fit: contain;
|
||
opacity: 0.78;
|
||
filter: grayscale(1) saturate(0) invert(1) brightness(1.18) contrast(0.72);
|
||
transition: opacity 180ms var(--ks-ease), filter 180ms var(--ks-ease);
|
||
}
|
||
|
||
/* Pi's source SVG is already white-on-transparent, so the row's default
|
||
invert(1) filter would turn it black (invisible against the lacquer
|
||
hero). Skip the filter entirely; the asset is already on-brand. */
|
||
.home-kinpaku .hero-rebuild-logo[data-tooltip="Pi"] img {
|
||
filter: none;
|
||
}
|
||
|
||
.home-kinpaku .hero-rebuild-logo:hover,
|
||
.home-kinpaku .hero-rebuild-logo:focus-visible {
|
||
opacity: 1;
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.home-kinpaku .hero-rebuild-logo:hover img,
|
||
.home-kinpaku .hero-rebuild-logo:focus-visible img {
|
||
opacity: 1;
|
||
filter: none;
|
||
}
|
||
|
||
/* GitHub Copilot and Codex CLI logos are dark monochrome glyphs. The row's
|
||
default invert filter makes them readable; removing it on hover would put
|
||
them back to dark-on-dark, which is invisible. Keep them inverted on
|
||
hover — they don't have a brand color to restore. */
|
||
.home-kinpaku .hero-rebuild-logo[data-tooltip="GitHub Copilot"]:hover img,
|
||
.home-kinpaku .hero-rebuild-logo[data-tooltip="GitHub Copilot"]:focus-visible img,
|
||
.home-kinpaku .hero-rebuild-logo[data-tooltip="Codex CLI"]:hover img,
|
||
.home-kinpaku .hero-rebuild-logo[data-tooltip="Codex CLI"]:focus-visible img {
|
||
filter: grayscale(1) saturate(0) invert(1) brightness(1.18);
|
||
}
|
||
|
||
/* Right column hosts the existing split comparison */
|
||
.hero-rebuild-right {
|
||
position: relative;
|
||
width: 100%;
|
||
max-width: 720px;
|
||
justify-self: end;
|
||
}
|
||
|
||
/* The .home-kinpaku .split-container rule caps width/height for the magazine
|
||
spread. Inside the hero rebuild we want the demo to fill the right column,
|
||
so override both caps here. Higher specificity wins. */
|
||
.home-kinpaku .hero-rebuild-right .split-comparison {
|
||
max-width: 100%;
|
||
}
|
||
.home-kinpaku .hero-rebuild-right .split-container {
|
||
max-width: 100%;
|
||
height: 440px;
|
||
}
|
||
|
||
/* problem-section.css applies @keyframes splitEntry to every .split-divider
|
||
with a baked-in skewX(-10deg) in the from/to transforms. That keyframe
|
||
transform overrides our flat translateX(-50%) for the 0.9s animation
|
||
window, producing a visible "skewed-then-corrects" effect on first paint.
|
||
Disable the animation here — the home kinpaku divider doesn't need an
|
||
entry effect. */
|
||
.home-kinpaku .hero-rebuild-right .split-divider {
|
||
animation: none;
|
||
}
|
||
|
||
/* The category labels under the demo ("Generic AI output" / "With Impeccable")
|
||
need to stay readable on the vibrant kinpaku-leaf hero. Localized frosted
|
||
pills behind each label only — backdrop stays full-color elsewhere. */
|
||
.home-kinpaku .hero-rebuild-right .split-labels {
|
||
margin-top: 18px;
|
||
}
|
||
|
||
.home-kinpaku .hero-rebuild-right .split-label-item {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 9px;
|
||
padding: 7px 14px;
|
||
background: oklch(4% 0.004 95 / 0.72);
|
||
backdrop-filter: blur(8px) saturate(1.4);
|
||
-webkit-backdrop-filter: blur(8px) saturate(1.4);
|
||
border: 1px solid var(--ks-rule);
|
||
border-radius: 2px;
|
||
font-size: 0.72rem;
|
||
letter-spacing: 0.14em;
|
||
font-weight: 500;
|
||
color: var(--ks-champagne);
|
||
}
|
||
|
||
.home-kinpaku .hero-rebuild-right .split-label-item[data-point="after"] {
|
||
color: var(--ks-kinpaku);
|
||
border-color: oklch(78% 0.12 82 / 0.45);
|
||
}
|
||
|
||
@media (max-width: 1080px) {
|
||
.hero-rebuild { padding: 108px 36px 80px; } /* keep top clear of the fixed header */
|
||
/* minmax(0, 1fr), not 1fr: a plain 1fr track has an `auto` (min-content)
|
||
floor, so the demo's 460px-wide browser frame held the column wider than a
|
||
phone viewport and the hero clipped. minmax(0,…) lets it shrink and the
|
||
frame clips its own content (overflow:hidden) instead. */
|
||
.hero-rebuild-container { grid-template-columns: minmax(0, 1fr); gap: 48px; }
|
||
.hero-rebuild-right { justify-self: stretch; max-width: 100%; }
|
||
.home-kinpaku .hero-rebuild-right .split-container { height: 380px; }
|
||
}
|
||
|
||
@media (max-width: 720px) {
|
||
.hero-rebuild { padding: 92px 24px 64px; } /* clear the compact mobile header */
|
||
/* The hero's 24px gutter is the mobile margin now; the container's desktop
|
||
56px padding stacked on top of it (160px of chrome) starved the content and
|
||
forced the grid track wider than the viewport. Drop it on mobile. */
|
||
.hero-rebuild-container { padding-left: 0; padding-right: 0; }
|
||
/* Title keeps its design-system clamp (floors at 3.4rem ≈ 54px here): the
|
||
Alumni Sans display face is narrow, so "interfaces" stays well within a
|
||
320px+ viewport. No mobile shrink — at 54px against 16.8px body it holds a
|
||
~3.2× hero hierarchy; shrinking it made the heading read smaller than body. */
|
||
.hero-rebuild-actions { flex-direction: column; align-items: stretch; }
|
||
.home-kinpaku .hero-rebuild-right .split-container { height: 320px; }
|
||
/* The hotel mock is authored ~460px wide; in the narrow mobile frame its full
|
||
nav (logo + 4 links + Book) overran the right edge and clipped mid-word.
|
||
Collapse to logo + Book — the realistic narrow-viewport nav, and it keeps
|
||
the hero (the picked element) as the demo's clear subject. */
|
||
.hero-live-demo .hotel-nav-links { display: none; }
|
||
}
|
||
|
||
/* ============================================================================
|
||
CATCH THE SLOP TEASER
|
||
============================================================================ */
|
||
|
||
.slop-teaser-section {
|
||
padding: 120px 0;
|
||
}
|
||
|
||
.slop-teaser-head {
|
||
display: grid;
|
||
gap: 18px;
|
||
max-width: 760px;
|
||
margin-bottom: 64px;
|
||
}
|
||
|
||
.slop-teaser-eyebrow {
|
||
margin: 0;
|
||
color: var(--ks-kinpaku);
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.72rem;
|
||
letter-spacing: 0.28em;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.home-kinpaku .slop-teaser-title {
|
||
margin: 0;
|
||
color: var(--ks-champagne);
|
||
font-family: var(--ks-font-display);
|
||
font-weight: var(--ks-type-headline-weight);
|
||
font-size: clamp(2.6rem, 4vw, 3.4rem);
|
||
line-height: 1.04;
|
||
letter-spacing: -0.005em;
|
||
}
|
||
|
||
.slop-teaser-body {
|
||
margin: 0;
|
||
color: var(--ks-text);
|
||
font-size: 1.02rem;
|
||
line-height: 1.6;
|
||
/* Weight 400 + explicit smoothing for the same reason .hero-rebuild-body
|
||
uses them: Avenir Next Light (weight 300) isn't installed on every system,
|
||
so the browser synthesizes it from the regular face and the anti-aliasing
|
||
goes splotchy. Weight 400 with explicit smoothing renders cleanly. */
|
||
font-weight: 400;
|
||
max-width: 60ch;
|
||
-webkit-font-smoothing: antialiased;
|
||
-moz-osx-font-smoothing: grayscale;
|
||
text-rendering: optimizeLegibility;
|
||
}
|
||
|
||
.slop-teaser-numerics {
|
||
display: inline-block;
|
||
margin-left: 8px;
|
||
color: var(--ks-muted);
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.82rem;
|
||
letter-spacing: 0.04em;
|
||
}
|
||
|
||
/* Gallery: horizontal scroller of antipattern thumbnails */
|
||
.slop-teaser-gallery {
|
||
position: relative;
|
||
margin: 0 0 56px;
|
||
}
|
||
|
||
.slop-teaser-track {
|
||
display: flex;
|
||
gap: 18px;
|
||
overflow-x: auto;
|
||
scroll-snap-type: x mandatory;
|
||
scrollbar-width: none;
|
||
padding: 4px 4px 18px;
|
||
margin: 0 -4px;
|
||
}
|
||
|
||
.slop-teaser-track::-webkit-scrollbar { display: none; }
|
||
|
||
.home-kinpaku .slop-teaser-card {
|
||
flex: 0 0 320px;
|
||
scroll-snap-align: start;
|
||
display: grid;
|
||
grid-template-rows: auto auto;
|
||
text-decoration: none;
|
||
background: var(--ks-lacquer-raised);
|
||
border: 1px solid var(--ks-rule);
|
||
border-radius: 2px;
|
||
overflow: hidden;
|
||
transition: border-color 180ms var(--ks-ease), transform 220ms var(--ks-ease);
|
||
position: relative;
|
||
}
|
||
|
||
.home-kinpaku .slop-teaser-card:hover {
|
||
border-color: var(--ks-kinpaku);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.slop-teaser-thumb {
|
||
position: relative;
|
||
aspect-ratio: 16 / 10;
|
||
overflow: hidden;
|
||
background: oklch(13% 0.008 290);
|
||
}
|
||
|
||
.slop-teaser-thumb img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
object-position: top left;
|
||
display: block;
|
||
transition: transform 360ms var(--ks-ease);
|
||
}
|
||
|
||
.home-kinpaku .slop-teaser-card:hover .slop-teaser-thumb img {
|
||
transform: scale(1.03);
|
||
}
|
||
|
||
/* Marker scribble across the thumbnail, evoking "caught" */
|
||
.slop-teaser-thumb::after {
|
||
content: "";
|
||
position: absolute;
|
||
inset: 0;
|
||
background:
|
||
repeating-linear-gradient(135deg, transparent 0 22px, oklch(58% 0.15 35 / 0.04) 22px 23px),
|
||
linear-gradient(180deg, transparent 60%, oklch(4% 0.004 95 / 0.55));
|
||
pointer-events: none;
|
||
}
|
||
|
||
.slop-teaser-meta {
|
||
display: grid;
|
||
grid-template-columns: auto 1fr auto;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 14px 16px;
|
||
border-top: 1px solid var(--ks-rule);
|
||
}
|
||
|
||
.slop-teaser-cat {
|
||
color: var(--ks-kinpaku);
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.62rem;
|
||
letter-spacing: 0.22em;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.home-kinpaku .slop-teaser-card strong {
|
||
color: var(--ks-champagne);
|
||
font-family: var(--ks-font);
|
||
font-weight: 400;
|
||
font-size: 1rem;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.slop-teaser-flag {
|
||
color: oklch(58% 0.15 35);
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.62rem;
|
||
letter-spacing: 0.22em;
|
||
border: 1px solid oklch(58% 0.15 35);
|
||
padding: 2px 8px;
|
||
border-radius: 2px;
|
||
}
|
||
|
||
/* Gallery nav buttons */
|
||
.slop-teaser-arrow {
|
||
position: absolute;
|
||
top: 38%;
|
||
width: 40px;
|
||
height: 40px;
|
||
display: grid;
|
||
place-items: center;
|
||
border: 1px solid var(--ks-rule);
|
||
background: oklch(8% 0.006 95 / 0.92);
|
||
color: var(--ks-kinpaku);
|
||
border-radius: 999px;
|
||
cursor: pointer;
|
||
transition: border-color 180ms var(--ks-ease), color 180ms var(--ks-ease);
|
||
}
|
||
|
||
.slop-teaser-arrow:hover { border-color: var(--ks-kinpaku); }
|
||
.slop-teaser-arrow svg { width: 12px; height: 20px; }
|
||
.slop-teaser-arrow--prev { left: -20px; }
|
||
.slop-teaser-arrow--next { right: -20px; }
|
||
|
||
@media (max-width: 720px) {
|
||
.slop-teaser-card { flex: 0 0 80%; }
|
||
.slop-teaser-arrow { display: none; }
|
||
}
|
||
|
||
.slop-teaser-actions {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 24px;
|
||
margin-top: 14px;
|
||
}
|
||
|
||
.home-kinpaku .slop-teaser-cta-primary {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 14px;
|
||
color: var(--ks-kinpaku);
|
||
font-size: 1rem;
|
||
font-weight: 400;
|
||
text-decoration: none;
|
||
padding-bottom: 4px;
|
||
border-bottom: 1px solid var(--ks-kinpaku);
|
||
transition: gap 180ms var(--ks-ease);
|
||
}
|
||
|
||
.home-kinpaku .slop-teaser-cta-primary:hover {
|
||
gap: 18px;
|
||
color: var(--ks-kinpaku);
|
||
}
|
||
|
||
.slop-teaser-cta-primary svg {
|
||
width: 16px;
|
||
height: 8px;
|
||
flex: none;
|
||
}
|
||
|
||
/* Scoped against the global .home-kinpaku code color override using the
|
||
chained selector (.home-kinpaku code.slop-teaser-cli has higher specificity
|
||
than .home-kinpaku code alone). Keeps the CLI block in the softer code-gold
|
||
so the line reads as a terminal command, not a pumpkin headline. */
|
||
.home-kinpaku code.slop-teaser-cli {
|
||
color: var(--ks-code-block-fg);
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.86rem;
|
||
background: var(--ks-code-block-bg);
|
||
padding: 10px 16px;
|
||
border: 1px solid var(--ks-code-block-border);
|
||
border-radius: var(--ks-code-block-radius);
|
||
user-select: all;
|
||
}
|
||
|
||
@media (max-width: 1080px) {
|
||
.slop-teaser-section { padding: 80px 0; }
|
||
.slop-teaser-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||
}
|
||
|
||
@media (max-width: 720px) {
|
||
.slop-teaser-section { padding: 64px 0; }
|
||
.slop-teaser-grid { grid-template-columns: 1fr; }
|
||
.slop-teaser-actions { flex-direction: column; align-items: stretch; }
|
||
.slop-teaser-cli { text-align: center; }
|
||
}
|
||
|
||
/* ============================================================================
|
||
LIVE SECTION — right-aligned intro
|
||
The section header and lead paragraph sit flush right so the heavy demo
|
||
block below reads as left-anchored to the page, with the intro language
|
||
right-anchored above it. (Language section keeps its default left-align;
|
||
Get Started below is centered.)
|
||
============================================================================ */
|
||
|
||
.home-kinpaku .live-section .section-header {
|
||
text-align: right;
|
||
justify-items: end;
|
||
}
|
||
.home-kinpaku .live-section .section-lead {
|
||
margin-left: auto;
|
||
text-align: right;
|
||
}
|
||
|
||
/* ============================================================================
|
||
GET STARTED REBUILD
|
||
============================================================================ */
|
||
|
||
.downloads-rebuild {
|
||
padding: 120px 0;
|
||
}
|
||
|
||
/* The card wrapper holds everything: header, install command, examples,
|
||
extras. Plinth surface. The --card-* tokens are kept so inner rules
|
||
reference them once and we can re-theme the whole card in one place if
|
||
needed later. */
|
||
.downloads-rebuild-card {
|
||
--card-fg: var(--ks-text);
|
||
--card-fg-strong: var(--ks-champagne);
|
||
--card-fg-muted: var(--ks-muted);
|
||
--card-accent: var(--ks-kinpaku);
|
||
--card-cmd-bg: var(--ks-code-block-bg);
|
||
--card-cmd-fg: var(--ks-code-block-fg);
|
||
--card-copy-border: var(--ks-rule);
|
||
--card-copy-fg: var(--ks-kinpaku);
|
||
--card-copy-hover-bg: oklch(77% 0.14 82 / 0.08);
|
||
--card-link-color: var(--ks-kinpaku);
|
||
--card-link-underline: var(--ks-kinpaku);
|
||
--card-link-secondary: var(--ks-muted);
|
||
|
||
/* Command-frame accents. The install box is the primary CTA and carries
|
||
kinpaku; the update box carries patina, which in this system reads as the
|
||
"improved / updated" state. Both re-theme in light-mode.css. */
|
||
--card-cmd-border: var(--ks-kinpaku);
|
||
--card-prompt: var(--ks-kinpaku);
|
||
--card-cmd-border-update: oklch(70% 0.12 188 / 0.5);
|
||
--card-prompt-update: var(--ks-patina);
|
||
|
||
/* Width of the accent's reserved right column. Content padding-right
|
||
matches this so the accent never overlaps text. */
|
||
--card-accent-w: 280px;
|
||
|
||
position: relative;
|
||
max-width: 1080px;
|
||
margin: 0 auto;
|
||
padding: 80px var(--card-accent-w) 80px 56px;
|
||
border-radius: 2px;
|
||
background: oklch(0.17 0 0);
|
||
color: var(--card-fg);
|
||
text-align: left;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* Oxidation edge accent — moved here from the footer. Full-bleed on the
|
||
right, full opacity (much more glorious than the 0.6 it carried in the
|
||
footer). Content gets dedicated padding-right via --card-accent-w above,
|
||
so the accent and the content never overlap. */
|
||
.downloads-rebuild-card::before {
|
||
content: "";
|
||
position: absolute;
|
||
right: 0;
|
||
top: 0;
|
||
width: var(--card-accent-w);
|
||
height: 100%;
|
||
/* `cover` instead of `contain` so the image fills the card's full height —
|
||
the source PNG is 1:2 but the card aspect is more like 1:5, so `contain`
|
||
bottom-anchored a half-height render. Right-anchored, vertically centered
|
||
overflow goes off the left edge (the gold streak lives on the right side
|
||
of the image, so cropping there is fine). */
|
||
background: url("/assets/neo-kinpaku/oxidation-edge-accent.png") right center / cover no-repeat;
|
||
opacity: 1;
|
||
pointer-events: none;
|
||
z-index: 0;
|
||
}
|
||
|
||
/* Make sure the inner content stacks above the accent. */
|
||
.downloads-rebuild-card > * {
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
/* Narrow viewports — the accent's reserved column would crush content.
|
||
Drop the accent below the lg breakpoint and restore symmetric padding. */
|
||
@media (max-width: 980px) {
|
||
.downloads-rebuild-card {
|
||
padding: 64px 40px;
|
||
}
|
||
.downloads-rebuild-card::before { display: none; }
|
||
}
|
||
|
||
.downloads-rebuild-head {
|
||
display: grid;
|
||
gap: 18px;
|
||
max-width: 760px;
|
||
margin: 0 0 40px;
|
||
text-align: left;
|
||
justify-items: start;
|
||
}
|
||
|
||
.downloads-rebuild-eyebrow {
|
||
margin: 0;
|
||
color: var(--ks-kinpaku);
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.72rem;
|
||
letter-spacing: 0.28em;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-title {
|
||
margin: 0;
|
||
color: var(--card-fg-strong);
|
||
font-family: var(--ks-font-display);
|
||
font-weight: var(--ks-type-headline-weight);
|
||
font-size: clamp(2.6rem, 4vw, 3.4rem);
|
||
line-height: 1.04;
|
||
}
|
||
|
||
.downloads-rebuild-install {
|
||
display: grid;
|
||
gap: 22px;
|
||
margin: 0 0 48px;
|
||
width: 100%;
|
||
max-width: 600px;
|
||
min-width: 0;
|
||
text-align: left;
|
||
}
|
||
|
||
/* Each command is a labeled group: a small mono cap above its box. */
|
||
.downloads-rebuild-cmd-group {
|
||
display: grid;
|
||
gap: 9px;
|
||
min-width: 0;
|
||
}
|
||
|
||
.downloads-rebuild-cmd-label {
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.7rem;
|
||
font-weight: 500;
|
||
letter-spacing: 0.2em;
|
||
text-transform: uppercase;
|
||
color: var(--card-prompt);
|
||
}
|
||
.downloads-rebuild-cmd-label--update {
|
||
color: var(--card-prompt-update);
|
||
}
|
||
|
||
/* "Install via [impeccable] [marketplace] [skills.sh]" — the method selector
|
||
that replaces the static "Install" label. Compact inline tabs that reuse the
|
||
design-system ks-tab selected-state language (kinpaku text + underline on the
|
||
active tab) beside the mono "Install via" cap. Baseline alignment so the tab
|
||
text sits on the same line as the "INSTALL VIA" label; the tabs are
|
||
inline-block (not flex) so their text baseline is what the row aligns to, and
|
||
marks ride along via vertical-align without dragging the baseline. */
|
||
.downloads-rebuild-tabs {
|
||
display: flex;
|
||
align-items: baseline;
|
||
flex-wrap: wrap;
|
||
gap: 4px 12px;
|
||
}
|
||
.downloads-rebuild-tabs-label {
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.7rem;
|
||
font-weight: 500;
|
||
letter-spacing: 0.2em;
|
||
text-transform: uppercase;
|
||
color: var(--card-prompt);
|
||
margin-right: 2px;
|
||
}
|
||
.downloads-rebuild-tab {
|
||
display: inline-block;
|
||
border: 0;
|
||
background: transparent;
|
||
padding: 2px 0 4px;
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.84rem;
|
||
line-height: 1;
|
||
color: var(--ks-text-muted);
|
||
cursor: pointer;
|
||
border-bottom: 2px solid transparent;
|
||
transition: color 160ms var(--ks-ease), border-color 160ms var(--ks-ease);
|
||
}
|
||
.downloads-rebuild-tab:hover { color: var(--ks-champagne); }
|
||
|
||
/* Brand mark flags the marketplace tab as Claude-tied. Dimmed until the tab is
|
||
active so it reads as a quiet qualifier, not a CTA. vertical-align:middle
|
||
keeps it centered on the text without shifting the text baseline the row
|
||
aligns to. */
|
||
.downloads-rebuild-tab-logo {
|
||
display: inline-block;
|
||
width: 13px;
|
||
height: 13px;
|
||
margin-right: 5px;
|
||
vertical-align: middle;
|
||
opacity: 0.7;
|
||
transition: opacity 160ms var(--ks-ease);
|
||
}
|
||
svg.downloads-rebuild-tab-logo {
|
||
fill: var(--ks-champagne);
|
||
}
|
||
.downloads-rebuild-tab:hover .downloads-rebuild-tab-logo,
|
||
.downloads-rebuild-tab[aria-selected="true"] .downloads-rebuild-tab-logo {
|
||
opacity: 1;
|
||
}
|
||
|
||
/* Persistent "Recommended" cue on the impeccable tab — a small gold star, the
|
||
conventional best/featured glyph. Stays visible even when another method is
|
||
selected, so the blessed path is always legible, and reads in a fraction of
|
||
the width the spelled-out word needed. Accessible name lives on the SVG. */
|
||
.downloads-rebuild-tab-rec {
|
||
display: inline-block;
|
||
width: 11px;
|
||
height: 11px;
|
||
margin-left: 5px;
|
||
vertical-align: middle;
|
||
fill: var(--ks-kinpaku);
|
||
opacity: 0.9;
|
||
}
|
||
.downloads-rebuild-tab[aria-selected="true"] {
|
||
color: var(--ks-kinpaku);
|
||
border-bottom-color: var(--ks-kinpaku);
|
||
}
|
||
.downloads-rebuild-tab:focus-visible {
|
||
outline: 2px solid var(--ks-kinpaku);
|
||
outline-offset: 3px;
|
||
border-radius: 2px;
|
||
}
|
||
|
||
/* Per-method explainer under the install box — carries the "what you give up"
|
||
note for the skills.sh path and the Claude-Code-only caveat for marketplace. */
|
||
.downloads-rebuild-method-note {
|
||
margin: 9px 0 0;
|
||
max-width: 60ch;
|
||
font-size: 0.82rem;
|
||
line-height: 1.5;
|
||
color: var(--ks-text-muted);
|
||
}
|
||
.home-kinpaku .downloads-rebuild-method-note code {
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.9em;
|
||
color: var(--card-fg-muted);
|
||
}
|
||
.downloads-rebuild-method-note strong {
|
||
color: var(--ks-kinpaku);
|
||
font-weight: 600;
|
||
cursor: help;
|
||
}
|
||
|
||
/* Marketplace updates from a menu, not a shell command, so its Update slot is
|
||
quiet guidance text rather than a runnable box. */
|
||
.downloads-rebuild-update-menu {
|
||
margin: 0;
|
||
font-size: 0.92rem;
|
||
line-height: 1.5;
|
||
color: var(--card-fg-muted);
|
||
padding: 4px 0;
|
||
}
|
||
.home-kinpaku .downloads-rebuild-update-menu code {
|
||
font-family: var(--ks-mono);
|
||
color: var(--card-prompt-update);
|
||
}
|
||
|
||
/* Copilot is built in, so it isn't a tab — it's a quiet one-line aside right
|
||
under the method tabs, there to catch Copilot users BEFORE they copy an
|
||
install command they don't need. Deliberately NOT boxed: a bordered notice
|
||
competed with the real command boxes. Plain muted text + small glyph reads as
|
||
a footnote, not a step. */
|
||
.downloads-rebuild-copilot-note {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 7px;
|
||
margin: 6px 0 18px;
|
||
max-width: 62ch;
|
||
font-size: 0.8rem;
|
||
line-height: 1.5;
|
||
color: var(--ks-text-muted);
|
||
}
|
||
.downloads-rebuild-copilot-note-logo {
|
||
flex: none;
|
||
width: 12px;
|
||
height: 12px;
|
||
transform: translateY(1px);
|
||
fill: currentColor;
|
||
opacity: 0.85;
|
||
}
|
||
.home-kinpaku .downloads-rebuild-copilot-note strong {
|
||
color: var(--ks-champagne);
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* "Why one command, many builds" — a collapsed disclosure that visualizes the
|
||
impeccable source fanning out into a build tailored per harness. The branch
|
||
lines draw themselves in when the section opens; the targets fade up behind
|
||
them, staggered, so the eye follows source → target. */
|
||
.downloads-rebuild-branch {
|
||
margin: 16px 0 0;
|
||
}
|
||
.downloads-rebuild-branch-summary {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 7px;
|
||
cursor: pointer;
|
||
list-style: none;
|
||
font-size: 0.82rem;
|
||
color: var(--ks-text-muted);
|
||
transition: color 160ms var(--ks-ease);
|
||
}
|
||
.downloads-rebuild-branch-summary::-webkit-details-marker { display: none; }
|
||
.downloads-rebuild-branch-summary:hover { color: var(--ks-champagne); }
|
||
.downloads-rebuild-branch[open] > .downloads-rebuild-branch-summary {
|
||
color: var(--card-fg-strong);
|
||
margin-bottom: 14px;
|
||
}
|
||
.downloads-rebuild-branch-arrow {
|
||
flex: none;
|
||
transition: transform 180ms var(--ks-ease);
|
||
}
|
||
.downloads-rebuild-branch[open] > .downloads-rebuild-branch-summary .downloads-rebuild-branch-arrow {
|
||
transform: rotate(90deg);
|
||
}
|
||
|
||
.downloads-rebuild-branch-diagram {
|
||
display: block;
|
||
width: 100%;
|
||
max-width: 430px;
|
||
height: auto;
|
||
font-family: var(--ks-mono);
|
||
}
|
||
|
||
/* Connectors: hidden (fully dashed-out) until the section opens, then each
|
||
draws from the source node to its target. */
|
||
.downloads-rebuild-branch-line {
|
||
fill: none;
|
||
stroke: color-mix(in oklch, var(--ks-kinpaku) 55%, transparent);
|
||
stroke-width: 1.3;
|
||
stroke-dasharray: 230;
|
||
stroke-dashoffset: 230;
|
||
}
|
||
.downloads-rebuild-branch[open] .downloads-rebuild-branch-line {
|
||
animation: branch-draw 620ms var(--ks-ease) forwards;
|
||
}
|
||
.downloads-rebuild-branch[open] .downloads-rebuild-branch-line:nth-of-type(1) { animation-delay: 60ms; }
|
||
.downloads-rebuild-branch[open] .downloads-rebuild-branch-line:nth-of-type(2) { animation-delay: 130ms; }
|
||
.downloads-rebuild-branch[open] .downloads-rebuild-branch-line:nth-of-type(3) { animation-delay: 200ms; }
|
||
.downloads-rebuild-branch[open] .downloads-rebuild-branch-line:nth-of-type(4) { animation-delay: 270ms; }
|
||
.downloads-rebuild-branch[open] .downloads-rebuild-branch-line:nth-of-type(5) { animation-delay: 340ms; }
|
||
|
||
.downloads-rebuild-branch-root-box {
|
||
fill: color-mix(in oklch, var(--ks-kinpaku) 9%, transparent);
|
||
stroke: var(--ks-kinpaku);
|
||
stroke-width: 1.2;
|
||
}
|
||
.downloads-rebuild-branch-root-text {
|
||
fill: var(--ks-kinpaku);
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
}
|
||
.downloads-rebuild-branch-dot {
|
||
fill: var(--ks-kinpaku);
|
||
}
|
||
.downloads-rebuild-branch-name {
|
||
fill: var(--ks-champagne);
|
||
font-size: 11.5px;
|
||
font-weight: 600;
|
||
}
|
||
.downloads-rebuild-branch-tags {
|
||
fill: var(--ks-text-muted);
|
||
font-size: 8.6px;
|
||
letter-spacing: 0.01em;
|
||
}
|
||
/* The model-specific slop rules are the headline of this diagram, so the rows
|
||
that carry them light up gold while the plumbing tags stay muted. */
|
||
.downloads-rebuild-branch-tags--slop {
|
||
fill: var(--ks-kinpaku);
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* Targets fade up behind the drawing lines, staggered to match. */
|
||
.downloads-rebuild-branch-target {
|
||
opacity: 0;
|
||
transform: translateX(-5px);
|
||
}
|
||
.downloads-rebuild-branch[open] .downloads-rebuild-branch-target {
|
||
animation: branch-rise 460ms var(--ks-ease) forwards;
|
||
}
|
||
.downloads-rebuild-branch[open] .downloads-rebuild-branch-target:nth-of-type(1) { animation-delay: 220ms; }
|
||
.downloads-rebuild-branch[open] .downloads-rebuild-branch-target:nth-of-type(2) { animation-delay: 290ms; }
|
||
.downloads-rebuild-branch[open] .downloads-rebuild-branch-target:nth-of-type(3) { animation-delay: 360ms; }
|
||
.downloads-rebuild-branch[open] .downloads-rebuild-branch-target:nth-of-type(4) { animation-delay: 430ms; }
|
||
.downloads-rebuild-branch[open] .downloads-rebuild-branch-target:nth-of-type(5) { animation-delay: 500ms; }
|
||
|
||
.downloads-rebuild-branch-caption {
|
||
margin: 12px 0 0;
|
||
max-width: 56ch;
|
||
font-size: 0.8rem;
|
||
line-height: 1.5;
|
||
color: var(--ks-text-muted);
|
||
}
|
||
.home-kinpaku .downloads-rebuild-branch-caption code {
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.9em;
|
||
color: var(--card-fg-muted);
|
||
}
|
||
|
||
@keyframes branch-draw {
|
||
to { stroke-dashoffset: 0; }
|
||
}
|
||
@keyframes branch-rise {
|
||
to { opacity: 1; transform: translateX(0); }
|
||
}
|
||
|
||
/* Reduced motion: skip the draw/rise, show the finished diagram immediately. */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.downloads-rebuild-branch[open] .downloads-rebuild-branch-line {
|
||
animation: none;
|
||
stroke-dashoffset: 0;
|
||
}
|
||
.downloads-rebuild-branch[open] .downloads-rebuild-branch-target {
|
||
animation: none;
|
||
opacity: 1;
|
||
transform: none;
|
||
}
|
||
}
|
||
|
||
/* Install command is THE primary CTA of the Get Started section. A full
|
||
kinpaku hairline frame + gold prompt glyph + left-aligned mono make it read
|
||
as a runnable terminal line, not a decorative chip. Copy button nests on the
|
||
right. Full border, not a side-stripe (brand-ban). */
|
||
.home-kinpaku .downloads-rebuild-cmd {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 14px;
|
||
width: 100%;
|
||
min-width: 0;
|
||
box-sizing: border-box;
|
||
padding: 15px 14px 15px 22px;
|
||
background: var(--card-cmd-bg);
|
||
border: 1px solid var(--card-cmd-border);
|
||
border-radius: 2px;
|
||
}
|
||
|
||
/* The shell prompt that signals "this is a command you run." Carries the
|
||
row's accent (gold for install, patina for update). */
|
||
.home-kinpaku .downloads-rebuild-prompt {
|
||
flex: none;
|
||
font-family: var(--ks-mono);
|
||
font-size: 1.125rem;
|
||
font-weight: 500;
|
||
line-height: 1;
|
||
color: var(--card-prompt);
|
||
user-select: none;
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-cmd > code {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
overflow-x: auto;
|
||
white-space: nowrap;
|
||
color: var(--card-cmd-fg);
|
||
font-family: var(--ks-mono);
|
||
font-size: 1.22rem;
|
||
letter-spacing: 0;
|
||
background: transparent;
|
||
border: 0;
|
||
padding: 0;
|
||
user-select: all;
|
||
}
|
||
|
||
/* Update box: same frame, quieter patina accent (the "improved" signal). */
|
||
.home-kinpaku .downloads-rebuild-cmd--update {
|
||
border-color: var(--card-cmd-border-update);
|
||
}
|
||
.home-kinpaku .downloads-rebuild-cmd--update .downloads-rebuild-prompt {
|
||
color: var(--card-prompt-update);
|
||
}
|
||
|
||
/* Compact icon-only copy button nested inside the cmd box, on the right. */
|
||
.home-kinpaku .downloads-rebuild-copy {
|
||
flex: none;
|
||
position: relative;
|
||
display: inline-grid;
|
||
place-items: center;
|
||
width: 40px;
|
||
height: 40px;
|
||
border: 1px solid var(--card-copy-border);
|
||
background: transparent;
|
||
color: var(--card-copy-fg);
|
||
border-radius: 2px;
|
||
cursor: pointer;
|
||
transition:
|
||
background 180ms var(--ks-ease),
|
||
border-color 180ms var(--ks-ease),
|
||
color 180ms var(--ks-ease),
|
||
transform 120ms var(--ks-ease);
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-copy:active {
|
||
transform: scale(0.96);
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-copy-icon,
|
||
.home-kinpaku .downloads-rebuild-check-icon {
|
||
grid-area: 1 / 1;
|
||
transition:
|
||
opacity 140ms var(--ks-ease),
|
||
transform 180ms var(--ks-ease);
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-copy-icon {
|
||
opacity: 1;
|
||
transform: scale(1);
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-check-icon {
|
||
opacity: 0;
|
||
transform: scale(0.86);
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-check-icon path {
|
||
stroke-dasharray: 24;
|
||
stroke-dashoffset: 24;
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-copy.copied {
|
||
background: var(--card-copy-hover-bg);
|
||
border-color: currentColor;
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-copy.copied .downloads-rebuild-copy-icon {
|
||
opacity: 0;
|
||
transform: scale(0.82);
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-copy.copied .downloads-rebuild-check-icon {
|
||
opacity: 1;
|
||
transform: scale(1);
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-copy.copied .downloads-rebuild-check-icon path {
|
||
animation: downloads-check-draw 260ms var(--ks-ease) forwards;
|
||
}
|
||
|
||
@keyframes downloads-check-draw {
|
||
to { stroke-dashoffset: 0; }
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.home-kinpaku .downloads-rebuild-copy,
|
||
.home-kinpaku .downloads-rebuild-copy-icon,
|
||
.home-kinpaku .downloads-rebuild-check-icon {
|
||
transition: none;
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-copy.copied .downloads-rebuild-check-icon path {
|
||
animation: none;
|
||
stroke-dashoffset: 0;
|
||
}
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-copy span {
|
||
/* Hide the "Copy" label — icon carries the meaning. Kept in markup for a11y. */
|
||
position: absolute;
|
||
width: 1px;
|
||
height: 1px;
|
||
overflow: hidden;
|
||
clip: rect(0 0 0 0);
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-copy:hover {
|
||
background: var(--card-copy-hover-bg);
|
||
border-color: var(--card-copy-fg);
|
||
}
|
||
|
||
/* Update copy button tracks the patina accent rather than gold. */
|
||
.home-kinpaku .downloads-rebuild-cmd--update .downloads-rebuild-copy {
|
||
color: var(--card-prompt-update);
|
||
}
|
||
.home-kinpaku .downloads-rebuild-cmd--update .downloads-rebuild-copy:hover {
|
||
background: oklch(70% 0.12 188 / 0.08);
|
||
border-color: var(--card-prompt-update);
|
||
}
|
||
|
||
/* "Other install methods" — a quiet disclosure under the primary command.
|
||
Native <details> so it works without JS and stays accessible. */
|
||
.downloads-rebuild-alts {
|
||
margin: 2px 0 0;
|
||
text-align: left;
|
||
}
|
||
.downloads-rebuild-alts-summary {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 7px;
|
||
cursor: pointer;
|
||
list-style: none;
|
||
font-size: 0.84rem;
|
||
color: var(--ks-text-muted);
|
||
transition: color 160ms var(--ks-ease);
|
||
}
|
||
.downloads-rebuild-alts-summary::-webkit-details-marker { display: none; }
|
||
.downloads-rebuild-alts-summary:hover { color: var(--ks-patina); }
|
||
.downloads-rebuild-alts[open] > .downloads-rebuild-alts-summary {
|
||
color: var(--card-fg-strong);
|
||
margin-bottom: 18px;
|
||
}
|
||
.downloads-rebuild-alts-arrow {
|
||
flex: none;
|
||
transition: transform 180ms var(--ks-ease);
|
||
}
|
||
.downloads-rebuild-alts[open] > .downloads-rebuild-alts-summary .downloads-rebuild-alts-arrow {
|
||
transform: rotate(90deg);
|
||
}
|
||
|
||
.downloads-rebuild-alt + .downloads-rebuild-alt { margin-top: 18px; }
|
||
.downloads-rebuild-alt-label {
|
||
display: block;
|
||
margin-bottom: 7px;
|
||
font-size: 0.82rem;
|
||
font-weight: 600;
|
||
color: var(--card-fg-strong);
|
||
}
|
||
|
||
/* Smaller, quieter command box for the alternatives. */
|
||
.home-kinpaku .downloads-rebuild-cmd--alt {
|
||
padding: 9px 9px 9px 18px;
|
||
gap: 12px;
|
||
}
|
||
.home-kinpaku .downloads-rebuild-cmd--alt > code { font-size: 0.98rem; }
|
||
.home-kinpaku .downloads-rebuild-cmd--alt .downloads-rebuild-copy {
|
||
width: 34px;
|
||
height: 34px;
|
||
}
|
||
|
||
.downloads-rebuild-alt-note {
|
||
display: block;
|
||
margin-top: 7px;
|
||
font-size: 0.82rem;
|
||
line-height: 1.5;
|
||
color: var(--ks-text-muted);
|
||
}
|
||
.downloads-rebuild-alt-note code {
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.9em;
|
||
color: var(--card-fg-muted);
|
||
}
|
||
|
||
.downloads-rebuild-note {
|
||
margin: 0;
|
||
max-width: 60ch;
|
||
text-align: left;
|
||
color: var(--card-fg-muted);
|
||
font-size: 0.88rem;
|
||
line-height: 1.55;
|
||
}
|
||
.home-kinpaku .downloads-rebuild-note code {
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.86em;
|
||
color: var(--card-fg-strong);
|
||
}
|
||
|
||
/* Quieter than the note — the changelog + FAQ hints sit just under the
|
||
primary "works with" line. Patina links so they read as related but
|
||
distinct from the kinpaku primary CTA above. */
|
||
.downloads-rebuild-secondary {
|
||
margin: 6px 0 0;
|
||
text-align: left;
|
||
font-size: 0.84rem;
|
||
color: var(--ks-text-muted);
|
||
}
|
||
.downloads-rebuild-secondary a {
|
||
color: var(--ks-patina);
|
||
text-decoration: none;
|
||
border-bottom: 1px solid oklch(70% 0.12 188 / 0.35);
|
||
}
|
||
.downloads-rebuild-secondary a:hover {
|
||
color: var(--ks-patina-pale);
|
||
border-bottom-color: var(--ks-patina-pale);
|
||
}
|
||
|
||
/* Closing links under the setup extras — workflow + command docs. */
|
||
.downloads-rebuild-more {
|
||
margin: 28px 0 0;
|
||
text-align: left;
|
||
font-size: 0.9rem;
|
||
color: var(--ks-text-muted);
|
||
}
|
||
.downloads-rebuild-more a {
|
||
color: var(--ks-patina);
|
||
text-decoration: none;
|
||
border-bottom: 1px solid oklch(70% 0.12 188 / 0.35);
|
||
}
|
||
.downloads-rebuild-more a:hover {
|
||
color: var(--ks-patina-pale);
|
||
border-bottom-color: var(--ks-patina-pale);
|
||
}
|
||
|
||
.downloads-rebuild-extras {
|
||
list-style: none;
|
||
margin: 0;
|
||
padding: 40px 0 0;
|
||
border-top: 1px solid var(--ks-rule);
|
||
display: grid;
|
||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||
gap: 36px;
|
||
max-width: 820px;
|
||
}
|
||
|
||
/* Each cell is a flex column so primary links can be bottom-anchored.
|
||
Text is left-aligned within the cell — wrapping paragraphs read better
|
||
that way than centered. The grid as a whole stays centered on the page. */
|
||
.downloads-rebuild-extras li {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
text-align: left;
|
||
align-items: stretch;
|
||
}
|
||
|
||
.downloads-rebuild-extras strong {
|
||
color: var(--card-fg-strong);
|
||
font-family: var(--ks-font);
|
||
font-weight: 400;
|
||
font-size: 1rem;
|
||
}
|
||
|
||
.downloads-rebuild-extras span {
|
||
color: var(--card-fg-muted);
|
||
font-size: 0.88rem;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-extras span code {
|
||
background: transparent;
|
||
border: 0;
|
||
border-radius: 0;
|
||
padding: 0;
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.84rem;
|
||
color: var(--card-fg-strong);
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-extras a {
|
||
color: var(--card-link-color);
|
||
font-size: 0.88rem;
|
||
text-decoration: underline;
|
||
text-decoration-color: var(--card-link-underline);
|
||
text-underline-offset: 4px;
|
||
text-decoration-thickness: 1px;
|
||
width: fit-content;
|
||
transition: color 180ms var(--ks-ease);
|
||
}
|
||
|
||
/* Push the primary link to the bottom of each cell so all three primary
|
||
links sit at the same Y. The secondary link follows naturally below. */
|
||
.home-kinpaku .downloads-rebuild-extras a:not(.downloads-rebuild-link-secondary) {
|
||
margin-top: auto;
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-extras a.downloads-rebuild-link-secondary {
|
||
color: var(--card-link-secondary);
|
||
text-decoration-color: var(--card-link-secondary);
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-extras a.downloads-rebuild-link-secondary:hover {
|
||
color: var(--card-fg);
|
||
text-decoration-color: var(--card-fg);
|
||
}
|
||
|
||
@media (max-width: 1080px) {
|
||
.downloads-rebuild { padding: 80px 0; }
|
||
.downloads-rebuild-extras { grid-template-columns: 1fr; }
|
||
}
|
||
|
||
@media (max-width: 720px) {
|
||
.downloads-rebuild { padding: 64px 0; }
|
||
.downloads-rebuild-install { grid-template-columns: 1fr; }
|
||
}
|
||
|
||
@media (max-width: 520px) {
|
||
.home-kinpaku .downloads-rebuild-cmd {
|
||
gap: 10px;
|
||
padding: 13px 12px 13px 16px;
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-cmd > code {
|
||
overflow-x: visible;
|
||
white-space: normal;
|
||
overflow-wrap: anywhere;
|
||
font-size: 0.96rem;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-prompt {
|
||
font-size: 1rem;
|
||
}
|
||
|
||
.home-kinpaku .downloads-rebuild-copy {
|
||
width: 34px;
|
||
height: 34px;
|
||
}
|
||
}
|
||
|
||
/* ============================================================================
|
||
FOUNDATION CARD VISUALIZATIONS (varied per discipline)
|
||
Foundation cards use the original animated SVG icons (foundation-animations.js
|
||
restored). No extra chrome here.
|
||
============================================================================ */
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.home-kinpaku .slop-teaser-cta-primary {
|
||
transition: none;
|
||
}
|
||
}
|
||
|
||
/* ============================================================================
|
||
Section dividers — varied patterns from /design-system#dividers, so each
|
||
transition has its own personality without being noisy. All drawn as a
|
||
::before pseudo on the section so no markup change required.
|
||
============================================================================ */
|
||
|
||
.home-kinpaku .slop-teaser-section,
|
||
.home-kinpaku .live-section,
|
||
.home-kinpaku .language-section,
|
||
.home-kinpaku .downloads-rebuild {
|
||
position: relative;
|
||
/* Override stray border-tops applied by main.css → live-mode.css and any
|
||
other transitive imports — section dividers are drawn via ::before. */
|
||
border-top: 0;
|
||
}
|
||
|
||
/* The Hero → Slop dot-grid divider previously lived here as a ::before on
|
||
.slop-teaser-section. After the testimonials section was inserted between
|
||
hero and slop, that divider moved onto .testimonials-section in
|
||
testimonials.css. The slop section no longer needs a top divider — its
|
||
own padding and the testimonials section above carry the transition. */
|
||
|
||
/* Slop → Live, Live → Language, Language → Get Started: intentionally no
|
||
dividers. The plinth dot grid at the top of Testimonials and the verdigris
|
||
dot grid at the top of the footer bracket the section stack — interior
|
||
transitions read on layout alone. */
|
||
|
||
|
||
/* ============================================================================
|
||
HERO LIVE-MODE DEMO
|
||
The Live Mode loop (live-demo.js, #live-demo) now lives in the hero. Override
|
||
the section-03 frame proportions — the base frame is 16:9, too short for the
|
||
portrait card in the narrow hero column — and give all variants a single
|
||
card footprint so the pick-outline hugs them.
|
||
============================================================================ */
|
||
|
||
.hero-live-demo {
|
||
display: block; /* override the .live-demo 2-col grid */
|
||
width: 100%;
|
||
}
|
||
|
||
.hero-live-demo .live-demo-frame {
|
||
aspect-ratio: auto;
|
||
/* Height follows content — fixed min was leaving ivory gap under clipped suites. */
|
||
height: auto;
|
||
max-height: min(540px, calc(100svh - 140px));
|
||
max-width: 100%;
|
||
}
|
||
|
||
/* The hero-scoped demo-chrome refinements (crisp outline, neutral chrome dots /
|
||
URL, quiet gbar border, crisp active-Pick pill, tighter radii, refined frame
|
||
elevation) were promoted into the shared picker chrome in kinpaku-kit.css /
|
||
live-mode-kinpaku.css / home-kinpaku.css so /live-mode and the injected
|
||
picker (skill/scripts/live-browser.js) match. Nothing hero-specific remains. */
|
||
|
||
/* The stage holds a full (fake) website now, not a centered card. */
|
||
.hero-live-demo .live-demo-stage {
|
||
display: block;
|
||
padding: 0;
|
||
height: auto;
|
||
min-height: 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* Default .hotel-site is position:absolute; inset:0, which stretches to the tall
|
||
stage and paints ivory in the gap below clipped suites. */
|
||
.hero-live-demo .hotel-site {
|
||
position: relative;
|
||
inset: auto;
|
||
height: auto;
|
||
}
|
||
|
||
/* The picked element is the hero — a single, full-width, one-of-a-kind block.
|
||
Contained with side margins so the pick-outline hugs it inside the frame. */
|
||
.hero-live-demo .live-demo-target.hotel-hero-pick {
|
||
align-self: stretch;
|
||
width: auto;
|
||
height: 214px;
|
||
min-height: 0;
|
||
margin: 16px 18px 0;
|
||
}
|
||
.hero-live-demo .live-demo-variant {
|
||
display: grid;
|
||
place-items: stretch;
|
||
}
|
||
.hero-live-demo .live-demo-variant > .hotel-hero {
|
||
width: 100%;
|
||
height: 100%;
|
||
box-sizing: border-box;
|
||
}
|
||
/* Suite cards run to the frame bottom; the gbar overlaps their lower edge. */
|
||
.hero-live-demo .hotel-suites {
|
||
flex: none;
|
||
max-height: 132px;
|
||
padding: 10px 0 0 22px;
|
||
overflow: hidden;
|
||
}
|
||
.hero-live-demo .hotel-suites-row > .hotel-card {
|
||
width: 198px;
|
||
height: 226px;
|
||
box-sizing: border-box;
|
||
flex: none;
|
||
}
|
||
|
||
/* The contextual bar's optional free-text input reads as a placeholder (in the
|
||
real picker you type here OR annotate; here the annotation is the input). */
|
||
.hero-live-demo [data-demo-input-text]:empty::before {
|
||
content: "refine further…";
|
||
opacity: 0.5;
|
||
}
|
||
|
||
@media (max-width: 1080px) {
|
||
.hero-live-demo { max-width: 460px; margin-inline: auto; }
|
||
}
|
||
|
||
|
||
/* ----------------------------------------------------------------------------
|
||
The page under the picker: AURELIA, a warm serif boutique-hotel site.
|
||
Deliberately the opposite of Impeccable's dark kinpaku aesthetic.
|
||
---------------------------------------------------------------------------- */
|
||
.hotel-site {
|
||
--h-ivory: oklch(96.5% 0.012 84);
|
||
--h-sand: oklch(92% 0.02 80);
|
||
--h-ink: oklch(31% 0.025 55);
|
||
--h-muted: oklch(50% 0.025 55);
|
||
--h-green: oklch(43% 0.055 150);
|
||
--h-line: oklch(82% 0.02 80);
|
||
--h-serif: "Cormorant Garamond", Georgia, serif;
|
||
--h-sans: var(--ks-font, system-ui, sans-serif);
|
||
position: absolute;
|
||
inset: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
background: var(--h-ivory);
|
||
color: var(--h-ink);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.hotel-nav {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 22px;
|
||
height: 44px;
|
||
flex: none;
|
||
border-bottom: 1px solid var(--h-line);
|
||
}
|
||
.hotel-logo {
|
||
font-family: var(--h-serif);
|
||
font-weight: 600;
|
||
font-size: 18px;
|
||
letter-spacing: 0.22em;
|
||
color: var(--h-ink);
|
||
}
|
||
.hotel-nav-links {
|
||
display: flex;
|
||
gap: 18px;
|
||
font-family: var(--h-sans);
|
||
font-size: 11px;
|
||
letter-spacing: 0.06em;
|
||
color: var(--h-muted);
|
||
}
|
||
.hotel-nav-book {
|
||
font-family: var(--h-sans);
|
||
font-size: 11px;
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
color: var(--h-green);
|
||
border: 1px solid var(--h-green);
|
||
border-radius: 999px;
|
||
padding: 5px 13px;
|
||
}
|
||
|
||
/* ----------------------------------------------------------------------------
|
||
The hero = the picked element. One shared structure; the slop original is
|
||
generic + sans, the three elegant variants are the on-brand fixes.
|
||
---------------------------------------------------------------------------- */
|
||
.hotel-hero {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: flex-end;
|
||
overflow: hidden;
|
||
border-radius: 12px;
|
||
box-sizing: border-box;
|
||
}
|
||
.hotel-hero-photo {
|
||
position: absolute;
|
||
inset: 0;
|
||
background:
|
||
linear-gradient(to top, oklch(24% 0.03 40 / 0.82), oklch(38% 0.04 40 / 0.12) 58%, transparent),
|
||
linear-gradient(150deg, oklch(66% 0.08 60), oklch(48% 0.08 34) 52%, oklch(36% 0.05 255));
|
||
}
|
||
.hotel-hero-inner {
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 6px;
|
||
padding: 22px 24px;
|
||
}
|
||
/* The hotel hero text sits on dark teal/gold photos, so it stays light in both
|
||
themes. These carry an extra .hotel-hero class purely for specificity: the
|
||
global `.home-kinpaku h2`/`p` color rules (0,1,1) otherwise win and, in light
|
||
mode, drag the headline to the dark --ks-champagne ink — illegible on the
|
||
photo. The slop variant's own dark overrides still win on source order. */
|
||
.hotel-hero .hotel-hero-eyebrow {
|
||
margin: 0;
|
||
font-family: var(--h-sans);
|
||
font-size: 10px;
|
||
letter-spacing: 0.26em;
|
||
text-transform: uppercase;
|
||
color: oklch(91% 0.05 85);
|
||
}
|
||
.hotel-hero .hotel-hero-title {
|
||
margin: 0;
|
||
font-family: var(--h-serif);
|
||
font-weight: 500;
|
||
font-size: 2rem;
|
||
line-height: 1.05;
|
||
letter-spacing: -0.01em;
|
||
color: oklch(98% 0.01 85);
|
||
}
|
||
.hotel-hero .hotel-hero-sub {
|
||
margin: 0;
|
||
font-family: var(--h-sans);
|
||
font-size: 12px;
|
||
color: oklch(89% 0.02 85);
|
||
}
|
||
.hotel-hero-cta {
|
||
margin-top: 5px;
|
||
font-family: var(--h-sans);
|
||
font-weight: 600;
|
||
font-size: 12px;
|
||
letter-spacing: 0.04em;
|
||
padding: 9px 18px;
|
||
border: 0;
|
||
border-radius: 6px;
|
||
background: var(--h-ivory);
|
||
color: var(--h-ink);
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* original — AI slop: flat, generic, sans, purple CTA. Clashes with the brand. */
|
||
.hotel-hero--slop {
|
||
align-items: center;
|
||
background: oklch(95% 0.004 270);
|
||
}
|
||
.hotel-hero--slop .hotel-hero-photo { display: none; }
|
||
.hotel-hero--slop .hotel-hero-title {
|
||
font-family: ui-sans-serif, system-ui, sans-serif;
|
||
font-weight: 800;
|
||
font-size: 1.75rem;
|
||
letter-spacing: -0.02em;
|
||
color: oklch(32% 0.02 285);
|
||
}
|
||
.hotel-hero--slop .hotel-hero-sub { color: oklch(56% 0.015 285); }
|
||
.hotel-hero--slop .hotel-hero-cta {
|
||
background: linear-gradient(135deg, oklch(62% 0.2 300), oklch(56% 0.21 268));
|
||
color: oklch(98% 0.01 300);
|
||
border-radius: 8px;
|
||
}
|
||
|
||
/* v1 — warm dusk, text bottom-left (uses base photo) */
|
||
.hotel-hero--1 .hotel-hero-cta { background: oklch(98% 0.01 85); color: oklch(30% 0.03 40); }
|
||
|
||
/* v2 — sea-toned, centered, outline CTA. justify-content centers the inner
|
||
block horizontally (the base hero is a flex row defaulting to flex-start). */
|
||
.hotel-hero--2 { align-items: center; justify-content: center; text-align: center; }
|
||
.hotel-hero--2 .hotel-hero-photo {
|
||
background:
|
||
linear-gradient(to top, oklch(24% 0.03 220 / 0.82), oklch(38% 0.04 220 / 0.12) 58%, transparent),
|
||
linear-gradient(150deg, oklch(64% 0.08 205), oklch(46% 0.07 230) 55%, oklch(34% 0.05 255));
|
||
}
|
||
.hotel-hero--2 .hotel-hero-inner { align-items: center; }
|
||
.hotel-hero--2 .hotel-hero-title { font-style: italic; }
|
||
.hotel-hero--2 .hotel-hero-cta {
|
||
background: transparent;
|
||
color: oklch(97% 0.01 85);
|
||
border: 1px solid oklch(97% 0.01 85 / 0.7);
|
||
}
|
||
|
||
/* v3 — golden hour, gold CTA */
|
||
.hotel-hero--3 .hotel-hero-photo {
|
||
background:
|
||
linear-gradient(to top, oklch(26% 0.04 50 / 0.78), oklch(42% 0.05 55 / 0.12) 58%, transparent),
|
||
linear-gradient(150deg, oklch(78% 0.1 75), oklch(60% 0.1 50) 52%, oklch(44% 0.07 38));
|
||
}
|
||
.hotel-hero--3 .hotel-hero-cta {
|
||
background: var(--ks-kinpaku, oklch(84% 0.19 80));
|
||
color: oklch(24% 0.03 60);
|
||
}
|
||
|
||
.hotel-suites {
|
||
flex: 1;
|
||
padding: 16px 0 0 22px; /* no right padding so the row runs off the edge */
|
||
overflow: hidden;
|
||
}
|
||
.hotel-suites-label {
|
||
margin: 0 0 11px;
|
||
font-family: var(--h-sans);
|
||
font-size: 10px;
|
||
letter-spacing: 0.24em;
|
||
text-transform: uppercase;
|
||
color: var(--h-muted);
|
||
}
|
||
/* Left-aligned row; the later suites run off the right edge (clipped by the
|
||
site), so the page reads as scrollable rather than a centered modal. */
|
||
.hotel-suites-row {
|
||
display: flex;
|
||
gap: 16px;
|
||
}
|
||
|
||
/* ----------------------------------------------------------------------------
|
||
The room/rate card — one shared structure, four distinct treatments.
|
||
---------------------------------------------------------------------------- */
|
||
.hotel-card {
|
||
--c-surface: oklch(99% 0.008 84);
|
||
--c-ink: var(--h-ink);
|
||
--c-accent: var(--h-green);
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
border-radius: 10px;
|
||
background: var(--c-surface);
|
||
border: 1px solid var(--h-line);
|
||
box-shadow: 0 3px 10px oklch(40% 0.04 60 / 0.08); /* flat in layout, not a modal */
|
||
font-family: var(--h-sans);
|
||
text-align: left;
|
||
}
|
||
.hotel-card-photo--b {
|
||
background:
|
||
radial-gradient(120% 90% at 30% 10%, oklch(88% 0.05 250 / 0.6), transparent 60%),
|
||
linear-gradient(150deg, oklch(70% 0.06 235), oklch(85% 0.05 90));
|
||
}
|
||
.hotel-card-photo--c {
|
||
background:
|
||
radial-gradient(120% 90% at 60% 0%, oklch(90% 0.06 70 / 0.7), transparent 60%),
|
||
linear-gradient(150deg, oklch(76% 0.07 50), oklch(86% 0.05 110));
|
||
}
|
||
.hotel-card-photo {
|
||
flex: none;
|
||
height: 104px;
|
||
background:
|
||
radial-gradient(120% 90% at 70% 10%, oklch(88% 0.06 95 / 0.7), transparent 60%),
|
||
linear-gradient(150deg, oklch(74% 0.05 160), oklch(86% 0.05 78));
|
||
}
|
||
.hotel-card-body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
flex: 1;
|
||
padding: 13px 15px 14px;
|
||
}
|
||
.hotel-card-kicker {
|
||
margin: 0;
|
||
font-size: 9px;
|
||
letter-spacing: 0.2em;
|
||
text-transform: uppercase;
|
||
color: var(--c-accent);
|
||
}
|
||
.hotel-card-name {
|
||
margin: 0;
|
||
font-family: var(--h-serif);
|
||
font-weight: 600;
|
||
font-size: 1.5rem;
|
||
line-height: 1.05;
|
||
color: var(--c-ink);
|
||
}
|
||
.hotel-card-meta {
|
||
margin: 0;
|
||
font-size: 11px;
|
||
color: var(--h-muted);
|
||
}
|
||
.hotel-card-foot {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 10px;
|
||
margin-top: auto;
|
||
padding-top: 10px;
|
||
}
|
||
.hotel-card-price {
|
||
font-family: var(--h-serif);
|
||
font-weight: 600;
|
||
font-size: 20px;
|
||
color: var(--c-ink);
|
||
}
|
||
.hotel-card-price i {
|
||
font-style: normal;
|
||
font-family: var(--h-sans);
|
||
font-size: 10px;
|
||
color: var(--h-muted);
|
||
}
|
||
.hotel-card-btn {
|
||
flex: none;
|
||
font-family: var(--h-sans);
|
||
font-weight: 500;
|
||
font-size: 11px;
|
||
letter-spacing: 0.06em;
|
||
padding: 7px 14px;
|
||
border: 0;
|
||
border-radius: 6px;
|
||
background: var(--c-ink);
|
||
color: var(--h-ivory);
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* original — the AI-slop card: generic sans, purple gradient, overdone radius +
|
||
shadow, weak copy. Clashes on purpose with the serif siblings. */
|
||
.hotel-card--slop {
|
||
font-family: ui-sans-serif, system-ui, sans-serif;
|
||
border-radius: 16px;
|
||
border-color: transparent;
|
||
box-shadow: 0 12px 30px oklch(52% 0.2 295 / 0.4);
|
||
}
|
||
.hotel-card--slop .hotel-card-photo {
|
||
background: linear-gradient(135deg, oklch(68% 0.2 300), oklch(60% 0.21 268));
|
||
}
|
||
.hotel-card--slop .hotel-card-kicker {
|
||
font-size: 10px;
|
||
letter-spacing: 0.02em;
|
||
text-transform: none;
|
||
color: oklch(58% 0.2 290);
|
||
}
|
||
.hotel-card--slop .hotel-card-name {
|
||
font-family: inherit;
|
||
font-weight: 700;
|
||
font-size: 1.125rem;
|
||
letter-spacing: -0.01em;
|
||
color: oklch(32% 0.02 290);
|
||
}
|
||
.hotel-card--slop .hotel-card-meta { color: oklch(62% 0.01 290); }
|
||
.hotel-card--slop .hotel-card-price {
|
||
font-family: inherit;
|
||
font-weight: 700;
|
||
font-size: 16px;
|
||
color: oklch(32% 0.02 290);
|
||
}
|
||
.hotel-card--slop .hotel-card-price i { font-family: inherit; }
|
||
.hotel-card--slop .hotel-card-btn {
|
||
background: linear-gradient(135deg, oklch(62% 0.2 300), oklch(56% 0.21 268));
|
||
border-radius: 8px;
|
||
text-transform: none;
|
||
letter-spacing: 0;
|
||
}
|
||
|
||
/* v1 — full-bleed dusk photo, body overlaid on a scrim */
|
||
.hotel-card--1 {
|
||
position: relative;
|
||
border-color: transparent;
|
||
}
|
||
.hotel-card--1 .hotel-card-photo {
|
||
position: absolute;
|
||
inset: 0;
|
||
height: auto;
|
||
background:
|
||
linear-gradient(to top, oklch(22% 0.03 40 / 0.92), oklch(30% 0.04 40 / 0.15) 55%, transparent),
|
||
linear-gradient(155deg, oklch(64% 0.08 55), oklch(40% 0.06 28) 55%, oklch(32% 0.05 320));
|
||
}
|
||
.hotel-card--1 .hotel-card-body { position: relative; justify-content: flex-end; }
|
||
.hotel-card--1 .hotel-card-kicker { color: oklch(88% 0.06 85); }
|
||
.hotel-card--1 .hotel-card-name,
|
||
.hotel-card--1 .hotel-card-price { color: oklch(97% 0.01 85); }
|
||
.hotel-card--1 .hotel-card-meta { color: oklch(86% 0.02 85); }
|
||
.hotel-card--1 .hotel-card-price i { color: oklch(86% 0.02 85); }
|
||
.hotel-card--1 .hotel-card-btn {
|
||
background: oklch(97% 0.01 85);
|
||
color: oklch(28% 0.03 40);
|
||
}
|
||
|
||
/* v2 — editorial, no photo, type-forward */
|
||
.hotel-card--2 {
|
||
background: var(--h-sand);
|
||
justify-content: center;
|
||
text-align: center;
|
||
}
|
||
.hotel-card--2 .hotel-card-photo { display: none; }
|
||
.hotel-card--2 .hotel-card-body { gap: 7px; padding: 22px 18px; align-items: center; }
|
||
.hotel-card--2 .hotel-card-name { font-size: 2rem; font-style: italic; }
|
||
.hotel-card--2 .hotel-card-foot {
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
border-top: 1px solid var(--h-line);
|
||
margin-top: 12px;
|
||
}
|
||
.hotel-card--2 .hotel-card-price { font-size: 1.5rem; }
|
||
.hotel-card--2 .hotel-card-btn {
|
||
background: transparent;
|
||
color: var(--c-accent);
|
||
border-bottom: 1px solid currentColor;
|
||
border-radius: 0;
|
||
padding: 2px 0;
|
||
}
|
||
|
||
/* v3 — framed, terracotta accent */
|
||
.hotel-card--3 {
|
||
--c-accent: oklch(56% 0.13 42);
|
||
background: oklch(98% 0.012 70);
|
||
padding: 7px;
|
||
border-color: oklch(56% 0.13 42 / 0.4);
|
||
}
|
||
.hotel-card--3 .hotel-card-photo {
|
||
height: 88px;
|
||
border-radius: 5px;
|
||
background:
|
||
linear-gradient(150deg, oklch(72% 0.09 45), oklch(84% 0.06 70));
|
||
}
|
||
.hotel-card--3 .hotel-card-name { color: oklch(34% 0.04 42); }
|
||
.hotel-card--3 .hotel-card-price { color: var(--c-accent); }
|
||
.hotel-card--3 .hotel-card-btn { background: var(--c-accent); }
|
||
|
||
|
||
/* ----------------------------------------------------------------------------
|
||
Annotation layer: freehand circle + comment pin, drawn on the picked card.
|
||
The layer is sized/positioned onto the card by live-demo.js (positionAnnotations);
|
||
marks sit in element-local space, mirroring the real picker.
|
||
---------------------------------------------------------------------------- */
|
||
.hero-live-demo .live-demo-annotations {
|
||
position: absolute;
|
||
z-index: 4; /* above the card + outline, below the cursor (5) */
|
||
pointer-events: none;
|
||
color: var(--ks-kinpaku);
|
||
transform: none; /* JS sets exact left/top/width/height onto the card */
|
||
opacity: 0;
|
||
transition: opacity 200ms var(--ks-ease, ease);
|
||
}
|
||
.hero-live-demo .live-demo-annotations.is-visible { opacity: 1; }
|
||
|
||
/* Circle stroke fills the card-sized layer (viewBox 0 0 100 100). The base rule
|
||
centers + offsets the stroke for the old layout — null that out here. */
|
||
.hero-live-demo .live-demo-stroke {
|
||
position: absolute;
|
||
inset: 0;
|
||
left: 0;
|
||
top: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
transform: none;
|
||
overflow: visible;
|
||
color: var(--ks-kinpaku);
|
||
}
|
||
.hero-live-demo .live-demo-stroke path {
|
||
stroke-dasharray: 1;
|
||
stroke-dashoffset: 1;
|
||
filter: drop-shadow(0 1px 2px oklch(0% 0 0 / 0.35));
|
||
}
|
||
/* Single circle around the off-brand button, drawn when the layer reveals. */
|
||
.hero-live-demo .live-demo-annotations.is-visible .live-demo-stroke path {
|
||
animation: liveDemoStroke 620ms var(--ks-ease, ease) forwards;
|
||
}
|
||
|
||
/* Comment pin: gold dot + note bubble, dropped near the button / suites edge. */
|
||
.live-demo-pin {
|
||
position: absolute;
|
||
left: 46%;
|
||
top: 78%;
|
||
opacity: 0;
|
||
transform: scale(0.5);
|
||
transform-origin: center;
|
||
transition: opacity 160ms ease, transform 220ms cubic-bezier(0.22, 1, 0.36, 1);
|
||
}
|
||
.hero-live-demo .live-demo-annotations.is-pin-visible .live-demo-pin {
|
||
opacity: 1;
|
||
transform: scale(1);
|
||
}
|
||
.live-demo-pin-dot {
|
||
display: block;
|
||
width: 14px;
|
||
height: 14px;
|
||
border-radius: 50%;
|
||
background: var(--ks-kinpaku);
|
||
border: 2px solid oklch(98% 0.005 95);
|
||
box-shadow: 0 2px 7px oklch(0% 0 0 / 0.45);
|
||
}
|
||
.live-demo-pin-note {
|
||
position: absolute;
|
||
left: -3px;
|
||
bottom: 24px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
white-space: nowrap;
|
||
padding: 6px 10px;
|
||
border-radius: 7px;
|
||
background: oklch(19% 0.008 95);
|
||
border: 1px solid var(--ks-gold-hairline-strong, oklch(74% 0.09 82 / 0.6));
|
||
color: oklch(94% 0.02 85);
|
||
font-family: var(--ks-font, system-ui, sans-serif);
|
||
font-size: 11.5px;
|
||
line-height: 1;
|
||
box-shadow: 0 8px 22px oklch(0% 0 0 / 0.4);
|
||
opacity: 0;
|
||
transform: translateY(3px);
|
||
transition: opacity 160ms ease, transform 160ms ease;
|
||
}
|
||
.hero-live-demo .live-demo-annotations.is-note-visible .live-demo-pin-note {
|
||
opacity: 1;
|
||
transform: none;
|
||
}
|
||
.live-demo-pin-caret {
|
||
display: inline-block;
|
||
width: 1px;
|
||
height: 12px;
|
||
margin-left: 1px;
|
||
background: var(--ks-kinpaku);
|
||
animation: liveDemoCaret 1s steps(1) infinite;
|
||
}
|
||
@keyframes liveDemoCaret { 50% { opacity: 0; } }
|
||
|
||
/* CLEAR chip, top-right of the annotation overlay (authentic to the picker). */
|
||
.live-demo-annot-clear {
|
||
position: absolute;
|
||
top: 6px;
|
||
right: 6px;
|
||
font-family: var(--ks-mono, monospace);
|
||
font-size: 8.5px;
|
||
letter-spacing: 0.14em;
|
||
text-transform: uppercase;
|
||
color: var(--ks-kinpaku);
|
||
background: oklch(8% 0.006 95 / 0.72);
|
||
border: 1px solid var(--ks-rule);
|
||
border-radius: 4px;
|
||
padding: 3px 7px;
|
||
opacity: 0;
|
||
transition: opacity 200ms ease;
|
||
}
|
||
.hero-live-demo .live-demo-annotations.is-visible .live-demo-annot-clear { opacity: 0.85; }
|
||
|
||
|
||
/* ============================================================================
|
||
SECTION 03 (Live Mode) — how-it-works flow + audience tiles
|
||
The animated demo lives in the hero; this section explains how Live Mode wires
|
||
itself into your codebase, then sells it to each evaluator. Left-aligned blocks
|
||
below the (right-aligned) section header.
|
||
============================================================================ */
|
||
|
||
/* --- How it works: 3 steps, each with a minimal glyph -------------------- */
|
||
.live-how {
|
||
list-style: none;
|
||
margin: 48px 0 0;
|
||
padding: 0;
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 18px;
|
||
text-align: left;
|
||
}
|
||
/* No outer card — the mini-screen below is the only framed element, so the
|
||
step reads as one clean unit instead of a box-in-a-box. */
|
||
.live-how-step {
|
||
display: grid;
|
||
align-content: start;
|
||
gap: 13px;
|
||
padding: 18px;
|
||
border: 1px solid var(--ks-rule);
|
||
border-radius: 6px;
|
||
background: oklch(10% 0.006 95 / 0.45);
|
||
}
|
||
.live-how-step .live-how-n { margin-top: 4px; }
|
||
.live-how-n {
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.72rem;
|
||
letter-spacing: 0.18em;
|
||
color: var(--ks-kinpaku);
|
||
}
|
||
.live-how-title {
|
||
margin: 0;
|
||
font-family: var(--ks-font-display);
|
||
font-weight: var(--ks-type-headline-weight);
|
||
font-size: 1.125rem;
|
||
color: var(--ks-champagne);
|
||
}
|
||
.live-how-copy {
|
||
margin: 0;
|
||
font-size: 0.92rem;
|
||
line-height: 1.55;
|
||
color: var(--ks-muted);
|
||
}
|
||
.live-how-copy code {
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.85em;
|
||
color: var(--ks-kinpaku);
|
||
}
|
||
|
||
/* Each step's visual is a clean mini browser screen (chrome bar + screen),
|
||
echoing the picker still — not an abstract panel. */
|
||
.live-how-glyph {
|
||
border-radius: 5px;
|
||
overflow: hidden;
|
||
background: oklch(6% 0.005 95);
|
||
}
|
||
.live-how-screen {
|
||
position: relative;
|
||
min-height: 104px;
|
||
padding: 16px;
|
||
display: grid;
|
||
align-content: center;
|
||
justify-items: start;
|
||
gap: 9px;
|
||
}
|
||
/* Step 1: terminal line + framework chips */
|
||
.live-how-term {
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.72rem;
|
||
color: var(--ks-champagne);
|
||
}
|
||
.live-how-term-prompt { color: var(--ks-kinpaku); }
|
||
.live-how-chips {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 5px;
|
||
}
|
||
.live-how-chips span {
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.6rem;
|
||
letter-spacing: 0.04em;
|
||
color: var(--ks-muted);
|
||
padding: 2px 7px;
|
||
border: 1px solid var(--ks-rule);
|
||
border-radius: 999px;
|
||
}
|
||
.live-how-chips span:first-child {
|
||
color: var(--ks-kinpaku);
|
||
border-color: oklch(74% 0.09 82 / 0.55);
|
||
}
|
||
/* Step 2: an element with a pick outline + an attached comment, echoing the
|
||
on-canvas picker from the hero loop. */
|
||
.live-how-screen--pick { justify-items: center; align-content: start; }
|
||
.live-how-pick {
|
||
position: relative;
|
||
width: 122px;
|
||
height: 40px;
|
||
border-radius: 4px;
|
||
background: oklch(14% 0.006 95);
|
||
border: 2px solid var(--ks-kinpaku);
|
||
box-shadow: 0 0 0 4px oklch(78% 0.12 82 / 0.12);
|
||
display: grid;
|
||
place-items: center;
|
||
}
|
||
.live-how-pick-tag {
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.62rem;
|
||
color: var(--ks-muted);
|
||
}
|
||
.live-how-pick-dot {
|
||
position: absolute;
|
||
right: -5px;
|
||
top: -5px;
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 50%;
|
||
background: var(--ks-kinpaku);
|
||
border: 2px solid oklch(98% 0.005 95);
|
||
}
|
||
.live-how-note {
|
||
position: absolute;
|
||
top: calc(100% + 9px);
|
||
right: -14px;
|
||
white-space: nowrap;
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.6rem;
|
||
color: var(--ks-champagne);
|
||
background: oklch(13% 0.006 95);
|
||
border: 1px solid var(--ks-rule);
|
||
box-shadow: inset 0 -2px 0 var(--ks-kinpaku);
|
||
border-radius: 3px;
|
||
padding: 4px 8px;
|
||
}
|
||
.live-how-note-caret {
|
||
position: absolute;
|
||
top: -4px;
|
||
left: 13px;
|
||
width: 7px;
|
||
height: 7px;
|
||
background: oklch(13% 0.006 95);
|
||
border-left: 1px solid var(--ks-rule);
|
||
border-top: 1px solid var(--ks-rule);
|
||
transform: rotate(45deg);
|
||
}
|
||
/* Step 3: the floating contextual bar at accept, plus the source write. */
|
||
.live-how-screen--accept { justify-items: center; gap: 12px; }
|
||
.live-how-ctxbar {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 5px 6px 5px 10px;
|
||
border: 1px solid var(--ks-rule);
|
||
border-radius: 999px;
|
||
background: oklch(12% 0.006 95);
|
||
box-shadow: 0 6px 18px oklch(2% 0.004 95 / 0.45);
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.62rem;
|
||
}
|
||
.live-how-ctx-nav { color: var(--ks-muted); }
|
||
.live-how-ctx-count { color: var(--ks-champagne); }
|
||
.live-how-ctx-div { width: 1px; height: 13px; background: var(--ks-rule); }
|
||
.live-how-ctx-accept {
|
||
padding: 3px 10px;
|
||
border-radius: 999px;
|
||
background: var(--ks-kinpaku);
|
||
color: oklch(16% 0.02 90);
|
||
font-weight: 600;
|
||
}
|
||
.live-how-wrote {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.62rem;
|
||
color: var(--ks-patina);
|
||
}
|
||
.live-how-wrote-check { font-size: 0.7rem; }
|
||
|
||
/* --- Who it's for: three role tiles -------------------------------------- */
|
||
.live-audience-eyebrow {
|
||
margin: 52px 0 22px;
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.62rem;
|
||
letter-spacing: 0.26em;
|
||
text-transform: uppercase;
|
||
color: var(--ks-patina);
|
||
}
|
||
.live-audience {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 30px;
|
||
text-align: left;
|
||
}
|
||
.live-audience-tile {
|
||
padding: 16px 0 0;
|
||
border-top: 2px solid var(--ks-patina);
|
||
}
|
||
.live-audience-role {
|
||
display: block;
|
||
margin-bottom: 11px;
|
||
font-family: var(--ks-mono);
|
||
font-size: 0.66rem;
|
||
letter-spacing: 0.2em;
|
||
text-transform: uppercase;
|
||
color: var(--ks-patina);
|
||
}
|
||
.live-audience-tile p {
|
||
margin: 0;
|
||
font-size: 0.92rem;
|
||
line-height: 1.55;
|
||
color: var(--ks-muted);
|
||
}
|
||
|
||
@media (max-width: 920px) {
|
||
.live-how { grid-template-columns: 1fr; }
|
||
.live-audience { grid-template-columns: 1fr; }
|
||
}
|