← snapshot
58547 bytes
/*
* lowell — the mill floor.
*
* Chassis borrowed from the s10 console (rail + topbar + panel/chip grammar,
* 13px density, uppercase tracked micro-labels, two motion durations and one
* easing curve). Palette deliberately NOT borrowed: see
* docs/adversarial-review/reviews/2026-08-23-lowell-visual-redesign.md.
*
* The one rule that outranks aesthetics: amber/gold carries meaning here and
* nothing else may wear it. Gold = a human's hands. Burnt amber + hatch = a
* verdict that verified nothing. If you need "just a warm accent", use indigo.
*/
/* ==========================================================================
Document — the app frame is a full-height grid, so the chain of
percentage heights has to start at :root or the whole floor collapses.
========================================================================== */
html,
body {
height: 100%;
margin: 0;
background: #08111a;
}
/* ==========================================================================
Tokens
========================================================================== */
.jac-site {
/* ground — mill iron and night glass */
--jac-paper: #08111a;
--jac-paper-warm: #050b12;
--jac-ink: #eae4d6;
--jac-muted: #b0a692;
--jac-line: rgba(234, 228, 214, 0.13);
--jac-line-strong: rgba(234, 228, 214, 0.22);
--jac-surface-1: rgba(234, 228, 214, 0.035);
--jac-surface-2: rgba(234, 228, 214, 0.06);
--jac-surface-3: rgba(234, 228, 214, 0.09);
--jac-rail: rgba(4, 9, 16, 0.55);
--jac-code-bg: rgba(2, 6, 11, 0.6);
/* indigo — the dye the loom actually weaves with. Links, primary, agent. */
--jac-accent: #8aa6e0;
--jac-accent-ink: #a3bbec;
--jac-accent-wash: rgba(138, 166, 224, 0.12);
/* sage — labels and eyebrows only. Never a status. */
--jac-sage: #9dbb8a;
/* RESERVED. Gold is a human's hands; burnt amber is an ungoverned verdict. */
--jac-gold: #e4c06d;
--jac-gold-wash: rgba(228, 192, 109, 0.13);
--jac-amber: #d9903f;
--jac-amber-wash: rgba(217, 144, 63, 0.11);
/* verdicts */
--jac-blocked: #e4827a;
--jac-blocked-wash: rgba(228, 130, 122, 0.1);
--jac-ok: #86c797;
--jac-ok-wash: rgba(134, 199, 151, 0.1);
/* provenance triad — the signature. */
--jac-human: var(--jac-gold);
--jac-human-wash: var(--jac-gold-wash);
--jac-agent: var(--jac-accent);
--jac-agent-wash: var(--jac-accent-wash);
--jac-serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
--jac-display: "SF Pro Display", "Avenir Next", "Segoe UI", Arial, sans-serif;
--jac-sans: "SF Pro Text", "Segoe UI", Arial, sans-serif;
--jac-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Monaco, Consolas, monospace;
--jac-r-pill: 999px;
--jac-r-lg: 14px;
--jac-r-md: 11px;
--jac-r-sm: 9px;
--jac-r-xs: 6px;
--jac-shadow-panel: 0 1px 3px rgba(0, 0, 0, 0.4), 0 8px 24px rgba(0, 0, 0, 0.22);
--jac-shadow-pop: 0 4px 12px rgba(0, 0, 0, 0.45), 0 24px 60px rgba(0, 0, 0, 0.4);
--jac-inset-hi: inset 0 1px 0 rgba(255, 255, 255, 0.05);
--jac-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
--dur-fast: 120ms;
--dur-base: 150ms;
--jac-tracking-eyebrow: 0.16em;
--jac-tracking-chip: 0.13em;
position: relative;
height: 100%;
font-family: var(--jac-sans);
font-size: 13px;
line-height: 1.5;
letter-spacing: 0.004em;
color: var(--jac-ink);
background: linear-gradient(180deg, var(--jac-paper) 0%, var(--jac-paper-warm) 100%);
background-attachment: fixed;
-webkit-font-smoothing: antialiased;
}
/* Light is kept as the non-default branch; dark is the design. */
.jac-site[data-jac-theme="light"] {
--jac-paper: #f4f0e6;
--jac-paper-warm: #ece6d8;
--jac-ink: #241f1a;
--jac-muted: #635b50;
--jac-line: rgba(36, 31, 26, 0.16);
--jac-line-strong: rgba(36, 31, 26, 0.26);
--jac-surface-1: rgba(255, 255, 255, 0.5);
--jac-surface-2: rgba(255, 255, 255, 0.75);
--jac-surface-3: rgba(36, 31, 26, 0.07);
--jac-rail: rgba(255, 255, 255, 0.6);
--jac-code-bg: rgba(36, 31, 26, 0.05);
--jac-accent: #56679a;
--jac-accent-ink: #46557f;
--jac-accent-wash: rgba(86, 103, 154, 0.1);
--jac-sage: #5f7a4e;
--jac-gold: #8a6d24;
--jac-gold-wash: rgba(184, 148, 58, 0.14);
--jac-amber: #a8641b;
--jac-amber-wash: rgba(168, 100, 27, 0.1);
--jac-blocked: #a83d35;
--jac-blocked-wash: rgba(168, 61, 53, 0.09);
--jac-ok: #3d7a4e;
--jac-ok-wash: rgba(61, 122, 78, 0.1);
}
.jac-site *,
.jac-site *::before,
.jac-site *::after {
box-sizing: border-box;
}
.jac-site ::selection {
background: rgba(138, 166, 224, 0.28);
}
.jac-site a {
color: var(--jac-accent-ink);
text-underline-offset: 2px;
text-decoration-thickness: 1px;
}
.jac-site a:hover {
color: var(--jac-ink);
}
.jac-site :focus-visible {
outline: 2px solid var(--jac-accent);
outline-offset: 2px;
border-radius: 4px;
}
.jac-site h1,
.jac-site h2,
.jac-site h3 {
margin: 0;
color: var(--jac-ink);
}
.jac-site ::-webkit-scrollbar {
width: 10px;
height: 10px;
}
.jac-site ::-webkit-scrollbar-thumb {
background: rgba(234, 228, 214, 0.14);
border-radius: 999px;
border: 2px solid transparent;
background-clip: padding-box;
}
.jac-site ::-webkit-scrollbar-thumb:hover {
background: rgba(234, 228, 214, 0.24);
background-clip: padding-box;
}
.jac-site ::-webkit-scrollbar-track {
background: transparent;
}
/* ==========================================================================
Mill-floor texture — warp/weft over accent glows, fixed behind everything
========================================================================== */
.jac-weave {
position: fixed;
inset: 0;
pointer-events: none;
z-index: 0;
background-image:
radial-gradient(circle at 16% 6%, rgba(138, 166, 224, 0.07) 0, rgba(138, 166, 224, 0) 30%),
radial-gradient(circle at 88% 12%, rgba(228, 192, 109, 0.045) 0, rgba(228, 192, 109, 0) 26%),
radial-gradient(circle at 8% 94%, rgba(157, 187, 138, 0.04) 0, rgba(157, 187, 138, 0) 30%),
repeating-linear-gradient(
0deg,
rgba(234, 228, 214, 0.022) 0,
rgba(234, 228, 214, 0.022) 1px,
transparent 1px,
transparent 4px
),
repeating-linear-gradient(
90deg,
rgba(234, 228, 214, 0.012) 0,
rgba(234, 228, 214, 0.012) 1px,
transparent 1px,
transparent 6px
);
}
.jac-site[data-jac-theme="light"] .jac-weave {
background-image:
radial-gradient(circle at 16% 6%, rgba(86, 103, 154, 0.06) 0, rgba(86, 103, 154, 0) 30%),
repeating-linear-gradient(
0deg,
rgba(36, 31, 26, 0.03) 0,
rgba(36, 31, 26, 0.03) 1px,
transparent 1px,
transparent 4px
),
repeating-linear-gradient(
90deg,
rgba(36, 31, 26, 0.016) 0,
rgba(36, 31, 26, 0.016) 1px,
transparent 1px,
transparent 6px
);
}
/* ==========================================================================
App frame — rail + topbar, only the scroll region scrolls
========================================================================== */
.jac-app {
position: relative;
z-index: 1;
display: grid;
grid-template-columns: 232px 1fr;
height: 100%;
overflow: hidden;
}
.jac-col {
display: flex;
flex-direction: column;
min-width: 0;
min-height: 0;
}
.jac-scroll {
flex: 1;
min-height: 0;
overflow-y: auto;
overflow-x: hidden;
}
/* ---- rail ---- */
.jac-rail {
display: flex;
flex-direction: column;
min-height: 0;
border-right: 1px solid var(--jac-line);
background: var(--jac-rail);
backdrop-filter: blur(6px);
overflow-y: auto;
}
.jac-rail-brand {
display: flex;
align-items: center;
gap: 10px;
padding: 16px 16px 14px;
border-bottom: 1px solid var(--jac-line);
text-decoration: none;
color: var(--jac-ink);
}
/* The mark is a punched card: four holes in a 2×2 register. */
.jac-rail-mark {
width: 26px;
height: 26px;
flex: none;
border-radius: 7px;
border: 1px solid var(--jac-line-strong);
background:
radial-gradient(circle at 30% 32%, var(--jac-gold) 0 1.6px, transparent 1.7px),
radial-gradient(circle at 70% 32%, transparent 0 1.7px, transparent 1.8px),
radial-gradient(circle at 30% 68%, transparent 0 1.7px, transparent 1.8px),
radial-gradient(circle at 70% 68%, var(--jac-accent) 0 1.6px, transparent 1.7px),
linear-gradient(150deg, rgba(138, 166, 224, 0.2), rgba(228, 192, 109, 0.12));
}
.jac-rail-name {
font-family: var(--jac-display);
font-weight: 650;
font-size: 14px;
letter-spacing: -0.01em;
line-height: 1.1;
}
.jac-rail-sub {
font-size: 10px;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--jac-muted);
}
.jac-rail-section {
padding: 14px 10px 4px;
}
.jac-rail-section-label {
padding: 0 8px 6px;
font-size: 9.5px;
font-weight: 600;
letter-spacing: var(--jac-tracking-eyebrow);
text-transform: uppercase;
color: var(--jac-muted);
opacity: 0.7;
}
.jac-nav-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 10px;
margin: 1px 0;
border: 1px solid transparent;
border-radius: var(--jac-r-sm);
color: var(--jac-muted);
font-size: 13px;
text-decoration: none;
user-select: none;
transition:
background var(--dur-fast) var(--jac-ease),
color var(--dur-fast) var(--jac-ease);
}
.jac-nav-item:hover {
background: var(--jac-surface-2);
color: var(--jac-ink);
}
.jac-nav-item[data-active="true"] {
background: var(--jac-surface-2);
color: var(--jac-ink);
border-color: var(--jac-line);
}
.jac-nav-item[data-active="true"] .jac-nav-ico {
color: var(--jac-accent);
}
.jac-nav-ico {
width: 17px;
height: 17px;
flex: none;
color: currentColor;
}
.jac-nav-badge {
margin-left: auto;
font-family: var(--jac-mono);
font-size: 10px;
color: var(--jac-muted);
background: var(--jac-surface-2);
border: 1px solid var(--jac-line);
border-radius: 999px;
padding: 0 6px;
}
/* The unsettled count is the one place a warning tint belongs in the rail. */
.jac-nav-badge[data-waiting="true"] {
color: var(--jac-blocked);
border-color: rgba(228, 130, 122, 0.35);
background: var(--jac-blocked-wash);
}
.jac-rail-foot {
margin-top: auto;
padding: 12px 14px;
border-top: 1px solid var(--jac-line);
font-size: 11px;
line-height: 1.5;
color: var(--jac-muted);
}
/* ---- topbar ---- */
.jac-topbar {
display: flex;
align-items: center;
gap: 10px;
flex: none;
min-height: 53px;
padding: 10px 18px;
border-bottom: 1px solid var(--jac-line);
background: var(--jac-rail);
backdrop-filter: blur(6px);
}
.jac-crumb {
font-family: var(--jac-display);
font-weight: 600;
font-size: 14px;
letter-spacing: -0.01em;
}
.jac-crumb-dim {
color: var(--jac-muted);
font-weight: 500;
}
.jac-spacer {
flex: 1;
}
/* ==========================================================================
Page scaffolding
========================================================================== */
.jac-page {
padding: 20px 22px 64px;
max-width: 1320px;
margin: 0 auto;
animation: jac-enter var(--dur-base) var(--jac-ease);
}
.jac-page--wide {
max-width: 1500px;
}
.jac-eyebrow {
margin: 0;
font-size: 10.5px;
font-weight: 600;
letter-spacing: var(--jac-tracking-eyebrow);
text-transform: uppercase;
color: var(--jac-sage);
}
.jac-h1 {
margin: 6px 0 0;
font-family: var(--jac-display);
font-weight: 650;
font-size: 24px;
letter-spacing: -0.02em;
}
.jac-h2 {
margin: 6px 0 0;
font-family: var(--jac-display);
font-weight: 650;
font-size: 19px;
letter-spacing: -0.015em;
}
.jac-h3 {
font-family: var(--jac-display);
font-weight: 600;
font-size: 15px;
letter-spacing: -0.01em;
}
.jac-lede {
margin: 8px 0 0;
font-size: 13px;
line-height: 1.6;
color: var(--jac-muted);
max-width: 64ch;
}
.jac-prose {
font-size: 13px;
line-height: 1.6;
}
.jac-small {
font-size: 11px;
color: var(--jac-muted);
}
.jac-mono {
font-family: var(--jac-mono);
font-variant-ligatures: none;
}
.jac-row-between {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 16px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.jac-grid {
display: grid;
gap: 14px;
margin-bottom: 14px;
}
.jac-grid--4 {
grid-template-columns: repeat(4, 1fr);
}
.jac-grid--3 {
grid-template-columns: repeat(3, 1fr);
}
.jac-grid--2 {
grid-template-columns: 1fr 1fr;
}
.jac-grid--cards {
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
.jac-cols {
display: flex;
flex-wrap: wrap;
gap: 10px 20px;
align-items: center;
}
.jac-divider {
height: 1px;
background: var(--jac-line);
margin: 18px 0;
border: none;
}
/* ==========================================================================
Panels, stat tiles
========================================================================== */
.jac-panel {
background: var(--jac-surface-1);
border: 1px solid var(--jac-line);
border-radius: var(--jac-r-lg);
box-shadow: var(--jac-inset-hi);
}
.jac-panel-pad {
padding: 16px 18px;
}
.jac-panel-head {
display: flex;
align-items: center;
gap: 10px;
padding: 11px 16px;
border-bottom: 1px solid var(--jac-line);
}
.jac-panel-title {
font-size: 10.5px;
font-weight: 600;
letter-spacing: var(--jac-tracking-chip);
text-transform: uppercase;
color: var(--jac-sage);
}
.jac-panel-foot {
padding: 9px 16px;
border-top: 1px solid var(--jac-line);
font-size: 11px;
color: var(--jac-muted);
}
.jac-stat {
padding: 14px 15px;
display: flex;
flex-direction: column;
gap: 3px;
min-height: 92px;
}
.jac-stat-label {
font-size: 10.5px;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--jac-muted);
}
.jac-stat-value {
font-family: var(--jac-display);
font-weight: 650;
font-size: 28px;
letter-spacing: -0.02em;
line-height: 1.05;
}
.jac-stat-sub {
font-size: 11px;
color: var(--jac-muted);
margin-top: auto;
}
/* ==========================================================================
Chips, badges, pills — one rule, driven by --chip-c
========================================================================== */
.jac-badge {
display: inline-flex;
align-items: center;
gap: 6px;
height: 21px;
padding: 0 9px 0 8px;
border-radius: var(--jac-r-pill);
font-size: 11.5px;
font-weight: 500;
line-height: 1;
white-space: nowrap;
border: 1px solid color-mix(in oklch, var(--chip-c, var(--jac-muted)) 36%, transparent);
background: color-mix(in oklch, var(--chip-c, var(--jac-muted)) 13%, transparent);
color: color-mix(in oklch, var(--chip-c, var(--jac-ink)) 78%, var(--jac-ink));
}
.jac-badge-dot {
width: 6px;
height: 6px;
border-radius: 999px;
background: var(--chip-c, var(--jac-muted));
flex: none;
}
.jac-badge--human {
--chip-c: var(--jac-human);
}
.jac-badge--agent {
--chip-c: var(--jac-agent);
}
/* Mixed is both hands at once — woven, never a third colour. */
.jac-badge--mixed {
--chip-c: var(--jac-muted);
background: linear-gradient(
100deg,
var(--jac-human-wash) 0 50%,
var(--jac-agent-wash) 50% 100%
);
color: var(--jac-ink);
}
.jac-badge--mixed .jac-badge-dot {
background: linear-gradient(135deg, var(--jac-human) 0 50%, var(--jac-agent) 50% 100%);
}
.jac-pill {
display: inline-block;
padding: 2px 9px;
border: 1px dashed var(--jac-accent);
border-radius: 999px;
font-family: var(--jac-mono);
font-size: 11.5px;
color: var(--jac-accent-ink);
white-space: nowrap;
}
.jac-tag {
display: inline-block;
padding: 2px 9px;
border-radius: 999px;
border: 1px solid var(--jac-line);
background: var(--jac-surface-2);
font-size: 10.5px;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--jac-muted);
white-space: nowrap;
}
.jac-tag--warn {
background: var(--jac-blocked-wash);
border-color: rgba(228, 130, 122, 0.34);
color: var(--jac-blocked);
}
.jac-chip {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 2px 6px;
border: 1px solid var(--jac-line);
border-radius: var(--jac-r-xs);
background: var(--jac-code-bg);
font-family: var(--jac-mono);
font-size: 11px;
color: var(--jac-accent-ink);
cursor: pointer;
text-decoration: none;
transition: border-color var(--dur-fast) var(--jac-ease);
}
.jac-chip:hover {
border-color: var(--jac-line-strong);
color: var(--jac-ink);
}
.jac-count {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 18px;
height: 18px;
padding: 0 5px;
border-radius: 999px;
background: var(--jac-blocked-wash);
color: var(--jac-blocked);
font-family: var(--jac-mono);
font-size: 11px;
}
/* ==========================================================================
Controls
========================================================================== */
.jac-btn {
display: inline-flex;
align-items: center;
gap: 7px;
height: 32px;
padding: 0 13px;
background: var(--jac-surface-2);
border: 1px solid var(--jac-line);
border-radius: var(--jac-r-sm);
color: var(--jac-ink);
font: inherit;
font-size: 12px;
letter-spacing: 0.02em;
cursor: pointer;
white-space: nowrap;
text-decoration: none;
transition:
background var(--dur-fast) var(--jac-ease),
border-color var(--dur-fast) var(--jac-ease),
transform var(--dur-fast) var(--jac-ease);
}
.jac-btn:hover {
background: var(--jac-surface-3);
border-color: var(--jac-line-strong);
color: var(--jac-ink);
transform: translateY(-1px);
}
.jac-btn:disabled {
opacity: 0.45;
cursor: not-allowed;
transform: none;
}
.jac-btn--primary {
background: color-mix(in srgb, var(--jac-accent) 16%, var(--jac-surface-2));
border-color: color-mix(in srgb, var(--jac-accent) 40%, var(--jac-line));
color: var(--jac-accent-ink);
font-weight: 600;
}
.jac-btn--primary:hover {
background: color-mix(in srgb, var(--jac-accent) 26%, var(--jac-surface-2));
}
.jac-btn--lg {
height: 38px;
padding: 0 18px;
font-size: 12.5px;
}
.jac-cta-row {
margin-top: 20px;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
}
.jac-theme-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
border-radius: var(--jac-r-sm);
border: 1px solid var(--jac-line);
background: var(--jac-surface-2);
color: var(--jac-muted);
cursor: pointer;
transition: color var(--dur-fast) var(--jac-ease);
}
.jac-theme-btn:hover {
color: var(--jac-ink);
}
/* segmented control */
.jac-seg {
display: inline-flex;
background: var(--jac-surface-1);
border: 1px solid var(--jac-line);
border-radius: var(--jac-r-sm);
padding: 2px;
gap: 2px;
}
.jac-seg a,
.jac-seg button {
font: inherit;
font-size: 11.5px;
background: transparent;
border: none;
color: var(--jac-muted);
padding: 3px 9px;
height: 24px;
border-radius: 6px;
cursor: pointer;
white-space: nowrap;
text-decoration: none;
display: inline-flex;
align-items: center;
transition:
background var(--dur-fast) var(--jac-ease),
color var(--dur-fast) var(--jac-ease);
}
.jac-seg a:hover,
.jac-seg button:hover {
color: var(--jac-ink);
}
.jac-seg a[data-on="true"],
.jac-seg button[data-on="true"] {
background: var(--jac-surface-3);
color: var(--jac-ink);
box-shadow: var(--jac-inset-hi);
}
/* ==========================================================================
Forms
========================================================================== */
.jac-field {
margin-top: 16px;
}
.jac-label {
display: block;
font-size: 9.5px;
font-weight: 600;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--jac-muted);
margin-bottom: 7px;
}
.jac-input,
.jac-textarea,
.jac-select {
width: 100%;
padding: 9px 12px;
background: var(--jac-surface-2);
border: 1px solid var(--jac-line);
border-radius: var(--jac-r-sm);
color: var(--jac-ink);
font-family: var(--jac-mono);
font-size: 13px;
line-height: 1.5;
}
.jac-input:focus,
.jac-textarea:focus,
.jac-select:focus {
outline: none;
border-color: color-mix(in srgb, var(--jac-accent) 55%, var(--jac-line));
box-shadow: 0 0 0 3px var(--jac-accent-wash);
}
.jac-input[aria-invalid="true"],
.jac-textarea[aria-invalid="true"] {
border-color: var(--jac-blocked);
}
.jac-textarea {
min-height: 110px;
resize: vertical;
}
.jac-hint {
margin-top: 6px;
font-size: 11.5px;
color: var(--jac-muted);
}
.jac-hint--error {
color: var(--jac-blocked);
}
.jac-counter {
font-family: var(--jac-mono);
font-size: 11px;
color: var(--jac-muted);
}
.jac-counter--over {
color: var(--jac-blocked);
}
.jac-check {
display: inline-flex;
align-items: center;
gap: 9px;
cursor: pointer;
font-size: 13px;
}
/* Provenance selector: three cards, one choice. */
.jac-prov-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(165px, 1fr));
gap: 10px;
}
.jac-prov-card {
border: 1px solid var(--jac-line);
border-radius: var(--jac-r-md);
padding: 13px;
background: var(--jac-surface-1);
cursor: pointer;
font-size: 12px;
line-height: 1.5;
color: var(--jac-muted);
text-align: left;
transition:
border-color var(--dur-fast) var(--jac-ease),
background var(--dur-fast) var(--jac-ease);
}
.jac-prov-card:hover {
border-color: var(--jac-line-strong);
}
.jac-prov-card[data-selected="true"] {
border-color: color-mix(in srgb, var(--jac-accent) 50%, var(--jac-line));
background: var(--jac-accent-wash);
color: var(--jac-ink);
}
/* ==========================================================================
Rows / tables
========================================================================== */
.jac-rows {
margin: 0;
padding: 0;
list-style: none;
border: 1px solid var(--jac-line);
border-radius: var(--jac-r-md);
background: var(--jac-surface-1);
overflow: hidden;
}
.jac-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 14px;
padding: 9px 14px;
border-top: 1px solid var(--jac-line);
font-size: 12.5px;
transition: background var(--dur-fast) var(--jac-ease);
}
.jac-row:first-child {
border-top: none;
}
.jac-row:hover {
background: var(--jac-surface-2);
}
.jac-row-message {
flex: 1 1 220px;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* ==========================================================================
The log: provenance rail beside the rows
========================================================================== */
.jac-log {
display: grid;
grid-template-columns: auto 1fr;
gap: 0 6px;
align-items: stretch;
}
.jac-graph-svg {
display: block;
height: 100%;
}
/* ==========================================================================
Decisions — the selvage carries the proposer's hands
========================================================================== */
.jac-decision-card {
border: 1px solid var(--jac-line);
border-left: 3px solid var(--jac-agent);
border-radius: 4px var(--jac-r-md) var(--jac-r-md) 4px;
background: var(--jac-surface-1);
padding: 14px 16px;
transition: background var(--dur-fast) var(--jac-ease);
}
.jac-decision-card:hover {
background: var(--jac-surface-2);
}
.jac-decision-card--human {
border-left-color: var(--jac-human);
}
.jac-decision-card + .jac-decision-card {
margin-top: 10px;
}
.jac-decision-title {
font-family: var(--jac-display);
font-size: 16px;
font-weight: 600;
letter-spacing: -0.01em;
line-height: 1.3;
}
.jac-decision-title a {
color: var(--jac-ink);
text-decoration: none;
}
.jac-decision-title a:hover {
color: var(--jac-accent-ink);
}
.jac-meta-row {
margin-top: 8px;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 7px 11px;
}
/* ---- the thesis: two panels, never conflated ---- */
.jac-panel-thesis {
border: 1px solid var(--jac-line);
border-radius: 4px var(--jac-r-lg) var(--jac-r-lg) 4px;
padding: 18px 20px;
}
.jac-panel--machine {
border-left: 3px solid var(--jac-agent);
background: var(--jac-surface-1);
}
.jac-panel--human {
border-left: 3px solid var(--jac-human);
background: var(--jac-human-wash);
margin-top: 18px;
}
.jac-panel-label {
font-size: 9.5px;
font-weight: 600;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--jac-muted);
}
.jac-panel--machine .jac-panel-label {
color: var(--jac-agent);
}
.jac-panel--human .jac-panel-label {
color: var(--jac-human);
}
.jac-rationale {
margin: 12px 0 0;
font-family: var(--jac-mono);
font-size: 12.5px;
line-height: 1.75;
white-space: pre-wrap;
color: var(--jac-muted);
}
.jac-statement {
margin: 12px 0 0;
font-family: var(--jac-mono);
font-size: 14px;
line-height: 1.8;
white-space: pre-wrap;
color: var(--jac-ink);
}
/* An unsigned signature line — nobody has put their name to this. */
.jac-unsigned {
margin-top: 14px;
padding: 20px 4px 8px;
border-bottom: 1px dashed var(--jac-line-strong);
color: var(--jac-muted);
font-style: italic;
font-size: 13px;
}
/* ==========================================================================
Verdicts — the honesty rules, in pixels
========================================================================== */
.jac-verdict {
border-radius: var(--jac-r-lg);
padding: 16px 18px;
border: 1px solid var(--jac-line);
background: var(--jac-surface-1);
}
.jac-verdict-title {
font-size: 11px;
font-weight: 700;
letter-spacing: var(--jac-tracking-chip);
text-transform: uppercase;
display: flex;
align-items: center;
gap: 8px;
}
.jac-verdict p {
margin: 10px 0 0;
font-size: 12.5px;
line-height: 1.6;
color: var(--jac-muted);
}
.jac-verdict ul {
margin: 10px 0 0;
padding-left: 0;
list-style: none;
}
.jac-verdict li {
margin-top: 6px;
}
.jac-verdict--blocked {
background: var(--jac-blocked-wash);
border-color: rgba(228, 130, 122, 0.35);
}
.jac-verdict--blocked .jac-verdict-title {
color: var(--jac-blocked);
}
.jac-verdict--admitted {
background: var(--jac-ok-wash);
border-color: rgba(134, 199, 151, 0.35);
}
.jac-verdict--admitted .jac-verdict-title {
color: var(--jac-ok);
}
/*
* Admitted with nothing checked. Burnt amber, and hatched: the hatch is the
* point — this is not solid ground. Never green, never plain.
*/
.jac-verdict--ungoverned {
border-color: rgba(217, 144, 63, 0.45);
background:
repeating-linear-gradient(
135deg,
rgba(217, 144, 63, 0.09) 0 7px,
transparent 7px 14px
),
var(--jac-amber-wash);
}
.jac-verdict--ungoverned .jac-verdict-title {
color: var(--jac-amber);
}
/* ==========================================================================
Empty / error
========================================================================== */
.jac-empty {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 40px 22px;
text-align: center;
color: var(--jac-muted);
font-size: 12.5px;
line-height: 1.6;
border: 1px dashed var(--jac-line-strong);
border-radius: var(--jac-r-md);
background: repeating-linear-gradient(
135deg,
rgba(234, 228, 214, 0.014) 0 8px,
transparent 8px 16px
);
}
.jac-error-box {
margin-top: 14px;
padding: 11px 14px;
border: 1px solid rgba(228, 130, 122, 0.4);
border-radius: var(--jac-r-sm);
background: var(--jac-blocked-wash);
color: var(--jac-blocked);
font-family: var(--jac-mono);
font-size: 12px;
white-space: pre-wrap;
}
/* ==========================================================================
Tree / blob / console surfaces
========================================================================== */
.jac-crumbs {
display: flex;
flex-wrap: wrap;
gap: 4px;
font-family: var(--jac-mono);
font-size: 12.5px;
margin-top: 14px;
}
.jac-blob {
margin-top: 14px;
border: 1px solid var(--jac-line);
border-radius: var(--jac-r-md);
background: var(--jac-code-bg);
padding: 14px 16px;
font-family: var(--jac-mono);
font-size: 12px;
line-height: 1.7;
overflow-x: auto;
white-space: pre;
color: var(--jac-ink);
}
/* ==========================================================================
The wizard
========================================================================== */
.jac-wizard {
display: grid;
grid-template-columns: minmax(0, 3fr) minmax(230px, 2fr);
gap: 32px;
align-items: start;
}
/* Card-punch progress: filled hole = punched, ring = under the needle. */
.jac-steps {
display: flex;
align-items: center;
gap: 12px;
margin-top: 22px;
}
.jac-step-hole {
width: 12px;
height: 12px;
border-radius: 50%;
border: 1.5px solid var(--jac-line-strong);
background: transparent;
padding: 0;
cursor: pointer;
flex: none;
transition:
background var(--dur-fast) var(--jac-ease),
box-shadow var(--dur-fast) var(--jac-ease);
}
.jac-step-hole[data-state="done"] {
background: var(--jac-accent);
border-color: var(--jac-accent);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.5);
animation: jac-punch var(--dur-base) var(--jac-ease);
}
.jac-step-hole[data-state="current"] {
border-color: var(--jac-accent);
box-shadow: 0 0 0 3px var(--jac-accent-wash);
}
.jac-step-hole:disabled {
cursor: default;
}
.jac-steps-rule {
flex: 1;
height: 1px;
background: var(--jac-line);
}
.jac-aside {
border-left: 2px solid var(--jac-accent);
padding: 2px 0 2px 16px;
font-size: 12.5px;
line-height: 1.65;
color: var(--jac-muted);
}
.jac-aside strong {
color: var(--jac-ink);
font-weight: 600;
}
.jac-honest {
display: block;
margin-top: 10px;
font-style: italic;
font-size: 12px;
}
.jac-exhibit {
margin-top: 14px;
padding: 11px 14px;
border: 1px dashed var(--jac-line-strong);
border-radius: var(--jac-r-sm);
background: var(--jac-code-bg);
font-family: var(--jac-mono);
font-size: 11.5px;
line-height: 1.9;
color: var(--jac-muted);
}
.jac-exhibit em {
font-style: normal;
color: var(--jac-ink);
}
.jac-ledger {
margin: 16px 0 0;
padding: 0;
list-style: none;
font-size: 12.5px;
}
.jac-ledger li {
display: flex;
justify-content: space-between;
gap: 16px;
padding: 9px 2px;
border-top: 1px solid var(--jac-line);
line-height: 1.5;
}
.jac-ledger li:first-child {
border-top: none;
}
.jac-ledger .jac-small {
text-align: right;
max-width: 30ch;
}
/* ==========================================================================
Voice attestation
========================================================================== */
.jac-voice {
border: 1px solid var(--jac-line);
border-radius: var(--jac-r-md);
padding: 15px 16px;
background: var(--jac-surface-1);
}
.jac-voice-row {
display: flex;
align-items: center;
gap: 13px;
}
.jac-mic-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 46px;
height: 46px;
border-radius: 50%;
border: 1.5px solid color-mix(in srgb, var(--jac-accent) 55%, var(--jac-line));
background: var(--jac-accent-wash);
color: var(--jac-accent-ink);
cursor: pointer;
flex: none;
font-size: 18px;
transition:
box-shadow var(--dur-base) var(--jac-ease),
background var(--dur-fast) var(--jac-ease);
}
.jac-mic-btn:hover {
background: color-mix(in srgb, var(--jac-accent) 22%, transparent);
}
.jac-mic-btn[data-recording="true"] {
background: var(--jac-blocked-wash);
border-color: var(--jac-blocked);
color: var(--jac-blocked);
animation: jac-beat 1.5s ease-in-out infinite;
}
.jac-voice-status {
font-size: 12.5px;
color: var(--jac-muted);
flex: 1;
line-height: 1.5;
}
.jac-voice-transcript {
margin-top: 13px;
font-family: var(--jac-mono);
font-size: 13px;
line-height: 1.7;
white-space: pre-wrap;
min-height: 1.7em;
color: var(--jac-ink);
}
.jac-voice-transcript[data-empty="true"] {
color: var(--jac-muted);
font-style: italic;
}
.jac-voice-interim {
opacity: 0.5;
}
.jac-type-instead {
background: none;
border: none;
padding: 0;
font-size: 11.5px;
color: var(--jac-accent-ink);
text-decoration: underline;
text-underline-offset: 3px;
cursor: pointer;
}
/* ==========================================================================
The loom — generative provenance cloth
========================================================================== */
.jac-loom {
position: relative;
border: 1px solid var(--jac-line);
border-radius: var(--jac-r-lg);
background: var(--jac-code-bg);
overflow: hidden;
box-shadow: var(--jac-inset-hi);
}
.jac-loom svg {
display: block;
width: 100%;
}
/* Each pick fades in as the shuttle passes it. */
.jac-loom .jac-pick {
animation: jac-lay-pick 420ms var(--jac-ease) both;
}
/* The shuttle: a bright weft carrier crossing the warp, once, on mount. */
.jac-loom-shuttle {
position: absolute;
top: 0;
bottom: 0;
width: 88px;
pointer-events: none;
background: linear-gradient(
90deg,
transparent,
rgba(234, 228, 214, 0.09) 45%,
rgba(234, 228, 214, 0.16) 50%,
rgba(234, 228, 214, 0.09) 55%,
transparent
);
animation: jac-shuttle 2.4s var(--jac-ease) 1;
}
.jac-loom-legend {
display: flex;
flex-wrap: wrap;
gap: 8px 16px;
align-items: center;
padding: 9px 14px;
border-top: 1px solid var(--jac-line);
font-size: 11px;
color: var(--jac-muted);
background: var(--jac-surface-1);
}
/* ==========================================================================
Archive plates — Library of Congress, HABS/HAER (public domain)
Treated: desaturated, low opacity, always behind content, aria-hidden.
========================================================================== */
.jac-plate {
position: relative;
isolation: isolate;
overflow: hidden;
}
.jac-plate::before {
content: "";
position: absolute;
inset: 0;
z-index: -1;
background-image: var(--plate);
background-size: cover;
background-position: center;
filter: grayscale(1) contrast(1.15);
opacity: 0.1;
mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.9), transparent 82%);
}
.jac-site[data-jac-theme="light"] .jac-plate::before {
opacity: 0.07;
}
.jac-credit {
font-size: 10px;
letter-spacing: 0.04em;
color: var(--jac-muted);
opacity: 0.75;
}
/* ==========================================================================
The diff — the detail view.
Console density, but its own reading: the left rail is "the cut" (which
paths the change touches and what governs them), and the file is read as
cloth — picks laid in, threads cut.
========================================================================== */
.jac-diff {
display: grid;
grid-template-columns: 250px minmax(0, 1fr);
gap: 14px;
align-items: start;
}
.jac-diff-rail {
position: sticky;
top: 0;
padding: 14px 14px 16px;
border: 1px solid var(--jac-line);
border-radius: var(--jac-r-lg);
background: var(--jac-surface-1);
}
.jac-cut {
list-style: none;
margin: 0;
padding: 0;
}
.jac-cut-item {
display: flex;
align-items: center;
gap: 8px;
padding: 5px 7px;
border-radius: 6px;
color: var(--jac-muted);
text-decoration: none;
font-family: var(--jac-mono);
font-size: 11.5px;
transition:
background var(--dur-fast) var(--jac-ease),
color var(--dur-fast) var(--jac-ease);
}
.jac-cut-item:hover {
background: var(--jac-surface-2);
color: var(--jac-ink);
}
.jac-cut-item[data-on="true"] {
background: var(--jac-surface-3);
color: var(--jac-ink);
}
.jac-cut-path {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
direction: rtl;
text-align: left;
}
.jac-cut-flag {
flex: none;
font-size: 10px;
color: var(--jac-blocked);
}
.jac-cut-flag[data-settled="true"] {
color: var(--jac-ok);
}
.jac-cut-flag[data-ungoverned="true"] {
color: var(--jac-muted);
opacity: 0.6;
}
.jac-diff-stat {
display: inline-flex;
gap: 10px;
font-family: var(--jac-mono);
font-size: 11.5px;
}
.jac-diff-add {
color: var(--jac-ok);
}
.jac-diff-del {
color: var(--jac-blocked);
}
/* The shape of the change at a glance — a strip of cloth. */
.jac-diff-strip {
display: flex;
gap: 1px;
height: 5px;
padding: 0 1px;
background: var(--jac-code-bg);
border-bottom: 1px solid var(--jac-line);
}
.jac-diff-strip span {
min-width: 1px;
background: var(--jac-ink);
opacity: 0.08;
}
.jac-diff-strip span[data-kind="add"] {
background: var(--jac-ok);
opacity: 0.8;
}
.jac-diff-strip span[data-kind="del"] {
background: var(--jac-blocked);
opacity: 0.8;
}
.jac-diff-body {
font-family: var(--jac-mono);
font-size: 12px;
line-height: 1.65;
overflow-x: auto;
max-height: calc(100vh - 340px);
overflow-y: auto;
}
.jac-diff-row {
display: grid;
grid-template-columns: 44px 44px 16px 1fr;
align-items: baseline;
white-space: pre;
border-left: 2px solid transparent;
}
.jac-diff-row[data-kind="add"] {
background: rgba(134, 199, 151, 0.07);
border-left-color: var(--jac-ok);
}
.jac-diff-row[data-kind="del"] {
background: rgba(228, 130, 122, 0.07);
border-left-color: var(--jac-blocked);
}
.jac-diff-ln {
text-align: right;
padding-right: 10px;
color: var(--jac-muted);
opacity: 0.5;
font-size: 10.5px;
user-select: none;
}
.jac-diff-mark {
color: var(--jac-muted);
user-select: none;
}
.jac-diff-row[data-kind="add"] .jac-diff-mark {
color: var(--jac-ok);
}
.jac-diff-row[data-kind="del"] .jac-diff-mark {
color: var(--jac-blocked);
}
.jac-diff-text {
padding-right: 14px;
color: var(--jac-ink);
}
.jac-diff-row[data-kind="ctx"] .jac-diff-text {
color: var(--jac-muted);
}
.jac-diff-gap {
padding: 2px 0 2px 104px;
color: var(--jac-muted);
opacity: 0.45;
user-select: none;
border-top: 1px solid var(--jac-line);
border-bottom: 1px solid var(--jac-line);
}
@media (max-width: 900px) {
.jac-diff {
grid-template-columns: 1fr;
}
.jac-diff-rail {
position: static;
}
}
/* ==========================================================================
Jackie's stage — the front door
Everything dense lives one link away. This surface has one job: get you
into a conversation.
========================================================================== */
.jac-stage-wrap {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
min-height: 100%;
}
.jac-stage-top {
display: flex;
align-items: center;
gap: 10px;
padding: 18px 24px;
font-size: 12px;
}
.jac-stage-top .jac-type-instead {
letter-spacing: 0.04em;
}
.jac-stage {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 18px;
text-align: center;
padding: 40px 24px 72px;
max-width: 720px;
margin: 0 auto;
width: 100%;
animation: jac-enter var(--dur-base) var(--jac-ease);
}
.jac-stage--running {
justify-content: flex-start;
padding-top: 28px;
}
.jac-stage-title {
font-family: var(--jac-display);
font-weight: 650;
font-size: clamp(26px, 4vw, 40px);
letter-spacing: -0.025em;
line-height: 1.1;
max-width: 18ch;
}
.jac-stage-line {
font-size: 14px;
line-height: 1.65;
color: var(--jac-muted);
max-width: 54ch;
margin: 0;
}
.jac-stage-foot {
font-size: 11.5px;
color: var(--jac-muted);
opacity: 0.7;
max-width: 52ch;
margin: 0;
}
/* What Jackie is saying, one line at a time. The biggest text on screen —
this is the interface. */
.jac-stage-say {
font-family: var(--jac-display);
font-weight: 600;
font-size: clamp(18px, 2.2vw, 25px);
line-height: 1.4;
letter-spacing: -0.015em;
max-width: 30ch;
min-height: 2.8em;
margin: 4px 0 0;
text-wrap: balance;
}
/* What you are saying back — gold, because that thread is yours. */
.jac-stage-heard {
font-family: var(--jac-mono);
font-size: 14px;
line-height: 1.6;
color: var(--jac-human);
max-width: 52ch;
margin: 0;
min-height: 1.6em;
}
.jac-stage-card {
width: 100%;
max-width: 620px;
text-align: left;
padding: 16px 18px;
border: 1px solid var(--jac-line);
border-radius: var(--jac-r-lg);
background: var(--jac-surface-1);
box-shadow: var(--jac-inset-hi);
animation: jac-enter var(--dur-base) var(--jac-ease);
}
.jac-stage-card--wide {
max-width: 720px;
}
.jac-stage-type {
width: 100%;
max-width: 620px;
display: flex;
flex-direction: column;
gap: 10px;
align-items: flex-end;
}
.jac-stage-done {
display: flex;
flex-direction: column;
align-items: center;
gap: 14px;
}
.jac-stage-controls {
display: flex;
align-items: center;
gap: 16px;
margin-top: 8px;
flex-wrap: wrap;
justify-content: center;
}
.jac-stage-progress {
font-size: 11px;
color: var(--jac-muted);
opacity: 0.7;
}
.jac-stage-switch {
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
flex-wrap: wrap;
padding: 0 24px 40px;
}
/* The one button on the front door. */
.jac-talk {
display: inline-flex;
align-items: center;
gap: 11px;
height: 52px;
padding: 0 26px;
border-radius: 999px;
border: 1px solid color-mix(in srgb, var(--jac-accent) 45%, var(--jac-line));
background: color-mix(in srgb, var(--jac-accent) 15%, transparent);
color: var(--jac-ink);
font-family: var(--jac-sans);
font-size: 14px;
font-weight: 600;
letter-spacing: 0.02em;
cursor: pointer;
transition:
background var(--dur-base) var(--jac-ease),
transform var(--dur-fast) var(--jac-ease),
box-shadow var(--dur-base) var(--jac-ease);
}
.jac-talk:hover {
background: color-mix(in srgb, var(--jac-accent) 24%, transparent);
transform: translateY(-1px);
box-shadow: 0 8px 26px rgba(0, 0, 0, 0.4);
}
.jac-talk-dot {
width: 9px;
height: 9px;
border-radius: 999px;
background: var(--jac-accent);
animation: jac-beat-indigo 2.4s ease-in-out infinite;
}
/* A review is waiting. Quiet, not a klaxon — the gate does not punish. */
.jac-alert {
display: inline-flex;
align-items: center;
gap: 9px;
align-self: center;
margin: 14px auto 0;
padding: 6px 14px;
border-radius: 999px;
border: 1px solid rgba(228, 130, 122, 0.35);
background: var(--jac-blocked-wash);
color: var(--jac-blocked);
font-size: 12px;
animation: jac-enter var(--dur-base) var(--jac-ease);
}
.jac-alert-dot {
width: 7px;
height: 7px;
border-radius: 999px;
background: var(--jac-blocked);
animation: jac-beat-red 2s ease-in-out infinite;
}
/* ---- what's on offer. She points; you choose. ---- */
.jac-offers {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(212px, 1fr));
gap: 10px;
width: 100%;
max-width: 720px;
margin-top: 4px;
text-align: left;
}
.jac-offer {
display: flex;
flex-direction: column;
gap: 5px;
padding: 13px 15px;
border: 1px solid var(--jac-line);
border-radius: var(--jac-r-md);
background: var(--jac-surface-1);
color: var(--jac-ink);
font: inherit;
text-align: left;
cursor: pointer;
transition:
background var(--dur-fast) var(--jac-ease),
border-color var(--dur-fast) var(--jac-ease),
transform var(--dur-fast) var(--jac-ease);
}
.jac-offer:hover {
background: var(--jac-surface-2);
border-color: color-mix(in srgb, var(--jac-accent) 40%, var(--jac-line));
transform: translateY(-1px);
}
.jac-offer-label {
font-family: var(--jac-display);
font-weight: 600;
font-size: 14px;
letter-spacing: -0.01em;
}
.jac-offer-pitch {
font-size: 12px;
line-height: 1.5;
color: var(--jac-muted);
}
/* The one offer that writes something down gets the agent thread. */
.jac-offer--record {
grid-column: 1 / -1;
border-color: color-mix(in srgb, var(--jac-accent) 35%, var(--jac-line));
background: var(--jac-accent-wash);
}
.jac-seen {
display: flex;
flex-wrap: wrap;
gap: 7px;
justify-content: center;
max-width: 720px;
}
.jac-seen-chip {
padding: 3px 11px;
border-radius: 999px;
border: 1px solid var(--jac-line);
background: transparent;
color: var(--jac-muted);
font: inherit;
font-size: 11.5px;
cursor: pointer;
transition: color var(--dur-fast) var(--jac-ease);
}
.jac-seen-chip:hover {
color: var(--jac-ink);
border-color: var(--jac-line-strong);
}
/* ---- the orb: a kaleidoscope of woven damask ---- */
.jac-orb {
position: relative;
flex: none;
display: grid;
place-items: center;
transform: scale(var(--orb-swell, 1));
transition: transform 120ms linear;
}
.jac-orb svg {
display: block;
overflow: visible;
}
/* The WebGL figure draws its own falloff, so it needs no extra halo. */
.jac-orb-canvas {
display: block;
}
.jac-orb--gl::after {
opacity: 0 !important;
}
/* The figure drifts bodily toward whatever you are reaching for. The drift
is set per frame by the renderer; the swell still tracks speech. */
.jac-orb--gl {
transform: translate(var(--orb-drift-x, 0), var(--orb-drift-y, 0))
scale(var(--orb-swell, 1));
transition: none;
will-change: transform;
}
/* The glass turns. The two layers counter-rotate at unrelated rates, so the
figure never repeats the same way twice — which is what a kaleidoscope
does, and what a jacquard draft does over a long enough repeat. */
.jac-orb-glass {
transform-origin: 60px 60px;
transform-box: view-box;
/* Period comes from the subject; the conversational state only scales it.
See MOTIFS / TEMPO in jackie-orb.tsx. */
animation: jac-wind var(--orb-period, 44s) linear infinite;
}
.jac-orb-glass--counter {
animation: jac-wind-back var(--orb-period-counter, 27s) linear infinite;
}
/* The figure belongs to the subject, so changing subject should read as a
change of figure — it fades up rather than cutting. */
.jac-orb svg {
animation: jac-orb-enter 420ms var(--jac-ease) both;
}
@keyframes jac-orb-enter {
from {
opacity: 0;
transform: scale(0.9) rotate(-8deg);
}
to {
opacity: 1;
transform: none;
}
}
.jac-orb::after {
content: "";
position: absolute;
inset: -12%;
border-radius: 50%;
background: radial-gradient(
circle,
color-mix(in srgb, var(--orb-thread) 18%, transparent) 0%,
transparent 62%
);
opacity: 0;
transition: opacity var(--dur-base) var(--jac-ease);
pointer-events: none;
}
.jac-orb[data-state="speaking"]::after,
.jac-orb[data-state="listening"]::after {
opacity: 1;
}
@keyframes jac-wind {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes jac-wind-back {
from {
transform: rotate(360deg);
}
to {
transform: rotate(0deg);
}
}
@keyframes jac-beat-indigo {
0%,
100% {
box-shadow: 0 0 0 0 var(--jac-accent-wash);
}
50% {
box-shadow: 0 0 0 7px transparent;
}
}
@keyframes jac-beat-red {
0%,
100% {
box-shadow: 0 0 0 0 var(--jac-blocked-wash);
}
50% {
box-shadow: 0 0 0 6px transparent;
}
}
/* ==========================================================================
Motion — the mill running
========================================================================== */
@keyframes jac-enter {
from {
transform: translateY(5px);
opacity: 0.6;
}
to {
transform: none;
opacity: 1;
}
}
@keyframes jac-fade {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* The shuttle crosses the warp. */
@keyframes jac-shuttle {
from {
transform: translateX(-120px);
}
to {
transform: translateX(calc(100vw + 120px));
}
}
/* A pick is laid and beaten up against the fell of the cloth. */
@keyframes jac-lay-pick {
from {
opacity: 0;
transform: translateY(-3px);
}
to {
opacity: 1;
transform: none;
}
}
/* The reed beat — the loom's heartbeat, ~40 picks/min. */
@keyframes jac-beat {
0%,
100% {
box-shadow: 0 0 0 0 var(--jac-blocked-wash);
}
50% {
box-shadow: 0 0 0 7px transparent;
}
}
/* A card hole punched through. */
@keyframes jac-punch {
from {
transform: scale(0.55);
}
to {
transform: scale(1);
}
}
/* Line-shaft belt drive — diagonal travel for indeterminate work. */
@keyframes jac-belt {
from {
background-position: 0 0;
}
to {
background-position: 28px 0;
}
}
@keyframes jac-shimmer {
from {
background-position: 100% 0;
}
to {
background-position: -100% 0;
}
}
.jac-belt {
height: 3px;
border-radius: 999px;
background-image: repeating-linear-gradient(
115deg,
var(--jac-accent) 0 6px,
transparent 6px 14px
);
background-size: 28px 100%;
opacity: 0.65;
animation: jac-belt 700ms linear infinite;
}
.jac-skel {
background: linear-gradient(
100deg,
var(--jac-surface-1) 30%,
var(--jac-surface-3) 50%,
var(--jac-surface-1) 70%
);
background-size: 200% 100%;
animation: jac-shimmer 1.35s ease-in-out infinite;
border-radius: 6px;
}
/* The landing moment: an id materialising out of the weave. */
.jac-materialize {
display: inline-block;
font-family: var(--jac-mono);
animation: jac-materialize 1.3s var(--jac-ease) both;
}
@keyframes jac-materialize {
from {
opacity: 0;
letter-spacing: 0.42em;
filter: blur(4px);
}
to {
opacity: 1;
letter-spacing: 0.01em;
filter: blur(0);
}
}
/*
* s10 ships no reduced-motion block; that omission is not inherited. Every
* animation above is decorative — none of them carries information that is
* lost when they stop.
*/
@media (prefers-reduced-motion: reduce) {
.jac-site *,
.jac-site *::before,
.jac-site *::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
}
.jac-loom-shuttle {
display: none;
}
}
/* ==========================================================================
Responsive — the rail folds away before the content does
========================================================================== */
@media (max-width: 900px) {
.jac-app {
grid-template-columns: 1fr;
height: auto;
overflow: visible;
}
.jac-rail {
flex-direction: row;
flex-wrap: wrap;
align-items: center;
gap: 4px;
border-right: none;
border-bottom: 1px solid var(--jac-line);
overflow: visible;
}
.jac-rail-brand {
border-bottom: none;
padding: 12px 14px;
}
.jac-rail-section {
display: flex;
flex-wrap: wrap;
gap: 2px;
padding: 4px 8px;
}
.jac-rail-section-label,
.jac-rail-foot {
display: none;
}
.jac-scroll {
overflow: visible;
}
.jac-wizard {
grid-template-columns: 1fr;
}
.jac-grid--4,
.jac-grid--3,
.jac-grid--2 {
grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}
}
/* ==========================================================================
The orb lab — a chooser for the figure
========================================================================== */
.jac-lab-grid {
display: grid;
gap: 14px;
margin-bottom: 24px;
}
.jac-lab-tile {
display: flex;
flex-direction: column;
gap: 10px;
padding: 14px;
border: 1px solid var(--jac-line);
border-radius: var(--jac-r-lg);
background: var(--jac-surface-1);
color: var(--jac-ink);
font: inherit;
text-align: left;
cursor: pointer;
transition:
border-color var(--dur-fast) var(--jac-ease),
background var(--dur-fast) var(--jac-ease),
transform var(--dur-fast) var(--jac-ease);
}
.jac-lab-tile:hover {
border-color: color-mix(in srgb, var(--jac-accent) 45%, var(--jac-line));
background: var(--jac-surface-2);
transform: translateY(-2px);
}
.jac-lab-tile[data-focused="true"] {
border-color: var(--jac-accent);
background: var(--jac-accent-wash);
}
.jac-lab-stage {
display: grid;
place-items: center;
border-radius: var(--jac-r-md);
background: radial-gradient(circle at 50% 45%, rgba(255,255,255,0.03), transparent 70%),
var(--jac-code-bg);
overflow: hidden;
}
.jac-lab-meta {
display: flex;
flex-direction: column;
gap: 3px;
min-width: 0;
}
.jac-lab-n {
font-family: var(--jac-mono);
font-size: 10px;
color: var(--jac-muted);
opacity: 0.6;
}
.jac-lab-label {
font-family: var(--jac-display);
font-weight: 600;
font-size: 14px;
letter-spacing: -0.01em;
}
.jac-lab-engine {
font-size: 9.5px;
font-weight: 600;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--jac-sage);
}
.jac-lab-blurb {
font-size: 11.5px;
line-height: 1.5;
color: var(--jac-muted);
}
.jac-lab-focus {
position: fixed;
inset: 0;
z-index: 60;
display: grid;
place-items: center;
background: rgba(3, 7, 12, 0.72);
backdrop-filter: blur(3px);
padding: 24px;
animation: jac-fade var(--dur-base) var(--jac-ease);
}
.jac-lab-focus-inner {
width: min(640px, 94vw);
padding: 20px 22px;
border: 1px solid var(--jac-line-strong);
border-radius: var(--jac-r-lg);
background: var(--jac-paper);
box-shadow: var(--jac-shadow-pop);
}
/* ---- the pure-CSS variant ---- */
.jac-css-orb {
display: grid;
place-items: center;
perspective: 700px;
}
.jac-css-scene {
position: relative;
width: 100%;
height: 100%;
transform-style: preserve-3d;
display: grid;
place-items: center;
animation: jac-css-spin 22s linear infinite;
}
.jac-css-ring {
position: absolute;
border-radius: 50%;
border: 1.5px solid rgba(var(--css-orb-rgb), 0.55);
box-shadow: 0 0 12px rgba(var(--css-orb-rgb), 0.35);
transform-style: preserve-3d;
transform: rotateX(calc(28deg * var(--i))) rotateY(calc(22deg * var(--i)));
animation: jac-css-ring-spin calc(9s + var(--i) * 3s) linear infinite;
animation-direction: alternate;
}
.jac-css-core {
width: 34%;
height: 34%;
border-radius: 50%;
background: radial-gradient(
circle at 38% 34%,
rgba(255, 255, 255, 0.9),
rgba(var(--css-orb-rgb), 0.85) 45%,
rgba(var(--css-orb-rgb), 0.15) 75%,
transparent
);
box-shadow: 0 0 30px rgba(var(--css-orb-rgb), 0.55);
}
@keyframes jac-css-spin {
from { transform: rotateY(0deg); }
to { transform: rotateY(360deg); }
}
@keyframes jac-css-ring-spin {
from { transform: rotateX(calc(28deg * var(--i))) rotateY(calc(22deg * var(--i))) rotateZ(0deg); }
to { transform: rotateX(calc(28deg * var(--i) + 40deg)) rotateY(calc(22deg * var(--i) + 60deg)) rotateZ(180deg); }
}
/* --------------------------------------------------------------- remarks --
* Saying something about the work you are reading.
*
* The quote is the load-bearing element: a suggestion is close to meaningless
* without the sentence that provoked it, so it is rendered with the reviewer's
* words everywhere the remark appears, not just at the moment of writing.
*/
.jac-remark-zone {
position: relative;
}
/* Floats over the selection the reader just made. */
.jac-remark-pin {
position: absolute;
z-index: 20;
transform: translate(-50%, -100%);
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
border: 1px solid var(--jac-line);
border-radius: 999px;
background: var(--jac-paper-2, #1d1a22);
color: var(--jac-ink);
font-size: 12px;
letter-spacing: 0.02em;
cursor: pointer;
box-shadow: 0 8px 22px rgba(0, 0, 0, 0.34);
animation: jac-remark-pin-in 140ms ease-out;
}
.jac-remark-pin:hover {
border-color: var(--jac-accent);
}
.jac-remark-pin kbd {
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 11px;
opacity: 0.7;
}
@keyframes jac-remark-pin-in {
from {
opacity: 0;
transform: translate(-50%, -90%);
}
}
.jac-remark-open {
margin-top: 14px;
}
.jac-remark-composer {
margin-top: 14px;
padding: 16px;
border: 1px solid var(--jac-line);
border-left: 3px solid var(--jac-human);
border-radius: 12px;
background: rgba(255, 255, 255, 0.02);
}
/* The claim being remarked on: quiet, but always present. */
.jac-remark-quote {
margin: 0 0 12px;
padding-left: 12px;
border-left: 2px solid var(--jac-line);
color: var(--jac-muted);
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 12.8px;
line-height: 1.55;
}
.jac-remark-quote-none {
margin: 0 0 12px;
color: var(--jac-muted);
font-style: italic;
}
.jac-remark-say {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 10px;
}
/* Ringed by actual microphone amplitude, so you can see it hearing you. */
.jac-mic {
flex: none;
padding: 8px 16px;
border: 1px solid var(--jac-line);
border-radius: 999px;
background: transparent;
color: var(--jac-ink);
font-size: 13px;
cursor: pointer;
}
.jac-mic:disabled {
opacity: 0.45;
cursor: not-allowed;
}
.jac-mic[data-live="true"] {
border-color: var(--jac-human);
color: var(--jac-human);
box-shadow: 0 0 0 calc(2px + var(--mic-level, 0) * 10px)
rgba(228, 192, 109, calc(0.05 + var(--mic-level, 0) * 0.22));
}
.jac-remark-kinds {
display: flex;
gap: 8px;
margin-top: 10px;
}
.jac-chip[data-on="true"] {
border-color: var(--jac-accent);
color: var(--jac-accent);
}
.jac-chip--tiny {
padding: 1px 7px;
font-size: 10.5px;
}
.jac-remark-hint {
margin: 6px 0 0;
color: var(--jac-muted);
}
.jac-remark-actions {
display: flex;
align-items: center;
gap: 10px;
margin-top: 14px;
flex-wrap: wrap;
}
.jac-remark-list {
list-style: none;
margin: 12px 0 0;
padding: 0;
display: grid;
gap: 12px;
}
.jac-remark {
padding: 14px 16px;
border: 1px solid var(--jac-line);
border-radius: 12px;
/* The selvage names the intent at a glance. */
border-left: 3px solid var(--jac-muted);
}
.jac-remark[data-kind="suggestion"] {
border-left-color: var(--jac-accent);
}
.jac-remark[data-kind="question"] {
border-left-color: var(--jac-human);
}
.jac-remark[data-state="drafted"],
.jac-remark[data-state="declined"] {
opacity: 0.62;
}
.jac-remark-body {
margin: 0;
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 13.5px;
line-height: 1.6;
}
.jac-remark-meta {
display: flex;
align-items: center;
gap: 8px;
margin: 10px 0 0;
color: var(--jac-muted);
flex-wrap: wrap;
}
.jac-remark-outcome {
color: var(--jac-muted);
}
.jac-remark-empty {
margin-top: 12px;
color: var(--jac-muted);
font-style: italic;
}
.jac-warn {
color: var(--jac-ungoverned, #e4c06d);
}
.jac-error {
color: var(--jac-blocked);
}
@media (prefers-reduced-motion: reduce) {
.jac-remark-pin {
animation: none;
}
}