/* ========================================================================== AI PLAYGROUND — COMPONENTS Reusable parts: navigation, buttons, cards, controls, panels, states, dialogs, visualisation chrome. Composed by both the homepage and the labs. ========================================================================== */ /* ========================================================================== 01 — NAVIGATION ========================================================================== */ .site-nav { position: sticky; top: 0; z-index: var(--z-nav); background: color-mix(in srgb, var(--paper) 88%, transparent); backdrop-filter: saturate(160%) blur(14px); -webkit-backdrop-filter: saturate(160%) blur(14px); border-bottom: var(--rule) solid var(--line); transition: border-color var(--dur-2) var(--ease), background-color var(--dur-2) var(--ease); } @supports not (backdrop-filter: blur(1px)) { .site-nav { background: var(--paper); } } .site-nav__inner { display: flex; align-items: center; gap: var(--sp-5); height: 4.25rem; } /* --- Wordmark ---------------------------------------------------------- */ .wordmark { display: inline-flex; align-items: center; gap: var(--sp-3); flex: none; padding-block: var(--sp-2); } .wordmark__mark { width: 1.375rem; height: 1.375rem; flex: none; overflow: visible; } .wordmark__text { font-family: var(--font-sans); font-size: var(--t-16); font-weight: var(--fw-semi); letter-spacing: var(--tr-tight); white-space: nowrap; } .wordmark__text span { color: var(--ink-muted); font-weight: var(--fw-regular); } .wordmark:hover .wordmark__mark { animation: spin 8s linear infinite; } /* --- Links ------------------------------------------------------------- */ .nav-links { display: flex; align-items: center; gap: var(--sp-1); } .nav-link { position: relative; display: inline-block; padding: var(--sp-2) var(--sp-3); font-size: var(--t-14); font-weight: var(--fw-medium); letter-spacing: var(--tr-mid); color: var(--ink-secondary); border-radius: var(--r-1); transition: color var(--dur-2) var(--ease); } .nav-link::after { content: ""; position: absolute; left: var(--sp-3); right: var(--sp-3); bottom: 0.25rem; height: var(--rule); background: currentColor; transform: scaleX(0); transform-origin: left; transition: transform var(--dur-2) var(--ease); } .nav-link:hover { color: var(--ink); } .nav-link:hover::after { transform: scaleX(1); } .nav-link[aria-current="page"] { color: var(--ink); } .nav-link[aria-current="page"]::after { transform: scaleX(1); background: var(--accent); height: var(--rule-thick); } .nav-actions { display: flex; align-items: center; gap: var(--sp-2); margin-left: auto; } /* --- Icon buttons ------------------------------------------------------ */ .icon-btn { display: inline-grid; place-items: center; width: 2.25rem; height: 2.25rem; border-radius: var(--r-2); color: var(--ink-secondary); border: var(--rule) solid transparent; transition: color var(--dur-2) var(--ease), background-color var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease); } .icon-btn:hover { color: var(--ink); background: var(--paper-sunk); border-color: var(--line); } .icon-btn svg { width: 1.125rem; height: 1.125rem; } /* Theme toggle swaps icons by theme, both present for no-JS resilience */ [data-theme="dark"] .icon-moon, .icon-sun { display: none; } [data-theme="dark"] .icon-sun { display: block; } .icon-moon { display: block; } .nav-toggle { display: none; } @media (max-width: 1000px) { .nav-links { display: none; } .nav-toggle { display: inline-grid; } .nav-actions .btn--nav { display: none; } } /* --- Mobile drawer ----------------------------------------------------- */ .drawer { position: fixed; inset: 0; z-index: var(--z-drawer); visibility: hidden; pointer-events: none; } .drawer.is-open { visibility: visible; pointer-events: auto; } .drawer__scrim { position: absolute; inset: 0; background: var(--scrim); opacity: 0; transition: opacity var(--dur-3) var(--ease); } .drawer.is-open .drawer__scrim { opacity: 1; } .drawer__panel { position: absolute; inset-block: 0; right: 0; width: min(26rem, 100%); background: var(--paper); border-left: var(--rule) solid var(--line); display: flex; flex-direction: column; transform: translateX(100%); transition: transform var(--dur-3) var(--ease); overflow-y: auto; overscroll-behavior: contain; } .drawer.is-open .drawer__panel { transform: none; } .drawer__head { display: flex; align-items: center; justify-content: space-between; height: 4.25rem; padding-inline: var(--sp-5); border-bottom: var(--rule) solid var(--line); flex: none; } .drawer__body { padding: var(--sp-6) var(--sp-5) var(--sp-8); } .drawer__group + .drawer__group { margin-top: var(--sp-7); } .drawer__group-label { display: block; margin-bottom: var(--sp-4); padding-bottom: var(--sp-3); border-bottom: var(--rule) solid var(--line); } .drawer__link { display: flex; align-items: baseline; gap: var(--sp-4); padding: var(--sp-4) 0; border-bottom: var(--rule) solid var(--line); font-size: var(--t-21); font-family: var(--font-display); letter-spacing: var(--tr-mid); transition: padding-left var(--dur-2) var(--ease), color var(--dur-2) var(--ease); } .drawer__link:hover, .drawer__link:focus-visible { padding-left: var(--sp-3); color: var(--accent-ink); } .drawer__link .mono { color: var(--ink-faint); flex: none; } .drawer__link .drawer__meta { margin-left: auto; font-family: var(--font-mono); font-size: var(--t-10); letter-spacing: var(--tr-label); text-transform: uppercase; color: var(--ink-faint); } /* ========================================================================== 02 — BUTTONS ========================================================================== */ .btn { --btn-pad-y: 0.7rem; --btn-pad-x: 1.25rem; position: relative; display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2); padding: var(--btn-pad-y) var(--btn-pad-x); font-family: var(--font-sans); font-size: var(--t-14); font-weight: var(--fw-medium); letter-spacing: var(--tr-mid); line-height: 1.15; border: var(--rule) solid transparent; border-radius: var(--r-2); cursor: pointer; text-align: center; white-space: nowrap; min-height: 2.75rem; /* comfortable touch target */ transition: background-color var(--dur-2) var(--ease), color var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease), transform var(--dur-1) var(--ease); } .btn:active { transform: translateY(1px); } .btn svg { width: 1rem; height: 1rem; flex: none; } .btn--primary { background: var(--ink); color: var(--ink-inverse); border-color: var(--ink); } .btn--primary:hover { background: var(--accent); border-color: var(--accent); color: var(--accent-on); } .btn--accent { background: var(--accent); color: var(--accent-on); border-color: var(--accent); } .btn--accent:hover { filter: brightness(1.1); } .btn--outline { background: transparent; color: var(--ink); border-color: var(--line-strong); } .btn--outline:hover { border-color: var(--ink); background: var(--paper-sunk); } .btn--ghost { background: transparent; color: var(--ink-secondary); border-color: transparent; } .btn--ghost:hover { color: var(--ink); background: var(--paper-sunk); } .btn--sm { --btn-pad-y: 0.45rem; --btn-pad-x: 0.85rem; font-size: var(--t-12); min-height: 2.25rem; } .btn--lg { --btn-pad-y: 0.95rem; --btn-pad-x: 1.75rem; font-size: var(--t-16); min-height: 3.25rem; } .btn--block { width: 100%; } .btn[disabled], .btn[aria-disabled="true"] { opacity: 0.4; cursor: not-allowed; pointer-events: none; } /* Arrow that slides on hover — the one flourish on primary CTAs */ .btn__arrow { transition: transform var(--dur-2) var(--ease); } .btn:hover .btn__arrow { transform: translateX(3px); } /* Busy state: a hairline sweep along the bottom edge */ .btn.is-busy { pointer-events: none; opacity: 0.75; } .btn.is-busy::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px; overflow: hidden; background: linear-gradient(90deg, transparent, currentColor, transparent) ; background-size: 40% 100%; background-repeat: no-repeat; animation: btn-sweep 1.1s linear infinite; } @keyframes btn-sweep { from { background-position: -50% 0; } to { background-position: 150% 0; } } /* ========================================================================== 03 — TAGS, CHIPS, PILLS ========================================================================== */ .tag { display: inline-flex; align-items: center; gap: var(--sp-2); padding: 0.3rem 0.55rem; font-family: var(--font-mono); font-size: var(--t-10); font-weight: var(--fw-medium); letter-spacing: var(--tr-label); text-transform: uppercase; line-height: 1; border: var(--rule) solid var(--line); border-radius: var(--r-1); color: var(--ink-secondary); white-space: nowrap; } .tag--accent { border-color: var(--accent-line); background: var(--accent-wash); color: var(--accent-ink); } .tag--fill { background: var(--accent); border-color: var(--accent); color: var(--accent-on); } .tag--dot::before { content: ""; width: 0.4rem; height: 0.4rem; border-radius: 50%; background: var(--accent); flex: none; } /* Live/offline status */ .status-dot { display: inline-flex; align-items: center; gap: var(--sp-2); font-family: var(--font-mono); font-size: var(--t-10); letter-spacing: var(--tr-label); text-transform: uppercase; color: var(--ink-muted); } .status-dot::before { content: ""; width: 0.4rem; height: 0.4rem; border-radius: 50%; background: var(--green-700); flex: none; } .status-dot[data-status="offline"]::before { background: var(--ink-faint); } .status-dot[data-status="degraded"]::before { background: var(--orange-600); } .status-dot[data-status="live"]::before { animation: pulse-soft 2.4s ease-in-out infinite; } /* Filter chip */ .chip { display: inline-flex; align-items: center; gap: var(--sp-2); padding: 0.5rem 0.85rem; min-height: 2.25rem; font-size: var(--t-12); font-weight: var(--fw-medium); letter-spacing: var(--tr-none); border: var(--rule) solid var(--line); border-radius: var(--r-pill); color: var(--ink-secondary); background: transparent; cursor: pointer; transition: all var(--dur-2) var(--ease); } .chip:hover { border-color: var(--ink); color: var(--ink); } .chip[aria-pressed="true"], .chip.is-active { background: var(--ink); border-color: var(--ink); color: var(--ink-inverse); } .chip__count { font-family: var(--font-mono); font-size: var(--t-10); opacity: 0.6; } /* ========================================================================== 04 — LAB CARD (grid + list views) ========================================================================== */ .lab-card { position: relative; display: flex; flex-direction: column; background: var(--paper-raised); border: var(--rule) solid var(--line); border-radius: var(--r-2); overflow: hidden; transition: border-color var(--dur-2) var(--ease), transform var(--dur-3) var(--ease), box-shadow var(--dur-3) var(--ease); } .lab-card:hover, .lab-card:focus-within { border-color: var(--accent); transform: translateY(-3px); box-shadow: var(--shadow-md); } /* Whole card is clickable; the CTA carries the accessible name */ .lab-card__link::after { content: ""; position: absolute; inset: 0; } .lab-card__thumb { position: relative; aspect-ratio: 16 / 10; background: var(--accent-wash); border-bottom: var(--rule) solid var(--line); overflow: hidden; display: grid; place-items: center; } .lab-card__thumb canvas, .lab-card__thumb svg { width: 100%; height: 100%; display: block; } .lab-card__num { position: absolute; top: var(--sp-3); left: var(--sp-4); font-family: var(--font-mono); font-size: var(--t-10); letter-spacing: var(--tr-label); color: var(--accent-ink); z-index: 2; } .lab-card__body { padding: var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-3); flex: 1; } .lab-card__title { font-family: var(--font-display); font-size: var(--t-28); line-height: var(--lh-tight); letter-spacing: var(--tr-tight); } .lab-card__desc { font-size: var(--t-14); line-height: var(--lh-prose); color: var(--ink-secondary); text-wrap: pretty; } .lab-card__meta { margin-top: auto; padding-top: var(--sp-4); border-top: var(--rule) solid var(--line); display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-3) var(--sp-4); } .meta-pair { display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; } .meta-pair dt { font-family: var(--font-mono); font-size: var(--t-9); letter-spacing: var(--tr-caps); text-transform: uppercase; color: var(--ink-faint); } .meta-pair dd { font-size: var(--t-12); font-weight: var(--fw-medium); color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .lab-card__cta { display: inline-flex; align-items: center; gap: var(--sp-2); font-size: var(--t-13, 0.8125rem); font-weight: var(--fw-semi); color: var(--accent-ink); letter-spacing: var(--tr-mid); } .lab-card:hover .lab-card__cta .btn__arrow { transform: translateX(4px); } /* List view: same markup, editorial row layout */ .lab-grid[data-view="list"] { display: block; border-top: var(--rule) solid var(--line-ink); } .lab-grid[data-view="list"] .lab-card { display: grid; grid-template-columns: 3.5rem minmax(0, 1.1fr) minmax(0, 1.4fr) auto; align-items: center; gap: var(--sp-5); border: none; border-bottom: var(--rule) solid var(--line); border-radius: 0; padding: var(--sp-5) var(--sp-2); transform: none; box-shadow: none; background: transparent; } .lab-grid[data-view="list"] .lab-card:hover { background: var(--accent-wash); transform: none; } .lab-grid[data-view="list"] .lab-card__thumb { display: none; } .lab-grid[data-view="list"] .lab-card__body { display: contents; } .lab-grid[data-view="list"] .lab-card__num { position: static; align-self: center; } .lab-grid[data-view="list"] .lab-card__title { font-size: var(--t-24); } .lab-grid[data-view="list"] .lab-card__tags { display: none; } .lab-grid[data-view="list"] .lab-card__desc { font-size: var(--t-14); margin: 0; } .lab-grid[data-view="list"] .lab-card__meta { border: none; padding: 0; margin: 0; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-4); } @media (max-width: 900px) { .lab-grid[data-view="list"] .lab-card { grid-template-columns: 2.5rem minmax(0, 1fr); gap: var(--sp-3); } .lab-grid[data-view="list"] .lab-card__meta { grid-template-columns: repeat(2, 1fr); } } /* View switcher */ .view-switch { display: inline-flex; border: var(--rule) solid var(--line); border-radius: var(--r-2); overflow: hidden; } .view-switch button { display: grid; place-items: center; width: 2.4rem; height: 2.25rem; color: var(--ink-muted); transition: all var(--dur-2) var(--ease); } .view-switch button + button { border-left: var(--rule) solid var(--line); } .view-switch button[aria-pressed="true"] { background: var(--ink); color: var(--ink-inverse); } .view-switch svg { width: 1rem; height: 1rem; } /* ========================================================================== 05 — PANELS ========================================================================== */ .panel { border: var(--rule) solid var(--line); border-radius: var(--r-2); background: var(--paper-raised); overflow: hidden; } .panel--flush { border-radius: 0; border-inline: none; } .panel--wash { background: var(--accent-wash); border-color: var(--accent-line); } .panel__head { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-4) var(--sp-5); border-bottom: var(--rule) solid var(--line); } .panel__body { padding: var(--sp-5); } .panel__foot { padding: var(--sp-4) var(--sp-5); border-top: var(--rule) solid var(--line); background: var(--paper-sunk); } /* Disclosure panel — "How it works", "What to notice", technical toggles */ .disclosure { border-top: var(--rule) solid var(--line); } .disclosure:last-of-type { border-bottom: var(--rule) solid var(--line); } .disclosure__trigger { display: flex; align-items: center; gap: var(--sp-4); width: 100%; padding: var(--sp-4) 0; text-align: left; font-size: var(--t-14); font-weight: var(--fw-semi); letter-spacing: var(--tr-mid); color: var(--ink); transition: color var(--dur-2) var(--ease); } .disclosure__trigger:hover { color: var(--accent-ink); } .disclosure__trigger .mono { color: var(--ink-faint); flex: none; } .disclosure__icon { margin-left: auto; width: 0.875rem; height: 0.875rem; flex: none; transition: transform var(--dur-3) var(--ease); } .disclosure__trigger[aria-expanded="true"] .disclosure__icon { transform: rotate(45deg); } .disclosure__content { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--dur-3) var(--ease); } .disclosure__content > div { overflow: hidden; } .disclosure.is-open .disclosure__content { grid-template-rows: 1fr; } .disclosure__inner { padding-bottom: var(--sp-5); } /* Educational callout */ .callout { border-left: var(--rule-thick) solid var(--accent); background: var(--accent-wash); padding: var(--sp-4) var(--sp-5); border-radius: 0 var(--r-2) var(--r-2) 0; } .callout__label { display: block; font-family: var(--font-mono); font-size: var(--t-10); letter-spacing: var(--tr-caps); text-transform: uppercase; color: var(--accent-ink); margin-bottom: var(--sp-2); } .callout p { font-size: var(--t-14); line-height: var(--lh-prose); color: var(--ink-secondary); } /* Notice list — "What to notice" bullets, numbered and hairline separated */ .notice-list { counter-reset: notice; } .notice-list li { counter-increment: notice; display: grid; grid-template-columns: 2rem minmax(0, 1fr); gap: var(--sp-3); padding: var(--sp-4) 0; border-top: var(--rule) solid var(--line); font-size: var(--t-14); line-height: var(--lh-prose); color: var(--ink-secondary); } .notice-list li::before { content: counter(notice, decimal-leading-zero); font-family: var(--font-mono); font-size: var(--t-10); letter-spacing: var(--tr-label); color: var(--accent-ink); padding-top: 0.25rem; } /* ========================================================================== 06 — FORM CONTROLS ========================================================================== */ .field { display: flex; flex-direction: column; gap: var(--sp-2); } .field__label { display: flex; align-items: baseline; gap: var(--sp-2); font-family: var(--font-mono); font-size: var(--t-10); letter-spacing: var(--tr-caps); text-transform: uppercase; color: var(--ink-muted); } .field__value { margin-left: auto; font-family: var(--font-mono); font-size: var(--t-11); color: var(--accent-ink); font-variant-numeric: tabular-nums; } .field__hint { font-size: var(--t-12); line-height: var(--lh-prose); color: var(--ink-faint); } .input, .textarea, .select { width: 100%; padding: 0.7rem 0.875rem; min-height: 2.75rem; background: var(--field); color: var(--ink); border: var(--rule) solid var(--field-line); border-radius: var(--r-2); font-size: var(--t-14); line-height: var(--lh-body); transition: border-color var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease); } .input:hover, .textarea:hover, .select:hover { border-color: var(--line-strong); } .input:focus, .textarea:focus, .select:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-wash); outline: none; } .input::placeholder, .textarea::placeholder { color: var(--ink-faint); } .textarea { resize: vertical; min-height: 6.5rem; font-family: var(--font-sans); } .textarea--mono { font-family: var(--font-mono); font-size: var(--t-13, 0.8125rem); } .select { appearance: none; padding-right: 2.25rem; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%23747474' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 0.875rem center; background-size: 0.7rem; cursor: pointer; } /* --- Range ------------------------------------------------------------- */ .range { -webkit-appearance: none; appearance: none; width: 100%; height: 2rem; /* generous hit area for touch */ background: transparent; cursor: pointer; } .range::-webkit-slider-runnable-track { height: 2px; background: var(--line-strong); border-radius: var(--r-pill); } .range::-moz-range-track { height: 2px; background: var(--line-strong); border-radius: var(--r-pill); } .range::-webkit-slider-thumb { -webkit-appearance: none; width: 1.05rem; height: 1.05rem; margin-top: -0.5rem; border-radius: 50%; background: var(--accent); border: 2px solid var(--paper); box-shadow: 0 0 0 1px var(--accent); transition: transform var(--dur-1) var(--ease); } .range::-moz-range-thumb { width: 1.05rem; height: 1.05rem; border-radius: 50%; background: var(--accent); border: 2px solid var(--paper); box-shadow: 0 0 0 1px var(--accent); } .range:hover::-webkit-slider-thumb { transform: scale(1.15); } .range:active::-webkit-slider-thumb { transform: scale(0.95); } /* --- Segmented control ------------------------------------------------- */ .segmented { display: flex; flex-wrap: wrap; gap: 0; border: var(--rule) solid var(--line); border-radius: var(--r-2); overflow: hidden; } .segmented button { flex: 1 1 auto; padding: 0.55rem 0.75rem; min-height: 2.5rem; font-size: var(--t-12); font-weight: var(--fw-medium); color: var(--ink-secondary); background: transparent; transition: all var(--dur-2) var(--ease); white-space: nowrap; } .segmented button + button { border-left: var(--rule) solid var(--line); } .segmented button:hover { background: var(--paper-sunk); color: var(--ink); } .segmented button[aria-pressed="true"] { background: var(--accent); color: var(--accent-on); } /* --- Option cards (persona, style, journey) ---------------------------- */ .option-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(8.5rem, 100%), 1fr)); gap: var(--sp-2); } .option { display: flex; flex-direction: column; gap: 0.35rem; padding: var(--sp-3) var(--sp-4); min-height: 3.5rem; text-align: left; border: var(--rule) solid var(--line); border-radius: var(--r-2); background: transparent; color: var(--ink-secondary); cursor: pointer; transition: all var(--dur-2) var(--ease); } .option:hover { border-color: var(--ink); color: var(--ink); } .option[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-wash); color: var(--ink); } .option__name { font-size: var(--t-14); font-weight: var(--fw-semi); letter-spacing: var(--tr-mid); } .option__desc { font-size: var(--t-11); color: var(--ink-faint); line-height: 1.35; } /* --- Dropzone ---------------------------------------------------------- */ .dropzone { position: relative; display: grid; place-items: center; gap: var(--sp-3); padding: var(--sp-8) var(--sp-5); min-height: 12rem; text-align: center; border: var(--rule) dashed var(--line-strong); border-radius: var(--r-2); background: var(--paper-sunk); cursor: pointer; transition: all var(--dur-2) var(--ease); } .dropzone:hover { border-color: var(--accent); background: var(--accent-wash); } .dropzone.is-dragging { border-color: var(--accent); border-style: solid; background: var(--accent-wash); transform: scale(1.005); } .dropzone__icon { width: 1.75rem; height: 1.75rem; color: var(--ink-faint); } .dropzone.is-dragging .dropzone__icon { color: var(--accent-ink); } .dropzone__title { font-size: var(--t-14); font-weight: var(--fw-semi); } .dropzone__hint { font-size: var(--t-12); color: var(--ink-faint); } .dropzone input[type="file"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; } /* Sample thumbnails so the labs are usable without an upload */ .samples { display: flex; gap: var(--sp-2); flex-wrap: wrap; } .sample { width: 3.25rem; height: 3.25rem; border: var(--rule) solid var(--line); border-radius: var(--r-2); overflow: hidden; cursor: pointer; padding: 0; transition: all var(--dur-2) var(--ease); } .sample:hover { border-color: var(--accent); transform: translateY(-2px); } .sample canvas, .sample img { width: 100%; height: 100%; object-fit: cover; } /* ========================================================================== 07 — STATES: loading, empty, error, timeout ========================================================================== */ .state { display: grid; place-items: center; gap: var(--sp-4); padding: var(--sp-8) var(--sp-5); text-align: center; min-height: 14rem; } .state__icon { width: 2rem; height: 2rem; color: var(--ink-faint); } .state__title { font-size: var(--t-16); font-weight: var(--fw-semi); } .state__text { font-size: var(--t-14); line-height: var(--lh-prose); color: var(--ink-muted); max-width: 26rem; text-wrap: pretty; } .state--error .state__icon { color: var(--red-800); } [data-theme="dark"] .state--error .state__icon { color: var(--red-500); } /* Indeterminate hairline progress */ .progress-line { position: relative; height: 2px; width: min(20rem, 100%); background: var(--line); overflow: hidden; } .progress-line::after { content: ""; position: absolute; inset-block: 0; width: 33%; background: var(--accent); animation: sweep 1.4s var(--ease-in-out) infinite; } .progress-line[data-determinate]::after { animation: none; width: var(--progress, 0%); transition: width var(--dur-3) var(--ease); } /* Skeleton */ .skeleton { background: linear-gradient(90deg, var(--paper-sunk) 25%, color-mix(in srgb, var(--paper-sunk) 60%, var(--line)) 37%, var(--paper-sunk) 63%); background-size: 400% 100%; animation: skeleton 1.4s ease infinite; border-radius: var(--r-2); } @keyframes skeleton { from { background-position: 100% 50%; } to { background-position: 0 50%; } } /* Step ticker — shows what the pipeline is doing, not just that it is busy */ .steps { display: flex; flex-direction: column; gap: var(--sp-2); text-align: left; } .step { display: flex; align-items: center; gap: var(--sp-3); font-family: var(--font-mono); font-size: var(--t-11); letter-spacing: var(--tr-label); text-transform: uppercase; color: var(--ink-faint); transition: color var(--dur-2) var(--ease); } .step::before { content: ""; width: 0.4rem; height: 0.4rem; border-radius: 50%; background: currentColor; flex: none; } .step.is-active { color: var(--accent-ink); } .step.is-active::before { animation: pulse-soft 1.2s ease-in-out infinite; } .step.is-done { color: var(--ink-secondary); } /* ========================================================================== 08 — VISUALISATION CHROME ========================================================================== */ /* Before/after comparison slider */ .compare { position: relative; overflow: hidden; border-radius: var(--r-2); background: var(--paper-sunk); touch-action: pan-y; /* horizontal drag belongs to the handle */ user-select: none; cursor: ew-resize; } .compare img, .compare canvas { display: block; width: 100%; height: auto; -webkit-user-drag: none; user-select: none; } .compare__after { position: absolute; inset: 0; overflow: hidden; width: var(--pos, 50%); } .compare__after img, .compare__after canvas { position: absolute; top: 0; left: 0; width: var(--frame-w, 100%); height: 100%; max-width: none; object-fit: cover; } .compare__handle { position: absolute; top: 0; bottom: 0; left: var(--pos, 50%); width: 2px; background: var(--paper); transform: translateX(-1px); box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25); z-index: 3; } .compare__grip { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 2.5rem; height: 2.5rem; border-radius: 50%; background: var(--paper); border: var(--rule) solid var(--line-strong); display: grid; place-items: center; color: var(--ink); cursor: ew-resize; box-shadow: var(--shadow-md); } .compare__grip svg { width: 1rem; height: 1rem; } .compare__tag { position: absolute; top: var(--sp-3); padding: 0.25rem 0.5rem; font-family: var(--font-mono); font-size: var(--t-10); letter-spacing: var(--tr-label); text-transform: uppercase; background: rgba(0, 0, 0, 0.72); color: #fff; border-radius: var(--r-1); z-index: 2; pointer-events: none; } .compare__tag--before { left: var(--sp-3); } .compare__tag--after { right: var(--sp-3); } /* Confidence bar */ .conf-row { display: grid; grid-template-columns: minmax(0, 1fr) 3.25rem; align-items: center; gap: var(--sp-3); padding: var(--sp-3) 0; border-top: var(--rule) solid var(--line); cursor: default; } .conf-row:first-child { border-top: none; } .conf-row.is-hot { background: var(--accent-wash); } .conf-row__label { font-size: var(--t-13, 0.8125rem); font-weight: var(--fw-medium); margin-bottom: var(--sp-2); display: flex; align-items: center; gap: var(--sp-2); } .conf-row__swatch { width: 0.55rem; height: 0.55rem; border-radius: 2px; flex: none; } .conf-bar { height: 4px; background: var(--paper-sunk); border-radius: var(--r-pill); overflow: hidden; } .conf-bar__fill { height: 100%; width: 0; background: var(--accent); border-radius: inherit; transition: width var(--dur-4) var(--ease); } .conf-row__value { font-family: var(--font-mono); font-size: var(--t-12); font-variant-numeric: tabular-nums; text-align: right; color: var(--ink); } /* Overlay canvas for bounding boxes */ .overlay-frame { position: relative; display: inline-block; width: 100%; line-height: 0; border-radius: var(--r-2); overflow: hidden; background: var(--paper-sunk); } .overlay-frame img { width: 100%; height: auto; } .overlay-frame canvas { position: absolute; inset: 0; width: 100%; height: 100%; } /* Legend */ .legend { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; font-family: var(--font-mono); font-size: var(--t-10); letter-spacing: var(--tr-label); text-transform: uppercase; color: var(--ink-muted); } .legend__scale { flex: 1 1 8rem; height: 0.5rem; border-radius: var(--r-pill); border: var(--rule) solid var(--line); } /* Metric readouts under a result */ .metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(6rem, 1fr)); gap: var(--sp-4); padding-top: var(--sp-4); border-top: var(--rule) solid var(--line); } .metric { display: flex; flex-direction: column; gap: 0.2rem; } .metric__value { font-family: var(--font-mono); font-size: var(--t-21); font-variant-numeric: tabular-nums; letter-spacing: var(--tr-tight); color: var(--ink); } .metric__label { font-family: var(--font-mono); font-size: var(--t-9); letter-spacing: var(--tr-caps); text-transform: uppercase; color: var(--ink-faint); } /* ========================================================================== 09 — EXPLAIN MODE Annotations that only appear when the user turns explanations on. ========================================================================== */ .explain { display: none; } [data-explain="on"] .explain { display: block; } [data-explain="on"] .explain--inline { display: inline; } [data-explain="on"] .explain--flex { display: flex; } .explain-note { position: relative; margin-top: var(--sp-3); padding: var(--sp-3) var(--sp-4); border: var(--rule) dashed var(--accent-line); border-radius: var(--r-2); background: var(--accent-wash); font-size: var(--t-12); line-height: var(--lh-prose); color: var(--ink-secondary); animation: rise var(--dur-3) var(--ease); } .explain-note__label { display: block; font-family: var(--font-mono); font-size: var(--t-9); letter-spacing: var(--tr-caps); text-transform: uppercase; color: var(--accent-ink); margin-bottom: var(--sp-1); } /* Explain toggle in the nav */ .explain-toggle { display: inline-flex; align-items: center; gap: var(--sp-2); padding: 0.4rem 0.7rem; min-height: 2.25rem; border: var(--rule) solid var(--line); border-radius: var(--r-pill); font-family: var(--font-mono); font-size: var(--t-10); letter-spacing: var(--tr-label); text-transform: uppercase; color: var(--ink-muted); transition: all var(--dur-2) var(--ease); } .explain-toggle:hover { border-color: var(--ink); color: var(--ink); } .explain-toggle[aria-pressed="true"] { background: var(--yellow-400); border-color: var(--yellow-500); color: var(--gray-900); } .explain-toggle__dot { width: 0.4rem; height: 0.4rem; border-radius: 50%; background: currentColor; opacity: 0.4; } .explain-toggle[aria-pressed="true"] .explain-toggle__dot { opacity: 1; } /* Inline term with a definition on hover/focus */ .term { border-bottom: var(--rule) dashed var(--ink-faint); cursor: help; position: relative; } .term:hover, .term:focus-visible { border-bottom-color: var(--accent); } .tip { position: absolute; bottom: calc(100% + 0.5rem); left: 50%; transform: translateX(-50%) translateY(4px); width: max-content; max-width: 17rem; padding: var(--sp-3); background: var(--paper-inverse); color: var(--paper); font-family: var(--font-sans); font-size: var(--t-11); line-height: 1.45; letter-spacing: 0; text-transform: none; border-radius: var(--r-2); box-shadow: var(--shadow-lg); opacity: 0; pointer-events: none; transition: opacity var(--dur-2) var(--ease), transform var(--dur-2) var(--ease); z-index: var(--z-modal); } .term:hover .tip, .term:focus-visible .tip { opacity: 1; transform: translateX(-50%) translateY(0); } /* ========================================================================== 10 — DIALOG, TOAST ========================================================================== */ .dialog { position: fixed; inset: 0; z-index: var(--z-modal); display: grid; place-items: center; padding: var(--sp-5); visibility: hidden; pointer-events: none; } .dialog.is-open { visibility: visible; pointer-events: auto; } .dialog__scrim { position: absolute; inset: 0; background: var(--scrim); opacity: 0; transition: opacity var(--dur-3) var(--ease); } .dialog.is-open .dialog__scrim { opacity: 1; } .dialog__panel { position: relative; width: min(46rem, 100%); max-height: min(85vh, 48rem); overflow-y: auto; background: var(--paper); border: var(--rule) solid var(--line); border-radius: var(--r-3); box-shadow: var(--shadow-lg); opacity: 0; transform: translateY(1rem) scale(0.99); transition: opacity var(--dur-3) var(--ease), transform var(--dur-3) var(--ease); } .dialog.is-open .dialog__panel { opacity: 1; transform: none; } .dialog__head { position: sticky; top: 0; display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); padding: var(--sp-5) var(--sp-6); border-bottom: var(--rule) solid var(--line); background: var(--paper); z-index: 2; } .dialog__body { padding: var(--sp-6); } .toast-stack { position: fixed; bottom: var(--sp-5); left: 50%; transform: translateX(-50%); z-index: var(--z-toast); display: flex; flex-direction: column; gap: var(--sp-2); width: min(28rem, calc(100vw - 2rem)); pointer-events: none; } .toast { display: flex; align-items: flex-start; gap: var(--sp-3); padding: var(--sp-4); background: var(--paper-inverse); color: var(--paper); border-radius: var(--r-2); box-shadow: var(--shadow-lg); font-size: var(--t-13, 0.8125rem); line-height: var(--lh-prose); animation: rise var(--dur-3) var(--ease); pointer-events: auto; } .toast__bar { width: 2px; align-self: stretch; background: var(--accent); flex: none; } .toast[data-tone="error"] .toast__bar { background: var(--red-700); } .toast[data-tone="success"] .toast__bar { background: var(--green-600); } /* ========================================================================== 11 — FOOTER ========================================================================== */ .site-footer { border-top: var(--rule) solid var(--line-ink); padding-block: var(--sp-9) var(--sp-7); margin-top: var(--sp-6); } .site-footer__grid { display: grid; grid-template-columns: minmax(0, 1.6fr) repeat(3, minmax(0, 1fr)); gap: var(--sp-7) var(--sp-6); } .site-footer__col-title { margin-bottom: var(--sp-4); padding-bottom: var(--sp-3); border-bottom: var(--rule) solid var(--line); display: block; } .site-footer__link { display: block; padding: var(--sp-2) 0; font-size: var(--t-14); color: var(--ink-secondary); transition: color var(--dur-2) var(--ease), padding-left var(--dur-2) var(--ease); } .site-footer__link:hover { color: var(--accent-ink); padding-left: var(--sp-2); } .site-footer__base { margin-top: var(--sp-8); padding-top: var(--sp-5); border-top: var(--rule) solid var(--line); display: flex; flex-wrap: wrap; gap: var(--sp-4); align-items: center; justify-content: space-between; } @media (max-width: 900px) { .site-footer__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } } @media (max-width: 560px) { .site-footer__grid { grid-template-columns: 1fr; } } /* ========================================================================== 12 — UTILITIES ========================================================================== */ .hidden { display: none !important; } .invisible { visibility: hidden; } .full { width: 100%; } .center { text-align: center; } .nowrap { white-space: nowrap; } .truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .scroll-x { overflow-x: auto; scrollbar-width: thin; -webkit-overflow-scrolling: touch; } .scroll-x::-webkit-scrollbar { height: 4px; } .scroll-x::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 2px; }