mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-18 09:06:53 +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>
1639 lines
42 KiB
CSS
1639 lines
42 KiB
CSS
/* ============================================================================
|
|
Impeccable — Neo Kinpaku kit (page-global primitives)
|
|
|
|
Reusable component primitives lifted from /design-system so they work on
|
|
every page. Brand lockup, section scaffolding, buttons, form controls,
|
|
tabs, badges, tags, pagination, toasts, tooltips, modal, empty state,
|
|
changelog, skeleton. No `.kinpaku-system-page` scope — drop these classes
|
|
onto any element on any page.
|
|
|
|
All values resolve through kinpaku-tokens.css. If a token name changes
|
|
there, this file follows.
|
|
|
|
Imported globally from Base.astro so every page picks them up.
|
|
============================================================================ */
|
|
|
|
|
|
/* ============================================================
|
|
Brand lockup — mark + wordmark
|
|
============================================================ */
|
|
|
|
.ks-brand {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.ks-mark {
|
|
width: 38px;
|
|
height: 38px;
|
|
display: grid;
|
|
place-items: center;
|
|
color: var(--ks-kinpaku);
|
|
}
|
|
|
|
.ks-mark svg { width: 32px; height: 32px; }
|
|
|
|
.ks-wordmark {
|
|
color: var(--ks-kinpaku);
|
|
font-family: var(--ks-font-wordmark);
|
|
font-weight: 400;
|
|
font-size: var(--ks-type-wordmark-size);
|
|
letter-spacing: var(--ks-type-wordmark-track);
|
|
text-transform: uppercase;
|
|
line-height: 1;
|
|
-webkit-font-smoothing: auto;
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Section scaffolding — page-level sections, eyebrows, subsections
|
|
============================================================ */
|
|
|
|
.ks-section {
|
|
max-width: 1320px;
|
|
margin: 0 auto;
|
|
padding: 110px 56px;
|
|
}
|
|
|
|
.ks-section + .ks-section {
|
|
border-top: 1px solid var(--ks-rule);
|
|
}
|
|
|
|
.ks-section-head {
|
|
display: grid;
|
|
gap: 14px;
|
|
margin-bottom: 56px;
|
|
max-width: 720px;
|
|
}
|
|
|
|
.ks-section-eyebrow {
|
|
color: var(--ks-kinpaku);
|
|
font-family: var(--ks-mono);
|
|
font-size: 0.7rem;
|
|
letter-spacing: 0.28em;
|
|
text-transform: uppercase;
|
|
margin: 0;
|
|
}
|
|
|
|
.ks-section-head h2 {
|
|
margin: 0;
|
|
color: var(--ks-champagne);
|
|
font-family: var(--ks-font-display);
|
|
font-size: clamp(2rem, 3.6vw, 3rem);
|
|
font-weight: var(--ks-type-headline-weight);
|
|
line-height: 1.06;
|
|
letter-spacing: -0.005em;
|
|
}
|
|
|
|
.ks-subsection {
|
|
margin-top: 56px;
|
|
}
|
|
|
|
.ks-subsection-label {
|
|
display: block;
|
|
color: var(--ks-kinpaku);
|
|
font-family: var(--ks-mono);
|
|
font-size: 0.66rem;
|
|
letter-spacing: 0.24em;
|
|
text-transform: uppercase;
|
|
margin-bottom: 22px;
|
|
}
|
|
|
|
.ks-section-sub {
|
|
color: var(--ks-text-muted);
|
|
font-size: 0.96rem;
|
|
line-height: 1.55;
|
|
margin: 4px 0 0;
|
|
max-width: 60ch;
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Buttons — primary, secondary, ghost, disabled
|
|
============================================================ */
|
|
|
|
.ks-button {
|
|
--b-shift: 0;
|
|
position: relative;
|
|
min-height: 52px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 14px;
|
|
padding: 0 28px;
|
|
border-radius: 2px;
|
|
font-size: 0.96rem;
|
|
font-weight: 500;
|
|
line-height: 1;
|
|
text-decoration: none;
|
|
border: 1px solid transparent;
|
|
cursor: pointer;
|
|
transition: transform 180ms var(--ks-ease), background-color 180ms var(--ks-ease), border-color 180ms var(--ks-ease), color 180ms var(--ks-ease);
|
|
}
|
|
|
|
.ks-button .ks-button-arrow {
|
|
width: 16px;
|
|
height: 8px;
|
|
display: inline-block;
|
|
flex: none;
|
|
}
|
|
|
|
.ks-button-arrow svg { width: 100%; height: 100%; display: block; }
|
|
|
|
/* Warm-gold flat primary. Hover lifts to pale; active darkens to rich.
|
|
Variants chain on .ks-button so specificity (0,2,0) wins over generic page
|
|
anchor resets like `.kinpaku-system-page a { color: inherit }` (0,1,1).
|
|
Use class="ks-button ks-button-primary" — both classes required. */
|
|
.ks-button.ks-button-primary {
|
|
color: var(--ks-lacquer-deep);
|
|
background: var(--ks-kinpaku);
|
|
border-color: var(--ks-kinpaku);
|
|
}
|
|
|
|
.ks-button.ks-button-primary:hover {
|
|
background: var(--ks-kinpaku-pale);
|
|
border-color: var(--ks-kinpaku-pale);
|
|
color: var(--ks-lacquer-deep);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.ks-button.ks-button-primary:active {
|
|
transform: translateY(0);
|
|
background: var(--ks-kinpaku-rich);
|
|
border-color: var(--ks-kinpaku-rich);
|
|
}
|
|
|
|
.ks-button.ks-button-secondary {
|
|
color: var(--ks-kinpaku);
|
|
background: transparent;
|
|
border-color: var(--ks-kinpaku);
|
|
}
|
|
|
|
.ks-button.ks-button-secondary:hover {
|
|
background: oklch(77% 0.14 82 / 0.08);
|
|
color: var(--ks-kinpaku);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.ks-button.ks-button-secondary:active {
|
|
background: oklch(77% 0.14 82 / 0.16);
|
|
transform: translateY(0);
|
|
}
|
|
|
|
.ks-button.ks-button-ghost {
|
|
color: var(--ks-champagne);
|
|
background: transparent;
|
|
border-color: transparent;
|
|
padding: 0 14px;
|
|
}
|
|
|
|
.ks-button.ks-button-ghost:hover {
|
|
color: var(--ks-kinpaku);
|
|
}
|
|
|
|
.ks-button[disabled],
|
|
.ks-button.ks-button-disabled {
|
|
color: var(--ks-text-mute-deep);
|
|
background: transparent;
|
|
border-color: var(--ks-rule);
|
|
cursor: not-allowed;
|
|
transform: none !important;
|
|
}
|
|
|
|
.ks-button:focus-visible {
|
|
outline: 2px solid var(--ks-patina);
|
|
outline-offset: 3px;
|
|
}
|
|
|
|
.ks-button-row {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 18px;
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Form controls — input, toggle, checkbox, select
|
|
============================================================ */
|
|
|
|
.ks-form-sample {
|
|
display: grid;
|
|
gap: 22px;
|
|
max-width: 360px;
|
|
}
|
|
|
|
.ks-form-sample label {
|
|
display: grid;
|
|
gap: 8px;
|
|
color: var(--ks-text-muted);
|
|
font-size: 0.82rem;
|
|
letter-spacing: 0.04em;
|
|
}
|
|
|
|
.ks-form-sample input[type="search"],
|
|
.ks-form-sample input[type="text"] {
|
|
min-height: 46px;
|
|
padding: 0 14px;
|
|
border: 1px solid var(--ks-rule);
|
|
border-radius: 2px;
|
|
background: var(--ks-lacquer-raised);
|
|
color: var(--ks-champagne);
|
|
font-size: 0.92rem;
|
|
}
|
|
|
|
.ks-form-sample input[type="search"]:focus,
|
|
.ks-form-sample input[type="text"]:focus {
|
|
outline: none;
|
|
border-color: var(--ks-patina);
|
|
}
|
|
|
|
.ks-toggle {
|
|
display: flex !important;
|
|
align-items: center;
|
|
gap: 12px !important;
|
|
}
|
|
|
|
.ks-toggle input {
|
|
appearance: none;
|
|
width: 44px;
|
|
height: 24px;
|
|
border-radius: 999px;
|
|
border: 1px solid var(--ks-rule);
|
|
background: var(--ks-graphite);
|
|
position: relative;
|
|
cursor: pointer;
|
|
flex: none;
|
|
}
|
|
|
|
.ks-toggle input::before {
|
|
content: "";
|
|
position: absolute;
|
|
width: 16px;
|
|
height: 16px;
|
|
left: 3px;
|
|
top: 3px;
|
|
border-radius: 999px;
|
|
background: var(--ks-text-muted);
|
|
transition: transform 220ms var(--ks-ease), background 220ms var(--ks-ease);
|
|
}
|
|
|
|
.ks-toggle input:checked {
|
|
border-color: var(--ks-patina);
|
|
background: oklch(48% 0.08 188 / 0.2);
|
|
}
|
|
|
|
.ks-toggle input:checked::before {
|
|
transform: translateX(20px);
|
|
background: var(--ks-patina);
|
|
}
|
|
|
|
.ks-toggle span { color: var(--ks-champagne); font-size: 0.92rem; }
|
|
|
|
.ks-checkbox {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
color: var(--ks-champagne);
|
|
font-size: 0.92rem;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.ks-checkbox input {
|
|
appearance: none;
|
|
width: 18px;
|
|
height: 18px;
|
|
border: 1px solid var(--ks-rule);
|
|
border-radius: 2px;
|
|
background: var(--ks-lacquer-raised);
|
|
position: relative;
|
|
cursor: pointer;
|
|
flex: none;
|
|
}
|
|
|
|
.ks-checkbox input:checked {
|
|
border-color: var(--ks-kinpaku);
|
|
background: var(--ks-kinpaku);
|
|
}
|
|
|
|
.ks-checkbox input:checked::after {
|
|
content: "";
|
|
position: absolute;
|
|
left: 5px;
|
|
top: 2px;
|
|
width: 5px;
|
|
height: 9px;
|
|
border: solid var(--ks-lacquer-deep);
|
|
border-width: 0 1.5px 1.5px 0;
|
|
transform: rotate(45deg);
|
|
}
|
|
|
|
.ks-select {
|
|
appearance: none;
|
|
min-height: 46px;
|
|
padding: 0 38px 0 14px;
|
|
border: 1px solid var(--ks-rule);
|
|
border-radius: 2px;
|
|
background-color: var(--ks-lacquer-raised);
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6' fill='none' stroke='%23c89a5a' stroke-width='1.2'%3E%3Cpath d='M1 1l4 4 4-4'/%3E%3C/svg%3E");
|
|
background-repeat: no-repeat;
|
|
background-position: right 14px center;
|
|
background-size: 10px 6px;
|
|
color: var(--ks-champagne);
|
|
font-size: 0.92rem;
|
|
cursor: pointer;
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Tabs
|
|
============================================================ */
|
|
|
|
.ks-tabs { max-width: 460px; }
|
|
|
|
.ks-tab-list { display: flex; border-bottom: 1px solid var(--ks-rule); }
|
|
|
|
.ks-tab-list button {
|
|
flex: 1;
|
|
min-height: 42px;
|
|
border: 0;
|
|
border-bottom: 2px solid transparent;
|
|
background: transparent;
|
|
color: var(--ks-text-muted);
|
|
cursor: pointer;
|
|
font-size: 0.92rem;
|
|
transition: color 180ms var(--ks-ease), border-color 180ms var(--ks-ease);
|
|
}
|
|
|
|
.ks-tab-list button:hover { color: var(--ks-champagne); }
|
|
|
|
.ks-tab-list button[aria-selected="true"] {
|
|
color: var(--ks-kinpaku);
|
|
border-bottom-color: var(--ks-kinpaku);
|
|
}
|
|
|
|
.ks-tab-panel {
|
|
padding: 22px 4px 0;
|
|
color: var(--ks-champagne);
|
|
font-size: 0.92rem;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Status badges + issue tags
|
|
============================================================ */
|
|
|
|
.ks-badge-row {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 12px;
|
|
}
|
|
|
|
.ks-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
min-height: 30px;
|
|
padding: 0 14px;
|
|
border: 1px solid currentColor;
|
|
border-radius: 2px;
|
|
font-size: 0.78rem;
|
|
letter-spacing: 0.06em;
|
|
}
|
|
|
|
.ks-badge::before {
|
|
content: "";
|
|
width: 6px;
|
|
height: 6px;
|
|
border-radius: 999px;
|
|
background: currentColor;
|
|
}
|
|
|
|
.ks-badge.is-detected { color: var(--ks-vermilion); }
|
|
.ks-badge.is-improved { color: var(--ks-patina); }
|
|
.ks-badge.is-ready { color: var(--ks-kinpaku); }
|
|
|
|
.ks-tag-row {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
}
|
|
|
|
.ks-tag {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
min-height: 26px;
|
|
padding: 0 12px;
|
|
border: 1px solid currentColor;
|
|
border-radius: 999px;
|
|
font-size: 0.76rem;
|
|
letter-spacing: 0.04em;
|
|
}
|
|
|
|
.ks-tag.is-detected { color: var(--ks-vermilion); }
|
|
.ks-tag.is-improved { color: var(--ks-patina); }
|
|
.ks-tag.is-neutral { color: var(--ks-text-muted); }
|
|
.ks-tag.is-ready { color: var(--ks-kinpaku); }
|
|
|
|
|
|
/* ============================================================
|
|
Pagination
|
|
============================================================ */
|
|
|
|
.ks-pagination {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
}
|
|
|
|
.ks-pagination button {
|
|
min-width: 36px;
|
|
min-height: 36px;
|
|
display: grid;
|
|
place-items: center;
|
|
border: 1px solid var(--ks-rule);
|
|
border-radius: 2px;
|
|
background: transparent;
|
|
color: var(--ks-champagne);
|
|
font-size: 0.86rem;
|
|
cursor: pointer;
|
|
transition: border-color 180ms var(--ks-ease), color 180ms var(--ks-ease);
|
|
}
|
|
|
|
.ks-pagination button:hover {
|
|
border-color: var(--ks-kinpaku);
|
|
color: var(--ks-kinpaku);
|
|
}
|
|
|
|
.ks-pagination button[aria-current="page"] {
|
|
border-color: var(--ks-kinpaku);
|
|
color: var(--ks-kinpaku);
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Toast / alert
|
|
============================================================ */
|
|
|
|
.ks-toast {
|
|
display: grid;
|
|
grid-template-columns: auto 1fr auto;
|
|
gap: 14px;
|
|
align-items: center;
|
|
padding: 14px 18px;
|
|
border: 1px solid currentColor;
|
|
border-radius: 2px;
|
|
max-width: 420px;
|
|
}
|
|
|
|
.ks-toast-icon {
|
|
width: 18px;
|
|
height: 18px;
|
|
display: grid;
|
|
place-items: center;
|
|
color: currentColor;
|
|
}
|
|
|
|
.ks-toast strong {
|
|
display: block;
|
|
color: var(--ks-champagne);
|
|
font-size: 0.92rem;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.ks-toast span:not(.ks-toast-close) {
|
|
color: var(--ks-text-muted);
|
|
font-size: 0.82rem;
|
|
display: block;
|
|
margin-top: 2px;
|
|
}
|
|
|
|
.ks-toast.is-success { color: var(--ks-patina); }
|
|
.ks-toast.is-warning { color: var(--ks-vermilion); }
|
|
|
|
.ks-toast-close {
|
|
width: 22px;
|
|
height: 22px;
|
|
display: grid;
|
|
place-items: center;
|
|
border: 0;
|
|
background: transparent;
|
|
color: var(--ks-text-muted);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.ks-toast-close svg { width: 12px; height: 12px; }
|
|
|
|
|
|
/* ============================================================
|
|
Icon button + tooltip
|
|
============================================================ */
|
|
|
|
.ks-icon-button {
|
|
width: 32px;
|
|
height: 32px;
|
|
display: grid;
|
|
place-items: center;
|
|
border: 1px solid var(--ks-rule);
|
|
border-radius: 999px;
|
|
background: transparent;
|
|
color: var(--ks-kinpaku);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.ks-tooltip {
|
|
position: absolute;
|
|
bottom: calc(100% - 18px);
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 200px;
|
|
padding: 10px 12px;
|
|
border: 1px solid var(--ks-rule);
|
|
background: var(--ks-lacquer-deep);
|
|
color: var(--ks-champagne);
|
|
font-size: 0.8rem;
|
|
line-height: 1.4;
|
|
border-radius: 2px;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
transition: opacity 160ms var(--ks-ease);
|
|
}
|
|
|
|
.ks-icon-button:focus-visible + .ks-tooltip {
|
|
opacity: 1;
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Modal
|
|
============================================================ */
|
|
|
|
.ks-modal {
|
|
position: relative;
|
|
max-width: 440px;
|
|
padding: 28px;
|
|
border: 1px solid var(--ks-rule);
|
|
background: var(--ks-lacquer-raised);
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.ks-modal h3 {
|
|
margin: 0 0 8px;
|
|
color: var(--ks-champagne);
|
|
font-size: 1.1rem;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.ks-modal p {
|
|
margin: 0 0 22px;
|
|
color: var(--ks-text-muted);
|
|
font-size: 0.92rem;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.ks-modal-actions {
|
|
display: flex;
|
|
gap: 12px;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.ks-modal-close {
|
|
position: absolute;
|
|
top: 16px;
|
|
right: 16px;
|
|
width: 24px;
|
|
height: 24px;
|
|
display: grid;
|
|
place-items: center;
|
|
border: 0;
|
|
background: transparent;
|
|
color: var(--ks-text-muted);
|
|
cursor: pointer;
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Empty state
|
|
============================================================ */
|
|
|
|
.ks-empty {
|
|
display: grid;
|
|
place-items: center;
|
|
gap: 14px;
|
|
padding: 56px 28px;
|
|
text-align: center;
|
|
border: 1px dashed var(--ks-rule);
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.ks-empty-icon {
|
|
width: 36px;
|
|
height: 36px;
|
|
display: grid;
|
|
place-items: center;
|
|
color: var(--ks-kinpaku);
|
|
}
|
|
|
|
.ks-empty strong {
|
|
color: var(--ks-champagne);
|
|
font-size: 1rem;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.ks-empty p {
|
|
margin: 0;
|
|
color: var(--ks-text-muted);
|
|
font-size: 0.86rem;
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Changelog row
|
|
============================================================ */
|
|
|
|
.ks-changelog {
|
|
display: grid;
|
|
gap: 18px;
|
|
}
|
|
|
|
.ks-changelog-entry {
|
|
display: grid;
|
|
grid-template-columns: 100px 1fr;
|
|
gap: 24px;
|
|
align-items: start;
|
|
padding-bottom: 18px;
|
|
border-bottom: 1px solid var(--ks-rule);
|
|
}
|
|
|
|
.ks-changelog-entry:last-child { border-bottom: 0; }
|
|
|
|
.ks-changelog-date {
|
|
color: var(--ks-text-muted);
|
|
font-family: var(--ks-mono);
|
|
font-size: 0.78rem;
|
|
}
|
|
|
|
.ks-changelog-body strong {
|
|
color: var(--ks-champagne);
|
|
font-weight: 400;
|
|
font-size: 0.96rem;
|
|
display: block;
|
|
}
|
|
|
|
.ks-changelog-body em {
|
|
display: inline-block;
|
|
margin-left: 8px;
|
|
padding: 1px 8px;
|
|
color: var(--ks-kinpaku);
|
|
border: 1px solid var(--ks-kinpaku);
|
|
font-style: normal;
|
|
font-size: 0.66rem;
|
|
letter-spacing: 0.18em;
|
|
text-transform: uppercase;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.ks-changelog-body p {
|
|
margin: 4px 0 0;
|
|
color: var(--ks-text-muted);
|
|
font-size: 0.86rem;
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Site chrome (kinpaku theme) — shared header AND footer
|
|
|
|
One definition for every kinpaku page, keyed off a single
|
|
`.kinpaku-chrome` body class instead of a per-page selector list. A
|
|
page opts into the dark-lacquer header + footer by adding
|
|
`kinpaku-chrome` to its bodyClass (alongside any page-specific class);
|
|
genuinely light pages (privacy, tutorials) simply omit it.
|
|
|
|
This is the reusable unit: the header markup is Header.astro, the
|
|
footer markup is Footer.astro, and everything visual lives here. Don't
|
|
re-declare header/footer chrome in per-page stylesheets — the only
|
|
legitimate per-page override is a deliberate brand moment (e.g. the
|
|
home page's textured `body.home-kinpaku .site-footer`).
|
|
|
|
Brand mark + wordmark mirror /design-system's .ks-brand / .ks-mark /
|
|
.ks-wordmark exactly, so the header is internally consistent. The
|
|
github pill and footer treatment used to be home-only / copy-pasted
|
|
per page; they now live here so every kinpaku page matches.
|
|
============================================================ */
|
|
|
|
.kinpaku-chrome .site-header {
|
|
padding: 28px clamp(22px, 4vw, 56px);
|
|
background: linear-gradient(180deg, oklch(5% 0.004 95 / 0.92), oklch(5% 0.004 95 / 0.68));
|
|
border-bottom: 1px solid oklch(78% 0.12 82 / 0.18);
|
|
backdrop-filter: blur(18px) saturate(1.25);
|
|
}
|
|
|
|
/* Hamburger toggle — hidden on desktop, shown on mobile. The toggle expands
|
|
the nav panel below the header. Two-line glyph styled in kinpaku gold so
|
|
it reads as the brand's own control, not a generic browser affordance. */
|
|
.site-header-menu {
|
|
display: none;
|
|
width: 36px;
|
|
height: 36px;
|
|
background: transparent;
|
|
border: 1px solid oklch(78% 0.12 82 / 0.32);
|
|
border-radius: 4px;
|
|
padding: 0;
|
|
cursor: pointer;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
flex: none;
|
|
margin-left: auto;
|
|
transition: border-color 160ms ease, background 160ms ease;
|
|
}
|
|
.site-header-menu:hover {
|
|
border-color: oklch(78% 0.12 82 / 0.55);
|
|
background: oklch(78% 0.12 82 / 0.06);
|
|
}
|
|
.site-header-menu-line {
|
|
display: block;
|
|
width: 16px;
|
|
height: 1.5px;
|
|
background: var(--ks-kinpaku);
|
|
transition: transform 200ms ease, opacity 200ms ease;
|
|
}
|
|
[data-site-header][data-nav-open="true"] .site-header-menu-line:nth-child(1) {
|
|
transform: translateY(3.75px) rotate(45deg);
|
|
}
|
|
[data-site-header][data-nav-open="true"] .site-header-menu-line:nth-child(2) {
|
|
transform: translateY(-3.75px) rotate(-45deg);
|
|
}
|
|
|
|
/* Mobile nav drawer. The right cluster (nav + github) collapses below the
|
|
header into a vertical panel that's hidden by default and revealed when
|
|
the hamburger toggles data-nav-open="true" on the header. */
|
|
@media (max-width: 760px) {
|
|
.kinpaku-chrome .site-header {
|
|
flex-wrap: wrap;
|
|
padding: 16px clamp(16px, 4vw, 24px);
|
|
row-gap: 12px;
|
|
}
|
|
|
|
.site-header-menu {
|
|
display: inline-flex;
|
|
}
|
|
|
|
/* The right cluster (nav + github) becomes a collapsible drawer. Default
|
|
hidden; revealed when the header carries data-nav-open="true". The
|
|
panel extends past the header's horizontal padding to read as a true
|
|
full-width drawer, with a solid background so it doesn't bleed into
|
|
the hero content behind it. */
|
|
[data-site-header] .site-header-right {
|
|
order: 3;
|
|
flex-basis: 100%;
|
|
display: none;
|
|
grid-template-columns: max-content max-content 1fr;
|
|
column-gap: 10px;
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
row-gap: 0;
|
|
margin: 12px 0 -16px;
|
|
padding: 6px;
|
|
background: oklch(4% 0.004 95);
|
|
border-radius: 4px;
|
|
border-top: 1px solid oklch(78% 0.12 82 / 0.18);
|
|
}
|
|
[data-site-header][data-nav-open="true"] .site-header-right {
|
|
display: grid;
|
|
}
|
|
|
|
.kinpaku-chrome .site-header-nav {
|
|
grid-column: 1 / -1;
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
gap: 0;
|
|
font-size: 1rem;
|
|
/* Drop sub-pages.css's horizontal-scroll mask + overflow on the nav. */
|
|
overflow: visible;
|
|
mask-image: none;
|
|
-webkit-mask-image: none;
|
|
}
|
|
/* Each row reads as a tappable surface — full-width, generous vertical
|
|
padding, hairline separators between rows. */
|
|
[data-site-header] .site-header-nav a {
|
|
display: block;
|
|
padding: 14px clamp(16px, 4vw, 24px);
|
|
border-bottom: 1px solid oklch(78% 0.12 82 / 0.06);
|
|
}
|
|
[data-site-header] .site-header-nav a:last-child {
|
|
border-bottom: 0;
|
|
}
|
|
|
|
/* sub-pages.css hides Home on mobile via `a[data-nav="home"] {display:none}`
|
|
because the brand acted as the home link in the old horizontal nav.
|
|
The drawer pattern surfaces all items, so override that here. */
|
|
[data-site-header] .site-header-nav a[data-nav="home"] {
|
|
display: block;
|
|
}
|
|
|
|
.kinpaku-chrome .theme-toggle {
|
|
grid-column: 1;
|
|
margin: 12px 0 6px;
|
|
}
|
|
|
|
.kinpaku-chrome .site-header-github {
|
|
grid-column: 2;
|
|
align-self: flex-start;
|
|
margin: 12px 0 6px;
|
|
}
|
|
|
|
/* sub-pages.css hides the github star count label on mobile. The drawer
|
|
has room for it, so bring it back. */
|
|
[data-site-header] .site-header-github-label {
|
|
display: inline;
|
|
}
|
|
}
|
|
|
|
.kinpaku-chrome .site-header-brand {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
color: var(--ks-kinpaku);
|
|
font-family: var(--ks-font);
|
|
text-decoration: none;
|
|
}
|
|
|
|
.kinpaku-chrome .site-header-brand-logo {
|
|
width: 38px;
|
|
height: 38px;
|
|
display: grid;
|
|
place-items: center;
|
|
color: var(--ks-kinpaku);
|
|
background: transparent;
|
|
box-shadow: none;
|
|
}
|
|
|
|
.kinpaku-chrome .site-header-brand-logo svg {
|
|
width: 32px;
|
|
height: 32px;
|
|
}
|
|
|
|
.kinpaku-chrome .site-header-brand-name,
|
|
.kinpaku-chrome .footer-logo {
|
|
color: var(--ks-kinpaku);
|
|
font-family: var(--ks-font-wordmark);
|
|
font-weight: 400;
|
|
font-size: var(--ks-type-wordmark-size);
|
|
letter-spacing: var(--ks-type-wordmark-track);
|
|
text-transform: uppercase;
|
|
line-height: 1;
|
|
/* main.css / sub-pages.css set -webkit-font-smoothing: antialiased on
|
|
<html> which renders thinner stems than the design-system page (which
|
|
uses the browser default). Match the design-system rendering so every
|
|
page's wordmark is visually identical, not just structurally the same. */
|
|
-webkit-font-smoothing: auto;
|
|
-moz-osx-font-smoothing: auto;
|
|
}
|
|
|
|
.kinpaku-chrome .site-header-nav {
|
|
gap: clamp(22px, 3vw, 48px);
|
|
font-family: var(--ks-font);
|
|
font-size: 0.95rem;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.kinpaku-chrome .theme-toggle,
|
|
.kinpaku-chrome .site-header-github {
|
|
height: 36px;
|
|
min-height: 36px;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.kinpaku-chrome .site-header-github {
|
|
padding: 0 12px 0 10px;
|
|
}
|
|
|
|
.kinpaku-chrome .site-header-nav a,
|
|
.kinpaku-chrome .site-header-github {
|
|
color: var(--ks-champagne);
|
|
}
|
|
|
|
.kinpaku-chrome .site-header-nav a:hover,
|
|
.kinpaku-chrome .site-header-nav a[aria-current="page"] {
|
|
color: var(--ks-kinpaku);
|
|
}
|
|
|
|
/* GitHub star pill. Previously home-only; now every kinpaku page gets it. */
|
|
.kinpaku-chrome .site-header-github {
|
|
border: 1px solid oklch(80% 0 0);
|
|
border-radius: 999px;
|
|
background: oklch(10% 0.006 95 / 0.74);
|
|
}
|
|
|
|
.kinpaku-chrome .site-header-github:hover {
|
|
color: var(--ks-patina);
|
|
border-color: var(--ks-patina);
|
|
background: oklch(70% 0.105 190 / 0.08);
|
|
}
|
|
|
|
.kinpaku-chrome .site-header-github-star {
|
|
color: var(--ks-kinpaku);
|
|
}
|
|
|
|
/* ============================================================
|
|
Site footer (kinpaku theme) — the default dark footer for every
|
|
kinpaku page. Was copy-pasted identically into slop/docs/designing/
|
|
live-mode stylesheets; lives here once now. The home page layers a
|
|
textured verdigris divider on top via `body.home-kinpaku .site-footer`
|
|
(higher specificity, so it wins regardless of import order).
|
|
============================================================ */
|
|
.kinpaku-chrome .site-footer {
|
|
background: oklch(4% 0.004 95);
|
|
border-top: 1px solid var(--ks-rule);
|
|
}
|
|
|
|
.kinpaku-chrome .footer-links a,
|
|
.kinpaku-chrome .footer-credit,
|
|
.kinpaku-chrome .footer-social-link {
|
|
color: var(--ks-text-muted);
|
|
}
|
|
|
|
.kinpaku-chrome .footer-credit a {
|
|
color: var(--ks-champagne);
|
|
}
|
|
|
|
.kinpaku-chrome .footer-links a:hover,
|
|
.kinpaku-chrome .footer-credit a:hover,
|
|
.kinpaku-chrome .footer-social-link:hover {
|
|
color: var(--ks-kinpaku);
|
|
}
|
|
|
|
/* ============================================================
|
|
Kinpaku surface — legacy-token remap
|
|
|
|
For pages whose content still reads the old light tokens
|
|
(--color-paper, --color-ink, --font-display, …): instead of overriding
|
|
every rule, remap those tokens to kinpaku values at the body scope so
|
|
the existing legacy CSS renders dark for free. This is the same trick
|
|
docs-kinpaku/designing-kinpaku use per page, lifted into one reusable
|
|
class. It is also the prototype for making kinpaku the global default —
|
|
the eventual move is to lift this remap to :root and delete the light
|
|
token values entirely.
|
|
|
|
Pair with .kinpaku-chrome (header + footer). Existing bespoke kinpaku
|
|
pages already remap in their own page stylesheet, so they don't need
|
|
this class.
|
|
============================================================ */
|
|
.kinpaku-surface {
|
|
--color-paper: oklch(7% 0.006 95);
|
|
--color-cream: oklch(9% 0.006 95);
|
|
--color-bg: oklch(11% 0.006 95);
|
|
--color-mist: var(--ks-rule);
|
|
--color-charcoal: var(--ks-text-muted);
|
|
--color-ash: var(--ks-text-faint);
|
|
--color-ink: var(--ks-champagne);
|
|
--color-text: var(--ks-text);
|
|
--color-accent: var(--ks-kinpaku);
|
|
--color-accent-dim: oklch(78% 0.12 82 / 0.18);
|
|
--color-accent-hover: var(--ks-kinpaku-pale);
|
|
--color-accent-soft: oklch(78% 0.12 82 / 0.32);
|
|
--font-body: var(--ks-font);
|
|
--font-display: var(--ks-font-display);
|
|
--font-mono: var(--ks-mono);
|
|
|
|
background: linear-gradient(180deg, oklch(7% 0.006 95), oklch(4% 0.004 95));
|
|
color: var(--ks-text);
|
|
min-height: 100vh;
|
|
}
|
|
|
|
/* The legacy display face was italic serif; the kinpaku display face
|
|
(Alumni Sans) renders wrong as synthesized italic, so reset it on
|
|
the headings the base stylesheet set to italic. */
|
|
.kinpaku-surface .sub-page-title,
|
|
.kinpaku-surface .prose h1,
|
|
.kinpaku-surface .prose h2 {
|
|
font-style: normal;
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Bento — multi-tile container pattern. The canonical alternative
|
|
to card-in-card. Tiles are full-width slabs on a darker rail; the
|
|
8px gap between them shows the rail through as inset gutters.
|
|
Use with .ks-bento-tile--span-4 / --span-6 / --span-8 across a
|
|
12-column grid. Pages can extend with full-bleed breakout or
|
|
custom edge padding (see /).
|
|
============================================================ */
|
|
|
|
.ks-bento {
|
|
display: grid;
|
|
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
gap: 8px;
|
|
background: oklch(0.17 0 0);
|
|
border-top: 8px solid oklch(0.17 0 0);
|
|
border-bottom: 8px solid oklch(0.17 0 0);
|
|
}
|
|
|
|
.ks-bento-tile {
|
|
position: relative;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 14px;
|
|
padding: 48px;
|
|
background: var(--ks-lacquer);
|
|
border: 0;
|
|
border-radius: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.ks-bento-tile--span-4 { grid-column: span 4; }
|
|
.ks-bento-tile--span-6 { grid-column: span 6; }
|
|
.ks-bento-tile--span-8 { grid-column: span 8; }
|
|
|
|
@media (max-width: 980px) {
|
|
.ks-bento-tile { grid-column: 1 / -1; }
|
|
}
|
|
|
|
/* Tile number — tiny mono caps. Defaults to kinpaku; flip individual tiles
|
|
to verdigris with data-color="patina" for tonal variety down a column. */
|
|
.ks-bento-num {
|
|
font-family: var(--ks-mono);
|
|
font-size: 0.62rem;
|
|
letter-spacing: 0.28em;
|
|
text-transform: uppercase;
|
|
color: var(--ks-kinpaku);
|
|
}
|
|
|
|
.ks-bento-num[data-color="patina"] {
|
|
color: var(--ks-patina);
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Skeleton (loading)
|
|
============================================================ */
|
|
|
|
.ks-skeleton {
|
|
display: grid;
|
|
gap: 8px;
|
|
max-width: 280px;
|
|
}
|
|
|
|
.ks-skeleton span {
|
|
height: 8px;
|
|
border-radius: 999px;
|
|
background: linear-gradient(90deg, var(--ks-graphite), oklch(22% 0.008 95), var(--ks-graphite));
|
|
background-size: 200% 100%;
|
|
animation: ks-shimmer 1.8s linear infinite;
|
|
}
|
|
|
|
.ks-skeleton span:nth-child(2) { width: 70%; }
|
|
.ks-skeleton span:nth-child(3) { width: 50%; }
|
|
|
|
@keyframes ks-shimmer {
|
|
0% { background-position: 200% 0; }
|
|
100% { background-position: -200% 0; }
|
|
}
|
|
|
|
|
|
/* ============================================================
|
|
Live picker (kinpaku theme)
|
|
|
|
Shared kinpaku styling for the .live-demo-* picker mock used on the
|
|
homepage and /live-mode. The base structural CSS lives in
|
|
site/styles/live-mode.css; this block re-skins the picker chrome
|
|
(browser frame URL bar, bottom global bar, context bar, target
|
|
outline, comment tag, stroke) on dark kinpaku surfaces.
|
|
|
|
Page-specific bits stay in the page CSS:
|
|
- .live-demo-frame background (lacquer-raised vs lacquer-deep)
|
|
- .live-demo-stage (homepage shows a gold-grid stylized stage;
|
|
/live-mode shows a light cream surface that simulates a real
|
|
dev preview)
|
|
- .live-demo-card (different content per page)
|
|
|
|
To extend the picker to a new kinpaku-themed page, just add its body
|
|
class to the selector lists below. No duplicating CSS in page files.
|
|
|
|
The actual live picker that injects into user dev servers
|
|
(skill/scripts/live-browser.js) is a separate runtime; its color
|
|
constants are kept in sync with these tokens by hand.
|
|
============================================================ */
|
|
|
|
/* Browser-chrome bar at the top of the mock (URL pill + dots). Refined to read
|
|
like a precise tool, not a stock screenshot frame: slimmer bar, small uniform
|
|
neutral dots, and a clean neutral-edged URL pill (no warm gold hairline). */
|
|
.home-kinpaku .live-demo-chrome,
|
|
.live-mode-kinpaku .live-demo-chrome {
|
|
padding: 9px 14px;
|
|
background: var(--ks-graphite);
|
|
border-bottom: 1px solid oklch(92% 0 0 / 0.07);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-dot,
|
|
.live-mode-kinpaku .live-demo-dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
background: oklch(34% 0 0);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-url,
|
|
.live-mode-kinpaku .live-demo-url {
|
|
margin-left: 12px;
|
|
font-family: var(--ks-mono);
|
|
font-size: 0.72rem;
|
|
color: var(--ks-text-muted);
|
|
letter-spacing: 0.04em;
|
|
background: transparent;
|
|
border: 1px solid oklch(92% 0 0 / 0.08);
|
|
}
|
|
|
|
/* Active control surfaces — the bottom picker bar and the context bar
|
|
that floats next to the picked element. These ARE the Impeccable UI,
|
|
so they get the full-saturation kinpaku-gold border at 1.5px with a
|
|
faint matching halo, distinguishing them from chrome. */
|
|
.home-kinpaku .live-demo-gbar,
|
|
.live-mode-kinpaku .live-demo-gbar,
|
|
.home-kinpaku .live-demo-ctx,
|
|
.live-mode-kinpaku .live-demo-ctx {
|
|
background: var(--ks-lacquer-deep);
|
|
/* Quiet neutral hairline instead of the loud 1.5px gold border + gold halo.
|
|
Gold is reserved for the brand mark and the active control, not the
|
|
container outline. */
|
|
border: 1px solid oklch(92% 0 0 / 0.13);
|
|
border-radius: 8px;
|
|
box-shadow: 0 16px 36px -12px oklch(0% 0 0 / 0.6);
|
|
color: var(--ks-champagne);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-gbar-brand,
|
|
.live-mode-kinpaku .live-demo-gbar-brand {
|
|
color: var(--ks-kinpaku);
|
|
background: transparent;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-gbar-btn,
|
|
.live-mode-kinpaku .live-demo-gbar-btn {
|
|
color: var(--ks-text-muted);
|
|
background: transparent;
|
|
}
|
|
|
|
/* Active state — a crisp neutral pill with kinpaku text. The translucent
|
|
kinpaku-dim wash read as a murky smudge; a solid graphite chip with gold
|
|
text/icon makes the selected control precise. */
|
|
.home-kinpaku .live-demo-gbar-btn.is-active,
|
|
.live-mode-kinpaku .live-demo-gbar-btn.is-active {
|
|
background: oklch(27% 0 0);
|
|
color: var(--ks-kinpaku);
|
|
border-radius: 6px;
|
|
}
|
|
|
|
/* Page chat — collapsed pill with chat + mic; expands on click. */
|
|
.home-kinpaku .live-demo-gbar-chat,
|
|
.live-mode-kinpaku .live-demo-gbar-chat {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
height: 28px;
|
|
width: 104px;
|
|
margin: 0 4px 0 6px;
|
|
border-radius: 5px;
|
|
background: var(--ks-graphite);
|
|
border: 1px solid transparent;
|
|
overflow: hidden;
|
|
cursor: pointer;
|
|
flex-shrink: 0;
|
|
transition: border-color 0.15s ease;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-gbar-chat.is-expanded,
|
|
.live-mode-kinpaku .live-demo-gbar-chat.is-expanded {
|
|
width: min(220px, 34vw);
|
|
cursor: text;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-gbar-chat-icon,
|
|
.live-mode-kinpaku .live-demo-gbar-chat-icon {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 28px;
|
|
height: 28px;
|
|
flex-shrink: 0;
|
|
color: var(--ks-text-muted);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-gbar-chat-hint,
|
|
.live-mode-kinpaku .live-demo-gbar-chat-hint {
|
|
flex: 1;
|
|
min-width: 0;
|
|
font-size: 11.5px;
|
|
font-weight: 500;
|
|
color: var(--ks-text-muted);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-gbar-chat-input,
|
|
.live-mode-kinpaku .live-demo-gbar-chat-input {
|
|
flex: 1;
|
|
min-width: 0;
|
|
width: 0;
|
|
padding: 0;
|
|
border: 0;
|
|
background: transparent;
|
|
font: inherit;
|
|
font-size: 11.5px;
|
|
color: var(--ks-champagne);
|
|
outline: none;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
caret-color: var(--ks-kinpaku);
|
|
transition: opacity 0.15s ease;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-gbar-chat[data-input-focused="true"]:not([data-expanded="true"]) .live-demo-gbar-chat-hint,
|
|
.live-mode-kinpaku .live-demo-gbar-chat[data-input-focused="true"]:not([data-expanded="true"]) .live-demo-gbar-chat-hint {
|
|
display: none;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-gbar-chat[data-input-focused="true"]:not([data-expanded="true"]) .live-demo-gbar-chat-input,
|
|
.live-mode-kinpaku .live-demo-gbar-chat[data-input-focused="true"]:not([data-expanded="true"]) .live-demo-gbar-chat-input {
|
|
width: auto;
|
|
padding: 0 4px;
|
|
opacity: 1;
|
|
pointer-events: auto;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-gbar-chat.is-expanded .live-demo-gbar-chat-hint,
|
|
.live-mode-kinpaku .live-demo-gbar-chat.is-expanded .live-demo-gbar-chat-hint {
|
|
display: none;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-gbar-chat.is-expanded .live-demo-gbar-chat-input,
|
|
.live-mode-kinpaku .live-demo-gbar-chat.is-expanded .live-demo-gbar-chat-input {
|
|
width: auto;
|
|
padding: 0 6px;
|
|
opacity: 1;
|
|
pointer-events: auto;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-gbar-chat-input::placeholder,
|
|
.live-mode-kinpaku .live-demo-gbar-chat-input::placeholder {
|
|
color: var(--ks-text-muted);
|
|
opacity: 1;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-gbar-chat-voice,
|
|
.live-mode-kinpaku .live-demo-gbar-chat-voice {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 28px;
|
|
height: 28px;
|
|
padding: 0;
|
|
border: 0;
|
|
background: transparent;
|
|
color: var(--ks-text-muted);
|
|
cursor: pointer;
|
|
flex-shrink: 0;
|
|
transition: color 0.12s ease, background 0.12s ease;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-gbar-chat-voice:hover,
|
|
.live-mode-kinpaku .live-demo-gbar-chat-voice:hover,
|
|
.home-kinpaku .live-demo-gbar-chat-voice.is-active,
|
|
.live-mode-kinpaku .live-demo-gbar-chat-voice.is-active {
|
|
color: var(--ks-kinpaku);
|
|
background: oklch(78% 0.12 82 / 0.12);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-gbar-divider,
|
|
.live-mode-kinpaku .live-demo-gbar-divider {
|
|
background: var(--ks-rule);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-gbar-x,
|
|
.live-mode-kinpaku .live-demo-gbar-x {
|
|
color: var(--ks-text-muted);
|
|
background: transparent;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-gbar-x:hover,
|
|
.live-mode-kinpaku .live-demo-gbar-x:hover {
|
|
color: var(--ks-vermilion);
|
|
}
|
|
|
|
/* Context bar sub-elements (the inline action panel that appears
|
|
next to a picked element during "configure" / "cycling" / "accepted"
|
|
states). */
|
|
.home-kinpaku .live-demo-ctx[data-phase="configuring"],
|
|
.live-mode-kinpaku .live-demo-ctx[data-phase="configuring"] {
|
|
padding: 0;
|
|
background: var(--ks-graphite-2);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-command,
|
|
.live-mode-kinpaku .live-demo-ctx-command {
|
|
display: flex;
|
|
align-items: center;
|
|
flex: 1;
|
|
min-width: 0;
|
|
height: 36px;
|
|
padding: 0 6px 0 7px;
|
|
background: transparent;
|
|
border: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-trailing,
|
|
.live-mode-kinpaku .live-demo-ctx-trailing {
|
|
display: inline-flex;
|
|
align-items: stretch;
|
|
flex-shrink: 0;
|
|
border-left: 1px solid oklch(92% 0 0 / 0.12);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-modifiers,
|
|
.live-mode-kinpaku .live-demo-ctx-modifiers {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 0 10px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-selection,
|
|
.live-mode-kinpaku .live-demo-ctx-selection {
|
|
box-sizing: border-box;
|
|
min-width: 28px;
|
|
height: auto;
|
|
padding: 1px 4px;
|
|
border: 1px solid oklch(70% 0.12 188);
|
|
border-radius: 7px;
|
|
flex-shrink: 0;
|
|
background: transparent;
|
|
color: oklch(70% 0.12 188);
|
|
font-family: var(--ks-mono);
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
line-height: 18px;
|
|
transition: background 0.15s ease, color 0.15s ease;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-action,
|
|
.home-kinpaku .live-demo-ctx-count,
|
|
.live-mode-kinpaku .live-demo-ctx-action,
|
|
.live-mode-kinpaku .live-demo-ctx-count {
|
|
box-sizing: border-box;
|
|
height: 18px;
|
|
padding: 0;
|
|
border: 0;
|
|
border-radius: 0;
|
|
background: transparent;
|
|
color: var(--ks-text-muted);
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
line-height: 18px;
|
|
transition: color 0.12s ease;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-count,
|
|
.live-mode-kinpaku .live-demo-ctx-count {
|
|
font-family: var(--ks-mono);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-row--configure,
|
|
.live-mode-kinpaku .live-demo-ctx-row--configure {
|
|
width: 100%;
|
|
min-height: 36px;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-actions,
|
|
.live-mode-kinpaku .live-demo-ctx-actions {
|
|
display: inline-flex;
|
|
align-items: stretch;
|
|
border-left: 1px solid oklch(92% 0 0 / 0.12);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-voice,
|
|
.live-mode-kinpaku .live-demo-ctx-voice {
|
|
width: 36px;
|
|
height: 36px;
|
|
padding: 0;
|
|
border: 0;
|
|
border-right: 1px solid oklch(92% 0 0 / 0.12);
|
|
border-radius: 0;
|
|
background: transparent;
|
|
color: var(--ks-text-muted);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-actions .live-demo-ctx-go,
|
|
.live-mode-kinpaku .live-demo-ctx-actions .live-demo-ctx-go {
|
|
width: 36px;
|
|
height: 36px;
|
|
padding: 0;
|
|
border-radius: 0;
|
|
border-left: 0;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-action-caret,
|
|
.live-mode-kinpaku .live-demo-ctx-action-caret {
|
|
font-size: 10px;
|
|
color: inherit;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-selection:hover,
|
|
.live-mode-kinpaku .live-demo-ctx-selection:hover {
|
|
background: oklch(27% 0 0);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-action:hover,
|
|
.home-kinpaku .live-demo-ctx-count:hover,
|
|
.live-mode-kinpaku .live-demo-ctx-action:hover,
|
|
.live-mode-kinpaku .live-demo-ctx-count:hover {
|
|
color: var(--ks-text);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-input,
|
|
.live-mode-kinpaku .live-demo-ctx-input {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
flex: 1;
|
|
min-width: 0;
|
|
height: 18px;
|
|
padding: 0;
|
|
background: transparent;
|
|
border: 0;
|
|
border-radius: 0;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
line-height: 18px;
|
|
color: oklch(94% 0.02 82);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-caret,
|
|
.live-mode-kinpaku .live-demo-ctx-caret {
|
|
background: oklch(94% 0.02 82);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-go,
|
|
.live-mode-kinpaku .live-demo-ctx-go,
|
|
.home-kinpaku .live-demo-ctx-accept,
|
|
.live-mode-kinpaku .live-demo-ctx-accept {
|
|
background: var(--ks-kinpaku);
|
|
color: var(--ks-lacquer-deep);
|
|
border: 0;
|
|
}
|
|
|
|
/* Command-palette switcher — the picker's design vocabulary. Mirrors the real
|
|
action picker in live-browser.js: a 4-column grid of icon-over-label chips,
|
|
the selected chip on a soft kinpaku wash with its icon recolored to brand.
|
|
This is the move that makes the live approach unique, so the demo shows it. */
|
|
.home-kinpaku .live-demo-ctx-palette,
|
|
.live-mode-kinpaku .live-demo-ctx-palette {
|
|
position: absolute;
|
|
left: 6px;
|
|
bottom: calc(100% + 8px);
|
|
z-index: 2;
|
|
padding: 6px;
|
|
border-radius: 8px;
|
|
background: var(--ks-lacquer-deep);
|
|
border: 1px solid oklch(92% 0 0 / 0.13);
|
|
box-shadow: 0 18px 40px -14px oklch(0% 0 0 / 0.7);
|
|
opacity: 0;
|
|
transform: translateY(6px) scale(0.97);
|
|
transform-origin: bottom left;
|
|
pointer-events: none;
|
|
transition: opacity 160ms ease, transform 200ms cubic-bezier(0.22, 1, 0.36, 1);
|
|
}
|
|
.home-kinpaku .live-demo-ctx-palette.is-open,
|
|
.live-mode-kinpaku .live-demo-ctx-palette.is-open {
|
|
opacity: 1;
|
|
transform: none;
|
|
}
|
|
.home-kinpaku .live-demo-ctx-palette-grid,
|
|
.live-mode-kinpaku .live-demo-ctx-palette-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
gap: 3px;
|
|
}
|
|
.home-kinpaku .live-demo-ctx-palette-chip,
|
|
.live-mode-kinpaku .live-demo-ctx-palette-chip {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 5px;
|
|
width: 62px;
|
|
padding: 9px 6px;
|
|
border: 0;
|
|
border-radius: 6px;
|
|
background: transparent;
|
|
color: var(--ks-champagne);
|
|
font-family: var(--ks-font);
|
|
font-size: 11px;
|
|
font-weight: 500;
|
|
white-space: nowrap;
|
|
cursor: pointer;
|
|
transition: background 0.12s ease, color 0.12s ease;
|
|
}
|
|
.home-kinpaku .live-demo-ctx-palette-ico,
|
|
.live-mode-kinpaku .live-demo-ctx-palette-ico {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 20px;
|
|
opacity: 0.82;
|
|
}
|
|
.home-kinpaku .live-demo-ctx-palette-name,
|
|
.live-mode-kinpaku .live-demo-ctx-palette-name {
|
|
line-height: 1;
|
|
}
|
|
.home-kinpaku .live-demo-ctx-palette-chip.is-highlight,
|
|
.live-mode-kinpaku .live-demo-ctx-palette-chip.is-highlight {
|
|
background: oklch(78% 0.12 82 / 0.16);
|
|
color: var(--ks-kinpaku);
|
|
}
|
|
.home-kinpaku .live-demo-ctx-palette-chip.is-highlight .live-demo-ctx-palette-ico,
|
|
.live-mode-kinpaku .live-demo-ctx-palette-chip.is-highlight .live-demo-ctx-palette-ico {
|
|
opacity: 1;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-nav,
|
|
.live-mode-kinpaku .live-demo-ctx-nav {
|
|
background: transparent;
|
|
color: var(--ks-text-muted);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-nav:hover,
|
|
.live-mode-kinpaku .live-demo-ctx-nav:hover {
|
|
color: var(--ks-kinpaku);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-counter,
|
|
.live-mode-kinpaku .live-demo-ctx-counter {
|
|
color: var(--ks-champagne);
|
|
font-family: var(--ks-mono);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-divider,
|
|
.live-mode-kinpaku .live-demo-ctx-divider {
|
|
background: var(--ks-rule);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-discard,
|
|
.live-mode-kinpaku .live-demo-ctx-discard {
|
|
background: transparent;
|
|
color: var(--ks-text-muted);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-discard:hover,
|
|
.live-mode-kinpaku .live-demo-ctx-discard:hover {
|
|
color: var(--ks-vermilion);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-spinner,
|
|
.live-mode-kinpaku .live-demo-ctx-spinner {
|
|
border-color: var(--ks-rule);
|
|
border-top-color: var(--ks-kinpaku);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-ctx-row--accepted,
|
|
.live-mode-kinpaku .live-demo-ctx-row--accepted {
|
|
color: var(--ks-kinpaku);
|
|
}
|
|
|
|
/* Target outline + annotations (stroke + comment tag) — kinpaku. */
|
|
.home-kinpaku .live-demo-outline,
|
|
.live-mode-kinpaku .live-demo-outline {
|
|
border-color: var(--ks-kinpaku);
|
|
border-width: 1.5px;
|
|
border-radius: 3px;
|
|
color: var(--ks-kinpaku);
|
|
/* No soft gold glow ring (off-brand accent halo); a crisp outline reads as a
|
|
precise selection, not a highlighter. */
|
|
box-shadow: none;
|
|
}
|
|
|
|
.home-kinpaku .live-demo-annotations,
|
|
.live-mode-kinpaku .live-demo-annotations {
|
|
color: var(--ks-kinpaku);
|
|
border-color: var(--ks-kinpaku);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-comment,
|
|
.live-mode-kinpaku .live-demo-comment {
|
|
background: var(--ks-kinpaku);
|
|
color: var(--ks-lacquer-deep);
|
|
}
|
|
|
|
.home-kinpaku .live-demo-stroke,
|
|
.live-mode-kinpaku .live-demo-stroke {
|
|
color: var(--ks-kinpaku);
|
|
}
|