← snapshot
84967 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;
}
}
/* ------------------------------------------------------------ diff modal --
* Reading a change without leaving the tour.
*
* The add/remove hues sit deliberately outside the verdict palette. Madder is
* blocked, verdant is admitted, amber is ungoverned — a green line background
* meaning "added" would be one glance from meaning "this passed". Teal and
* plum carry the diff instead, and the two vocabularies never share a shape:
* verdicts are pills, diffs are rows.
*/
:root {
--jac-add: #4fb3a1;
--jac-add-wash: rgba(79, 179, 161, 0.09);
--jac-add-edge: rgba(79, 179, 161, 0.5);
--jac-del: #c07ba8;
--jac-del-wash: rgba(192, 123, 168, 0.09);
--jac-del-edge: rgba(192, 123, 168, 0.5);
}
:root[data-jac-theme="light"] {
--jac-add: #237e6c;
--jac-add-wash: rgba(35, 126, 108, 0.1);
--jac-add-edge: rgba(35, 126, 108, 0.45);
--jac-del: #94487a;
--jac-del-wash: rgba(148, 72, 122, 0.1);
--jac-del-edge: rgba(148, 72, 122, 0.45);
}
.jac-diffm-scrim {
position: fixed;
inset: 0;
z-index: 60;
display: grid;
place-items: center;
padding: 3vh 2vw;
background: rgba(3, 7, 12, 0.72);
backdrop-filter: blur(14px) saturate(0.9);
animation: jac-diffm-fade 180ms ease-out;
}
.jac-diffm {
display: flex;
flex-direction: column;
width: min(1440px, 96vw);
height: min(940px, 94vh);
border: 1px solid var(--jac-line);
border-radius: 16px;
background: var(--jac-paper);
box-shadow:
0 40px 90px rgba(0, 0, 0, 0.55),
0 2px 0 rgba(255, 255, 255, 0.05) inset;
overflow: hidden;
animation: jac-diffm-rise 220ms cubic-bezier(0.16, 0.84, 0.36, 1);
}
/* A hairline selvage, the way every panel in this app is bound. */
.jac-diffm::before {
content: "";
display: block;
height: 2px;
background: linear-gradient(
90deg,
var(--jac-add) 0%,
var(--jac-accent) 50%,
var(--jac-del) 100%
);
opacity: 0.55;
}
@keyframes jac-diffm-fade {
from { opacity: 0; }
}
@keyframes jac-diffm-rise {
from {
opacity: 0;
transform: translateY(14px) scale(0.985);
}
}
.jac-diffm-head {
display: flex;
align-items: center;
gap: 16px;
padding: 14px 18px;
border-bottom: 1px solid var(--jac-line);
flex-wrap: wrap;
}
.jac-diffm-title {
display: flex;
align-items: center;
gap: 14px;
min-width: 0;
}
.jac-diffm-path {
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.jac-diffm-path-dir {
color: var(--jac-muted);
}
.jac-diffm-path b {
color: var(--jac-ink);
font-weight: 600;
}
.jac-diffm-stat {
display: inline-flex;
align-items: center;
gap: 7px;
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 12px;
}
.jac-diffm-stat b { color: var(--jac-add); font-weight: 600; }
.jac-diffm-stat i { color: var(--jac-del); font-style: normal; }
.jac-diffm-bar {
display: inline-flex;
gap: 2px;
}
.jac-diffm-bar span {
width: 5px;
height: 10px;
border-radius: 1px;
background: var(--jac-del);
opacity: 0.75;
}
.jac-diffm-bar span[data-kind="add"] {
background: var(--jac-add);
}
.jac-diffm-govern {
display: flex;
gap: 6px;
flex-wrap: wrap;
min-width: 0;
max-width: 42%;
}
.jac-diffm-tools {
display: flex;
align-items: center;
gap: 10px;
margin-left: auto;
}
.jac-diffm-close {
display: inline-flex;
align-items: center;
gap: 7px;
padding: 6px 13px;
border: 1px solid var(--jac-line);
border-radius: 999px;
background: transparent;
color: var(--jac-ink);
font-size: 12.5px;
cursor: pointer;
}
.jac-diffm-close:hover { border-color: var(--jac-accent); }
.jac-diffm kbd {
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 10.5px;
padding: 1px 5px;
border: 1px solid var(--jac-line);
border-radius: 4px;
color: var(--jac-muted);
}
.jac-diffm-main {
display: flex;
min-height: 0;
flex: 1;
}
/* The changed-path rail. */
.jac-diffm-rail {
width: 236px;
flex: none;
border-right: 1px solid var(--jac-line);
overflow-y: auto;
padding: 10px 8px;
}
.jac-diffm-rail-label {
margin: 2px 8px 8px;
font-size: 10.5px;
letter-spacing: 0.13em;
text-transform: uppercase;
color: var(--jac-muted);
}
.jac-diffm-file {
display: block;
width: 100%;
text-align: left;
padding: 8px 10px;
margin-bottom: 2px;
border: 0;
border-left: 2px solid transparent;
border-radius: 8px;
background: transparent;
color: var(--jac-ink);
cursor: pointer;
}
.jac-diffm-file:hover { background: rgba(255, 255, 255, 0.04); }
.jac-diffm-file[data-on="true"] {
background: rgba(255, 255, 255, 0.06);
border-left-color: var(--jac-accent);
}
.jac-diffm-file-name {
display: block;
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 12.5px;
}
.jac-diffm-file-dir {
display: block;
font-size: 10.5px;
color: var(--jac-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
direction: rtl;
text-align: left;
}
.jac-diffm-file-stat {
display: inline-flex;
gap: 6px;
margin-top: 3px;
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 10.5px;
}
.jac-diffm-file-stat b { color: var(--jac-add); font-weight: 500; }
.jac-diffm-file-stat i { color: var(--jac-del); font-style: normal; }
.jac-diffm-body {
flex: 1;
min-width: 0;
overflow: auto;
padding: 14px 18px 20px;
}
.jac-diffm-empty,
.jac-diffm-note {
margin: 8px 2px 14px;
color: var(--jac-muted);
font-style: italic;
font-size: 12.5px;
}
.jac-diffm-table {
width: 100%;
border-collapse: collapse;
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 12.5px;
line-height: 1.62;
}
.jac-diffm-row td {
padding: 0;
vertical-align: top;
}
/* Line numbers: present, but never competing with the code. */
.jac-diffm-ln {
width: 46px;
padding: 0 10px 0 0 !important;
text-align: right;
color: var(--jac-muted);
opacity: 0.5;
user-select: none;
font-size: 11px;
}
.jac-diffm-mark {
width: 16px;
text-align: center;
user-select: none;
opacity: 0.85;
}
.jac-diffm-text {
padding-left: 10px !important;
white-space: pre-wrap;
word-break: break-word;
}
/* The wash fades left to right, so the gutter mark stays the anchor. */
.jac-diffm-row[data-kind="add"] .jac-diffm-text,
.jac-diffm-text[data-kind="add"] {
background: linear-gradient(90deg, var(--jac-add-wash), transparent 78%);
box-shadow: inset 2px 0 0 var(--jac-add-edge);
}
.jac-diffm-row[data-kind="del"] .jac-diffm-text,
.jac-diffm-text[data-kind="del"] {
background: linear-gradient(90deg, var(--jac-del-wash), transparent 78%);
box-shadow: inset 2px 0 0 var(--jac-del-edge);
}
.jac-diffm-row[data-kind="add"] .jac-diffm-mark { color: var(--jac-add); }
.jac-diffm-row[data-kind="del"] .jac-diffm-mark { color: var(--jac-del); }
.jac-diffm-row[data-kind="ctx"] .jac-diffm-text { color: var(--jac-muted); }
/* What actually changed inside the line. */
.jac-diffm-word {
background: transparent;
color: inherit;
border-radius: 3px;
padding: 0 1px;
box-shadow: inset 0 -0.55em 0 var(--jac-add-wash);
}
.jac-diffm-row[data-kind="add"] .jac-diffm-word,
.jac-diffm-text[data-kind="add"] .jac-diffm-word {
box-shadow: inset 0 -0.62em 0 rgba(79, 179, 161, 0.3);
color: var(--jac-ink);
}
.jac-diffm-row[data-kind="del"] .jac-diffm-word,
.jac-diffm-text[data-kind="del"] .jac-diffm-word {
box-shadow: inset 0 -0.62em 0 rgba(192, 123, 168, 0.3);
color: var(--jac-ink);
}
.jac-diffm-table[data-split="true"] .jac-diffm-text {
width: 50%;
}
/* Collapsed stretches. */
.jac-diffm-gap td {
padding: 5px 0 !important;
text-align: center;
}
.jac-diffm-gap button {
padding: 2px 14px;
border: 1px dashed var(--jac-line);
border-radius: 999px;
background: transparent;
color: var(--jac-muted);
font-size: 10.5px;
letter-spacing: 0.06em;
cursor: pointer;
}
.jac-diffm-gap button:hover {
color: var(--jac-accent);
border-color: var(--jac-accent);
}
.jac-diffm-foot {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 18px;
border-top: 1px solid var(--jac-line);
color: var(--jac-muted);
}
@media (max-width: 900px) {
.jac-diffm-rail { display: none; }
.jac-diffm-govern { max-width: 100%; }
.jac-diffm-table[data-split="true"] { font-size: 11px; }
}
@media (prefers-reduced-motion: reduce) {
.jac-diffm-scrim,
.jac-diffm { animation: none; }
}
/* A folded change: many files, one substitution, described once. */
.jac-diffm-fold {
display: grid;
grid-template-columns: auto 1fr;
grid-template-areas: "badge name" "badge dir";
column-gap: 8px;
align-items: center;
}
.jac-diffm-fold .jac-diffm-file-name {
grid-area: name;
font-size: 11.5px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.jac-diffm-fold .jac-diffm-file-dir {
grid-area: dir;
direction: ltr;
}
.jac-diffm-fold-badge {
grid-area: badge;
display: inline-grid;
place-items: center;
min-width: 30px;
padding: 3px 6px;
border-radius: 6px;
background: var(--jac-accent);
color: var(--jac-paper);
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 11px;
font-weight: 600;
}
.jac-diffm-subs {
display: grid;
gap: 8px;
margin-bottom: 14px;
}
.jac-diffm-sub {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 14px;
border: 1px solid var(--jac-line);
border-radius: 10px;
background: rgba(255, 255, 255, 0.02);
flex-wrap: wrap;
}
.jac-diffm-sub code {
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 14px;
padding: 3px 9px;
border-radius: 6px;
}
.jac-diffm-sub-before {
background: var(--jac-del-wash);
color: var(--jac-del);
box-shadow: inset 0 0 0 1px var(--jac-del-edge);
}
.jac-diffm-sub-after {
background: var(--jac-add-wash);
color: var(--jac-add);
box-shadow: inset 0 0 0 1px var(--jac-add-edge);
}
.jac-diffm-sub-arrow {
color: var(--jac-muted);
font-size: 15px;
}
.jac-diffm-sub-count {
margin-left: auto;
color: var(--jac-muted);
font-size: 11.5px;
}
.jac-diffm-sample-label {
margin: 16px 0 6px;
font-size: 10.5px;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--jac-muted);
}
.jac-diffm-sample-label b {
font-family: ui-monospace, "SF Mono", Menlo, monospace;
letter-spacing: 0;
text-transform: none;
color: var(--jac-ink);
font-weight: 500;
}
/* Everything the fold covers, still individually reachable. */
.jac-diffm-members {
list-style: none;
margin: 18px 0 0;
padding: 0;
display: grid;
gap: 1px;
border-top: 1px solid var(--jac-line);
}
.jac-diffm-members button {
display: flex;
align-items: center;
gap: 12px;
width: 100%;
padding: 7px 10px;
border: 0;
border-bottom: 1px solid var(--jac-line);
background: transparent;
color: var(--jac-ink);
cursor: pointer;
text-align: left;
}
.jac-diffm-members button:hover {
background: rgba(255, 255, 255, 0.04);
}
.jac-diffm-member-path {
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 12px;
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* A fold a model proposed. Marked, never mistaken for a proven one. */
.jac-diffm-fold[data-judged="true"] .jac-diffm-fold-badge {
background: transparent;
color: var(--jac-accent);
box-shadow: inset 0 0 0 1px var(--jac-accent);
}
.jac-diffm-judged {
margin-bottom: 14px;
padding: 12px 14px;
border: 1px dashed var(--jac-accent);
border-radius: 10px;
background: rgba(138, 166, 224, 0.05);
}
.jac-diffm-judged-why {
margin: 6px 0 8px;
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 12.5px;
line-height: 1.55;
}
/* ----------------------------------------------------------- the weave --
* Every artifact the substrate has produced, in one ledger.
*
* Rows are bound on the left in their provenance colour: gold for a human
* hand, indigo for an agent's, and both for mixed. That is not decoration —
* provenance is hashed into each address, so the colour is showing you part
* of the identity, not a tag someone applied afterwards.
*/
.jac-weavep-head {
margin-bottom: 16px;
}
.jac-weavep-filters {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
margin-bottom: 14px;
}
.jac-weavep-search {
flex: 1;
min-width: 200px;
max-width: 340px;
}
.jac-weavep-body {
display: grid;
grid-template-columns: minmax(320px, 1fr) minmax(360px, 1.15fr);
gap: 18px;
align-items: start;
}
/* Grid items refuse to shrink below their content unless told to; without
this a long source line pushes the whole card wide instead of scrolling. */
.jac-weavep-list,
.jac-weavep-detail,
.jac-weavep-card,
.jac-weavep-source {
min-width: 0;
}
.jac-weavep-list {
list-style: none;
margin: 0;
padding: 0;
max-height: 72vh;
overflow-y: auto;
border: 1px solid var(--jac-line);
border-radius: 12px;
}
.jac-weavep-empty {
padding: 18px;
font-style: italic;
}
.jac-weavep-row {
display: flex;
align-items: flex-start;
gap: 11px;
width: 100%;
padding: 11px 13px;
border: 0;
border-bottom: 1px solid var(--jac-line);
/* The selvage: whose hands made this. */
border-left: 3px solid var(--jac-agent);
background: transparent;
color: var(--jac-ink);
text-align: left;
cursor: pointer;
}
.jac-weavep-row[data-hand="human"] { border-left-color: var(--jac-human); }
.jac-weavep-row[data-hand="mixed"] {
border-left-color: transparent;
border-image: linear-gradient(var(--jac-human) 50%, var(--jac-agent) 50%) 1;
}
.jac-weavep-row:hover { background: rgba(255, 255, 255, 0.035); }
.jac-weavep-row[data-on="true"] { background: rgba(255, 255, 255, 0.06); }
.jac-weavep-glyph {
flex: none;
width: 18px;
text-align: center;
color: var(--jac-muted);
font-size: 13px;
line-height: 1.5;
}
.jac-weavep-row[data-kind="attestation"] .jac-weavep-glyph { color: var(--jac-human); }
.jac-weavep-main {
flex: 1;
min-width: 0;
}
.jac-weavep-title {
display: block;
font-size: 13.5px;
line-height: 1.45;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.jac-weavep-sub {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-top: 3px;
font-size: 11px;
color: var(--jac-muted);
align-items: center;
}
.jac-weavep-warn { color: var(--jac-gold); }
.jac-weavep-ref {
padding: 0 6px;
border: 1px dashed var(--jac-line);
border-radius: 999px;
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 10px;
}
.jac-weavep-when {
flex: none;
font-size: 11px;
color: var(--jac-muted);
}
.jac-weavep-detail {
position: sticky;
top: 16px;
}
.jac-weavep-card {
padding: 16px 18px;
border: 1px solid var(--jac-line);
border-radius: 12px;
max-height: 72vh;
overflow-y: auto;
}
.jac-weavep-address {
display: flex;
flex-direction: column;
gap: 3px;
margin: 12px 0;
}
.jac-weavep-address code {
font-size: 11px;
word-break: break-all;
color: var(--jac-ink);
}
.jac-weavep-facts {
display: grid;
grid-template-columns: auto 1fr;
gap: 5px 14px;
margin: 12px 0 0;
font-size: 12px;
}
.jac-weavep-facts dt {
color: var(--jac-muted);
text-transform: uppercase;
font-size: 10px;
letter-spacing: 0.1em;
padding-top: 2px;
}
.jac-weavep-facts dd { margin: 0; }
.jac-weavep-parent { margin-right: 8px; font-size: 11px; }
.jac-weavep-files {
list-style: none;
margin: 6px 0 0;
padding: 0;
border-top: 1px solid var(--jac-line);
}
.jac-weavep-files button {
display: flex;
align-items: center;
gap: 9px;
width: 100%;
padding: 6px 8px;
border: 0;
border-bottom: 1px solid var(--jac-line);
background: transparent;
color: var(--jac-ink);
cursor: pointer;
text-align: left;
}
.jac-weavep-files button:hover { background: rgba(255, 255, 255, 0.04); }
.jac-weavep-files button[data-on="true"] { background: rgba(255, 255, 255, 0.07); }
.jac-weavep-file-path {
flex: 1;
min-width: 0;
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 11.5px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.jac-weavep-changed {
font-size: 9.5px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--jac-add);
}
.jac-weavep-blob { font-size: 10px; color: var(--jac-muted); }
.jac-weavep-source { margin-top: 14px; }
.jac-weavep-blob-full {
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 9.5px;
text-transform: none;
letter-spacing: 0;
color: var(--jac-muted);
}
@media (max-width: 1080px) {
.jac-weavep-body { grid-template-columns: 1fr; }
.jac-weavep-detail { position: static; }
}
/* ------------------------------------------------------------ the import --
* The one screen where it would be easiest to lie: GitHub hands over an
* author and a description, and rendering those as provenance and rationale
* would take nothing. So the report leads with what it had to guess.
*/
.jac-import-form {
display: grid;
gap: 6px;
max-width: 460px;
margin-top: 20px;
}
.jac-import-form .jac-btn {
justify-self: start;
margin-top: 10px;
}
.jac-import-report {
margin-top: 30px;
padding: 20px 22px;
border: 1px solid var(--jac-line);
border-radius: 14px;
}
.jac-import-cols {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 18px;
margin-top: 18px;
}
.jac-import-facts {
margin: 8px 0 0;
padding-left: 18px;
font-size: 12.5px;
line-height: 1.7;
color: var(--jac-muted);
}
.jac-import-facts b { color: var(--jac-ink); }
.jac-import-inference {
margin-top: 8px;
padding: 12px 14px;
border: 1px solid var(--jac-line);
border-radius: 10px;
}
/* An assumption is bounded in amber, the same as an ungoverned admission —
both are the app saying "nothing verified this". */
.jac-import-inference[data-assumed="true"] {
border-color: var(--jac-gold);
background: var(--jac-gold-wash);
}
.jac-import-gaps {
margin-top: 22px;
padding: 16px 18px;
border: 1px solid var(--jac-line);
border-left: 3px solid var(--jac-human);
border-radius: 12px;
}
.jac-import-gaps ol {
margin: 10px 0 0;
padding-left: 20px;
display: grid;
gap: 12px;
}
.jac-import-ask {
display: block;
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 13px;
line-height: 1.55;
margin-bottom: 3px;
}
.jac-import-honesty {
margin-top: 18px;
font-size: 12.5px;
font-style: italic;
line-height: 1.65;
color: var(--jac-muted);
}
/* ------------------------------------------------------------- the shelf --
* A whole working directory's provenance.
*
* The assumed share is drawn first and given the plainest, largest treatment,
* because it is the finding. A chart that led with "3% machine-written" would
* be reporting a number nobody measured — the 96% is not a claim that people
* wrote it, it is the absence of any claim at all. Amber, the same colour an
* ungoverned admission gets, for the same reason.
*/
.jac-shelf-summary {
padding: 18px 20px;
border: 1px solid var(--jac-line);
border-radius: 14px;
}
.jac-shelf-counts {
display: flex;
gap: 22px;
flex-wrap: wrap;
font-size: 12.5px;
color: var(--jac-muted);
margin-bottom: 14px;
}
.jac-shelf-counts b {
color: var(--jac-ink);
font-size: 17px;
margin-right: 5px;
}
.jac-shelf-failed b { color: var(--jac-blocked); }
.jac-shelf-bar {
display: flex;
height: 26px;
border-radius: 6px;
overflow: hidden;
background: var(--jac-line);
}
.jac-shelf-seg { display: block; height: 100%; }
.jac-shelf-seg[data-kind="assumed"] { background: var(--jac-gold); opacity: 0.55; }
.jac-shelf-seg[data-kind="mixed"] {
background: linear-gradient(90deg, var(--jac-human), var(--jac-agent));
}
.jac-shelf-seg[data-kind="agent"] { background: var(--jac-agent); }
.jac-shelf-legend {
list-style: none;
margin: 14px 0 0;
padding: 0;
display: grid;
gap: 8px;
font-size: 12.5px;
color: var(--jac-muted);
}
.jac-shelf-legend li {
display: flex;
gap: 10px;
align-items: baseline;
line-height: 1.55;
}
.jac-shelf-legend li::before {
content: "";
flex: none;
width: 11px;
height: 11px;
border-radius: 3px;
transform: translateY(1px);
}
.jac-shelf-legend li[data-kind="assumed"]::before {
background: var(--jac-gold);
opacity: 0.55;
}
.jac-shelf-legend li[data-kind="mixed"]::before {
background: linear-gradient(135deg, var(--jac-human), var(--jac-agent));
}
.jac-shelf-legend li[data-kind="agent"]::before { background: var(--jac-agent); }
.jac-shelf-legend b { color: var(--jac-ink); }
.jac-shelf-list {
list-style: none;
margin: 0;
padding: 0;
border: 1px solid var(--jac-line);
border-radius: 12px;
max-height: 62vh;
overflow-y: auto;
}
.jac-shelf-list li {
display: flex;
align-items: center;
gap: 14px;
padding: 8px 14px;
border-bottom: 1px solid var(--jac-line);
}
.jac-shelf-name {
flex: none;
width: 210px;
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.jac-shelf-mini {
flex: 1;
display: flex;
height: 9px;
border-radius: 3px;
overflow: hidden;
background: var(--jac-line);
min-width: 80px;
}
.jac-shelf-mini span[data-kind="assumed"] { background: var(--jac-gold); opacity: 0.5; }
.jac-shelf-mini span[data-kind="mixed"] {
background: linear-gradient(90deg, var(--jac-human), var(--jac-agent));
}
.jac-shelf-mini span[data-kind="agent"] { background: var(--jac-agent); }
.jac-shelf-nums {
flex: none;
display: flex;
gap: 12px;
font-size: 11px;
color: var(--jac-muted);
min-width: 190px;
justify-content: flex-end;
}
.jac-shelf-nums b { color: var(--jac-human); font-weight: 500; }
.jac-shelf-nums i { color: var(--jac-agent); font-style: normal; }
.jac-shelf-failures {
margin-top: 16px;
font-size: 12px;
color: var(--jac-muted);
}
.jac-shelf-failures summary { cursor: pointer; }
.jac-shelf-failures ul { margin: 8px 0 0; padding-left: 18px; line-height: 1.7; }
.jac-shelf-empty {
margin-top: 20px;
padding: 18px;
border: 1px dashed var(--jac-line);
border-radius: 12px;
}
/* The keyboard cursor. A diff read by scroll wheel is a diff whose third
hunk never got looked at, so the cursor is the primary way to move. */
.jac-diffm-row[data-cursor="true"] td,
.jac-diffm-gap[data-cursor="true"] td {
background-image: linear-gradient(
90deg,
rgba(255, 255, 255, 0.07),
rgba(255, 255, 255, 0.02) 60%,
transparent
);
}
.jac-diffm-row[data-cursor="true"] .jac-diffm-ln:first-child {
box-shadow: inset 3px 0 0 var(--jac-accent);
color: var(--jac-accent);
opacity: 1;
}
.jac-diffm-gap[data-cursor="true"] button {
border-color: var(--jac-accent);
color: var(--jac-accent);
}
.jac-diffm-gap button kbd { margin-left: 8px; }
.jac-diffm-keys {
position: absolute;
inset: 0;
z-index: 5;
display: grid;
place-items: center;
background: rgba(3, 7, 12, 0.86);
backdrop-filter: blur(6px);
cursor: pointer;
}
.jac-diffm-keys dl {
display: grid;
grid-template-columns: auto 1fr;
gap: 10px 20px;
margin: 0;
padding: 26px 30px;
border: 1px solid var(--jac-line);
border-radius: 14px;
background: var(--jac-paper);
font-size: 12.5px;
}
.jac-diffm-keys dt {
display: flex;
gap: 5px;
justify-content: flex-end;
}
.jac-diffm-keys dd {
margin: 0;
color: var(--jac-muted);
line-height: 1.5;
}
/* The modal is the positioning context for the overlay. */
.jac-diffm { position: relative; }
/* ------------------------------------------------------- what is waiting --
* The front door.
*
* The floor used to open on Jackie, mid-sentence, before anyone had asked her
* anything. The first question is not "shall we look around" — it is "does
* anything need me", and most of the time the honest answer is no. So the
* queue leads, and Jackie appears when an interview starts.
*
* Rank is carried by the left mark rather than by colour alone: a decision is
* gold because only a human hand clears it, a remark is muted because it
* blocks nothing.
*/
.jac-await-list {
list-style: none;
margin: 22px 0 0;
padding: 0;
display: grid;
gap: 8px;
}
.jac-await-row {
display: flex;
gap: 13px;
padding: 14px 16px;
border: 1px solid var(--jac-line);
border-radius: 12px;
text-decoration: none;
color: var(--jac-ink);
}
.jac-await-row:hover {
border-color: var(--jac-accent);
background: rgba(255, 255, 255, 0.025);
}
.jac-await-mark {
flex: none;
width: 3px;
border-radius: 2px;
background: var(--jac-muted);
}
.jac-await-list li[data-kind="decision"] .jac-await-mark {
background: var(--jac-human);
}
.jac-await-main {
flex: 1;
min-width: 0;
}
.jac-await-title {
display: block;
font-size: 14px;
line-height: 1.45;
}
/* What would actually clear it — the only line that tells you what to do. */
.jac-await-asks {
display: block;
margin-top: 4px;
font-size: 12px;
color: var(--jac-gold);
}
.jac-await-list li[data-kind="remark"] .jac-await-asks {
color: var(--jac-muted);
}
.jac-await-quote {
display: block;
margin-top: 6px;
padding-left: 10px;
border-left: 2px solid var(--jac-line);
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 11.5px;
color: var(--jac-muted);
}
.jac-await-meta {
display: flex;
gap: 10px;
align-items: center;
flex-wrap: wrap;
margin-top: 8px;
font-size: 11px;
color: var(--jac-muted);
}
.jac-await-kind {
text-transform: uppercase;
letter-spacing: 0.09em;
font-size: 10px;
}
.jac-await-repos { margin-top: 34px; }
.jac-await-repo-row {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
gap: 12px;
margin-top: 12px;
}
.jac-await-repo {
display: grid;
gap: 3px;
padding: 14px 16px;
border: 1px solid var(--jac-line);
border-radius: 12px;
}
.jac-await-repo-name {
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 13px;
}
.jac-await-repo-nums { font-size: 11.5px; color: var(--jac-muted); }
.jac-await-repo-nums b { color: var(--jac-gold); font-weight: 500; }
.jac-await-repo-acts {
display: flex;
gap: 8px;
margin-top: 10px;
flex-wrap: wrap;
}
/* Jackie, once she has been asked for. */
.jac-interview-top {
display: flex;
align-items: center;
gap: 14px;
padding: 14px 20px;
flex-wrap: wrap;
}