/* TuringDNA · Engine — editorial design system. * * Reference register: Stripe Press × Nature Methods. Warm-paper canvas, * Playfair Display italic for display headlines, Source Serif 4 for body, * JetBrains Mono for chrome / labels / data identifiers. One archival-blue * accent. Hairline rules over heavy borders. Everything is intentional. * * Architecture: * - Layout: sidebar + top bar + content workspace (kept from prior system). * - Color: ink-on-paper neutrals + 1 archival-blue accent + 4 semantic colors. * - Typography: triple-stack — Playfair display / Source Serif body / JBM chrome. * - Depth: hairline rules + soft shadows (5 tiers, used sparingly). * - Motion: 120–280ms cubic-bezier easing, purposeful only. * * Token names kept from the previous Linear-style system so component * rules below pick up the new palette automatically. Only the values * underneath the semantic aliases were rewritten. */ @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Playfair+Display:ital,wght@0,400;0,600;0,700;1,400;1,700;1,900&family=Source+Serif+4:ital,opsz,wght@0,8..60,300;0,8..60,400;0,8..60,500;0,8..60,600;0,8..60,700;1,8..60,400&display=swap'); :root { /* ── Neutral scale — warm-paper ink, redirected from the old slate scale. gray-0 (was pure white) is now the warmest paper; gray-9 (was near-black) stays as ink. The progression in between is desaturated and warmed so every surface reads as "off-white paper" rather than "tinted slate". */ --gray-0: #FBFAF6; /* warm paper card */ --gray-1: #F7F5F0; /* warm paper canvas */ --gray-2: #EFECE5; /* paper soft */ --gray-3: #E5E2DC; /* hairline-soft */ --gray-4: #D6D3D1; /* hairline */ --gray-5: #B8B4AE; /* hairline-strong */ --gray-6: #8A857E; /* ink faint */ --gray-7: #6B6862; /* ink soft */ --gray-8: #2A2A29; /* ink */ --gray-9: #0A0A0A; /* ink strong */ /* ── Brand — archival blue, matching landing's single accent. Reading these in old rules: --brand replaces the old teal everywhere, so nav highlights, focus rings, primary buttons, and the AlphaFold / ESMFold spinner all shift to one consistent archival blue. */ --brand: #1E3A8A; --brand-deep: #1E40AF; --brand-light: #3151AB; --brand-50: #EEF1F8; --brand-100: #DCE2EF; --brand-200: #B7C2DE; --brand-ring: rgba(30, 58, 138, 0.18); /* ── Semantic — kept for functional chips (GC warnings, internal RE-site errors, success toasts). Toned down slightly so they don't shout on the warm canvas. */ --success: #047857; --success-light: #ECFDF5; --warning: #92400E; --warning-light: #FEF3C7; --danger: #991B1B; --danger-light: #FEF2F2; --info: var(--brand); --info-light: var(--brand-50); /* ── Surfaces — paper, not slate. */ --bg-app: var(--gray-1); --bg-card: var(--gray-0); --bg-sidebar: var(--gray-2); --bg-subtle: var(--gray-2); --bg-raised: var(--gray-0); --bg-hover: var(--gray-3); /* ── Ink — direct ports; gray-9 is ink-strong, gray-7/6 carry the secondary hierarchy. Source Serif 4 reads at slightly lower contrast than Inter, so --ink (body text) is the same value as before but visually warmer. */ --ink-strong: var(--gray-9); --ink: var(--gray-8); --ink-soft: var(--gray-7); --ink-faint: var(--gray-6); --ink-disabled: var(--gray-5); --line: var(--gray-3); --line-strong: var(--gray-4); --line-bold: var(--gray-5); /* ── Elevation — softer on warm paper. The old shadows used a cool slate blue (rgba(14,20,27)); the new ones use warm-paper ink so cards don't look like they're casting a fluorescent shadow on butcher paper. */ --elev-1: 0 1px 2px rgba(42, 42, 41, 0.04); --elev-2: 0 1px 3px rgba(42, 42, 41, 0.05), 0 1px 2px rgba(42, 42, 41, 0.03); --elev-3: 0 4px 12px rgba(42, 42, 41, 0.06), 0 2px 4px rgba(42, 42, 41, 0.03); --elev-4: 0 12px 28px rgba(42, 42, 41, 0.08), 0 4px 10px rgba(42, 42, 41, 0.04); --elev-5: 0 24px 48px rgba(42, 42, 41, 0.10), 0 6px 14px rgba(42, 42, 41, 0.05); /* ── Geometry — slightly smaller radii. Editorial pages don't use pill shapes; cards prefer a thin square edge. Values kept compatible. */ --r-1: 3px; --r-2: 4px; --r-3: 6px; --r-4: 10px; --r-5: 14px; /* ── Type — the triple stack. --font-ui is aliased to body serif so every existing rule that used Inter now gets Source Serif 4 automatically. --font-display is new, used by the editorial overlay below for h1/h2 + the brand wordmark. */ --font-ui: 'Source Serif 4', 'Source Serif Pro', Georgia, serif; --font-body: 'Source Serif 4', 'Source Serif Pro', Georgia, serif; --font-display: 'Playfair Display', 'EB Garamond', Georgia, serif; --font-mono: 'JetBrains Mono', 'IBM Plex Mono', ui-monospace, Menlo, monospace; /* Motion */ --ease: cubic-bezier(0.22, 1, 0.36, 1); --ease-snap: cubic-bezier(0.4, 0, 0.2, 1); --t-fast: 120ms; --t-base: 180ms; --t-slow: 280ms; /* Layout */ --sidebar-w: 232px; --topbar-h: 56px; } * { box-sizing: border-box; margin: 0; padding: 0; } html, body { background: var(--bg-app); color: var(--ink); font-family: var(--font-ui); font-size: 14px; line-height: 1.55; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11'; } body { min-height: 100vh; } a { color: inherit; text-decoration: none; } button, input, select, textarea { font-family: inherit; font-size: inherit; color: inherit; } button { cursor: pointer; border: none; background: none; } kbd { font-family: var(--font-mono); font-size: 11px; padding: 2px 5px; background: var(--gray-3); border: 1px solid var(--line-strong); border-radius: var(--r-1); color: var(--ink-soft); box-shadow: 0 1px 0 var(--line-strong); } ::selection { background: var(--brand-100); color: var(--brand-deep); } ::-webkit-scrollbar { width: 10px; height: 10px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: var(--gray-4); border-radius: 5px; border: 2px solid transparent; background-clip: padding-box; } ::-webkit-scrollbar-thumb:hover { background: var(--gray-5); background-clip: padding-box; } /* ====================================================================== APP */ .app { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh; } /* ================================================================== SIDEBAR */ .sidebar { position: sticky; top: 0; height: 100vh; background: var(--bg-sidebar); border-right: 1px solid var(--line); display: flex; flex-direction: column; padding: 16px 14px 18px; gap: 24px; } .brand { display: flex; align-items: center; gap: 10px; padding: 6px 8px; border-radius: var(--r-2); transition: background var(--t-fast) var(--ease); } .brand:hover { background: var(--bg-hover); } .brand-mark { width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; } .brand-mark img, .brand-mark svg { width: 100%; height: 100%; object-fit: contain; display: block; } .brand-word { font-weight: 700; font-size: 15px; letter-spacing: -0.01em; color: var(--ink-strong); } .nav { display: flex; flex-direction: column; gap: 2px; flex: 1; overflow-y: auto; } .nav-section { font-size: 10px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-faint); padding: 14px 10px 4px; } .nav-item { display: flex; align-items: center; gap: 10px; padding: 7px 10px; border-radius: var(--r-2); color: var(--ink-soft); font-size: 13px; font-weight: 500; transition: all var(--t-fast) var(--ease); position: relative; } .nav-icon { width: 14px; height: 14px; flex-shrink: 0; color: var(--ink-faint); } .nav-item:hover { background: var(--bg-hover); color: var(--ink); } .nav-item:hover .nav-icon { color: var(--ink-soft); } .nav-item.active { background: var(--brand-50); color: var(--brand-deep); font-weight: 600; } .nav-item.active .nav-icon { color: var(--brand); } .nav-tag { margin-left: auto; font-size: 11px; color: var(--ink-faint); background: var(--gray-3); padding: 1px 6px; border-radius: 999px; font-variant-numeric: tabular-nums; font-weight: 500; } .nav-item.active .nav-tag { background: var(--brand-100); color: var(--brand-deep); } .sidebar-footer { display: flex; flex-direction: column; gap: 8px; border-top: 1px solid var(--line); padding-top: 14px; } .model-card { background: var(--gray-0); border: 1px solid var(--line); border-radius: var(--r-3); padding: 10px 12px; } .model-card-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; } .model-card-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-faint); font-weight: 600; } .status-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--gray-5); display: inline-block; } .status-dot.status-on { background: var(--success); box-shadow: 0 0 0 3px rgba(4, 120, 87, 0.18); } .model-card-name { font-size: 13px; font-weight: 600; color: var(--ink-strong); margin-bottom: 1px; } .model-card-meta { font-family: var(--font-mono); font-size: 10px; color: var(--ink-faint); letter-spacing: 0.01em; } /* ==================================================================== MAIN */ .content { min-width: 0; display: flex; flex-direction: column; } .topbar { height: var(--topbar-h); /* Fully opaque so rows scrolling underneath don't show through. The * previous semi-transparent + blur combination caused visual bleeding * with the sticky table header positioned just below. */ background: var(--bg-app); border-bottom: 1px solid var(--line); padding: 0 32px; display: flex; align-items: center; justify-content: space-between; position: sticky; top: 0; z-index: 10; } .breadcrumb { display: flex; align-items: center; gap: 6px; font-size: 13px; } .crumb { color: var(--ink-faint); } .crumb-sep { color: var(--ink-disabled); } .crumb-active { color: var(--ink-strong); font-weight: 500; } .topbar-actions { display: flex; gap: 8px; align-items: center; } .cmd-trigger { display: flex; align-items: center; gap: 8px; padding: 6px 10px 6px 10px; border: 1px solid var(--line-strong); background: var(--gray-0); border-radius: var(--r-2); color: var(--ink-faint); font-size: 13px; box-shadow: var(--elev-1); transition: all var(--t-fast) var(--ease); } .cmd-trigger svg { width: 14px; height: 14px; color: var(--ink-faint); } .cmd-trigger:hover { border-color: var(--line-bold); color: var(--ink-soft); box-shadow: var(--elev-2); } .cmd-trigger span { color: var(--ink-soft); } .cmd-trigger kbd { margin-left: 16px; } /* =============================================================== WORKSPACE */ .workspace { flex: 1; padding: 28px 32px 40px; display: flex; flex-direction: column; gap: 20px; max-width: 1180px; width: 100%; } footer { padding: 20px 32px 24px; text-align: center; border-top: 1px solid var(--line); margin-top: auto; } .footer-note { font-size: 11px; color: var(--ink-faint); letter-spacing: 0.02em; } /* =================================================================== HERO */ .hero { background: radial-gradient(80% 120% at 100% 0%, var(--brand-50) 0%, transparent 55%), var(--bg-card); border: 1px solid var(--line); border-radius: var(--r-4); padding: 28px 32px; display: grid; grid-template-columns: 1fr auto; gap: 32px; align-items: end; box-shadow: var(--elev-1); } .eyebrow { display: inline-block; font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--brand); margin-bottom: 12px; } .hero h1 { font-size: 28px; font-weight: 700; letter-spacing: -0.02em; color: var(--ink-strong); margin-bottom: 8px; line-height: 1.15; } .hero-lede { font-size: 14px; color: var(--ink-soft); max-width: 56ch; line-height: 1.6; } .hero-pipeline { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } .pipe-step { display: flex; flex-direction: column; gap: 2px; padding: 8px 12px; border: 1px solid var(--line); border-radius: var(--r-2); background: var(--gray-0); min-width: 92px; } .pipe-num { font-family: var(--font-mono); font-size: 10px; color: var(--ink-faint); font-weight: 600; letter-spacing: 0.04em; } .pipe-label { font-size: 12px; color: var(--ink); font-weight: 500; white-space: nowrap; } .pipe-arrow { color: var(--ink-disabled); font-size: 14px; } @media (max-width: 980px) { .hero { grid-template-columns: 1fr; } .hero-pipeline { gap: 6px; } .pipe-step { min-width: 0; padding: 6px 9px; } .pipe-label { font-size: 11px; } } /* =================================================================== CARDS */ .card { background: var(--bg-card); border: 1px solid var(--line); border-radius: var(--r-4); padding: 24px 28px; box-shadow: var(--elev-1); } .card-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; margin-bottom: 20px; padding-bottom: 16px; border-bottom: 1px solid var(--line); } .card-header.results-header { padding-bottom: 20px; } .card-header-left { display: flex; gap: 14px; align-items: flex-start; flex: 1; min-width: 0; } .card-header h2 { font-size: 16px; font-weight: 600; letter-spacing: -0.005em; color: var(--ink-strong); margin-bottom: 2px; } .card-sub { font-size: 12px; color: var(--ink-faint); line-height: 1.5; } .step-num { width: 28px; height: 28px; flex-shrink: 0; border-radius: 50%; background: var(--gray-2); color: var(--ink-soft); font-size: 12px; font-weight: 600; display: inline-flex; align-items: center; justify-content: center; font-variant-numeric: tabular-nums; border: 1px solid var(--line); } .step-num.done { background: var(--success-light); color: var(--success); border-color: rgba(4, 120, 87, 0.25); } .step-num.done svg { width: 14px; height: 14px; } /* ==================================================================== TABS */ .input-tabs { display: flex; gap: 2px; margin-bottom: 18px; padding: 3px; background: var(--gray-2); border-radius: var(--r-3); width: fit-content; border: 1px solid var(--line); } .tab { display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border-radius: var(--r-2); font-size: 12px; font-weight: 500; color: var(--ink-soft); transition: all var(--t-fast) var(--ease); } .tab svg { width: 12px; height: 12px; color: currentColor; opacity: 0.8; } .tab:hover { color: var(--ink); } .tab.active { background: var(--gray-0); color: var(--ink-strong); box-shadow: var(--elev-1); font-weight: 600; } .tab-panel { display: none; } .tab-panel.active { display: block; } /* ================================================================ DROPZONE */ .dropzone { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; padding: 48px 32px; border: 1.5px dashed var(--line-strong); border-radius: var(--r-3); background: var(--gray-1); cursor: pointer; transition: all var(--t-base) var(--ease); text-align: center; } .dropzone:hover, .dropzone.drag { border-color: var(--brand); background: var(--brand-50); border-style: solid; } .dz-icon { width: 48px; height: 48px; border-radius: var(--r-3); background: var(--gray-0); border: 1px solid var(--line-strong); display: flex; align-items: center; justify-content: center; color: var(--ink-soft); box-shadow: var(--elev-1); transition: all var(--t-base) var(--ease); } .dz-icon svg { width: 22px; height: 22px; } .dropzone:hover .dz-icon, .dropzone.drag .dz-icon { color: var(--brand); border-color: var(--brand-200); } .dz-copy { display: flex; flex-direction: column; gap: 2px; } .dz-copy strong { font-size: 14px; font-weight: 600; color: var(--ink-strong); } .dz-copy span { font-size: 13px; color: var(--ink-soft); } .dz-formats { font-size: 11px; color: var(--ink-faint); margin-top: 6px; display: flex; flex-wrap: wrap; gap: 4px; justify-content: center; letter-spacing: 0.02em; } /* ================================================================== INPUTS */ textarea, input[type="text"], input[type="number"], select { width: 100%; padding: 8px 12px; background: var(--gray-0); border: 1px solid var(--line-strong); border-radius: var(--r-2); transition: all var(--t-fast) var(--ease); font-family: var(--font-mono); font-size: 13px; line-height: 1.5; color: var(--ink-strong); box-shadow: var(--elev-1); } textarea { resize: vertical; min-height: 160px; } select { font-family: var(--font-ui); cursor: pointer; appearance: none; background-image: url("data:image/svg+xml;utf8,"); background-repeat: no-repeat; background-position: right 12px center; background-size: 10px; padding-right: 32px; } input:focus, textarea:focus, select:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-ring); } /* ============================================================= PASTE SHELL */ .paste-shell { display: flex; border: 1px solid var(--line-strong); border-radius: var(--r-2); background: var(--gray-0); overflow: hidden; transition: all var(--t-fast) var(--ease); box-shadow: var(--elev-1); } .paste-shell:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-ring); } .paste-gutter { flex-shrink: 0; padding: 10px 10px 10px 14px; background: var(--gray-2); border-right: 1px solid var(--line); color: var(--ink-faint); font-family: var(--font-mono); font-size: 12px; line-height: 1.5; text-align: right; min-width: 46px; overflow: hidden; user-select: none; white-space: pre; font-variant-numeric: tabular-nums; } .paste-gutter span { display: block; height: calc(1.5em); } .paste-gutter span.error-line { color: var(--danger); font-weight: 600; background: var(--danger-light); } .paste-shell textarea { border: none !important; border-radius: 0; flex: 1; background: transparent; box-shadow: none !important; overflow: auto; white-space: pre; } .paste-actions { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 12px; } .paste-stats { font-size: 11px; color: var(--ink-faint); font-variant-numeric: tabular-nums; letter-spacing: 0.04em; text-transform: uppercase; font-weight: 500; } /* ============================================================== BUTTONS */ .primary { display: inline-flex; align-items: center; justify-content: center; gap: 10px; padding: 12px 22px; border-radius: var(--r-2); background: var(--ink-strong); color: var(--gray-0); font-weight: 600; font-size: 13px; letter-spacing: -0.005em; transition: all var(--t-base) var(--ease); border: 1px solid var(--ink-strong); box-shadow: var(--elev-1); } .primary:not(:disabled):hover { background: var(--gray-9); transform: translateY(-1px); box-shadow: var(--elev-3); } .primary:not(:disabled):active { transform: translateY(0); box-shadow: var(--elev-2); } .primary:disabled { background: var(--gray-3); color: var(--ink-disabled); border-color: var(--line-strong); cursor: not-allowed; box-shadow: none; } .primary-lg { width: 100%; padding: 16px 24px; font-size: 14px; background: linear-gradient(180deg, var(--brand) 0%, var(--brand-deep) 100%); border-color: var(--brand-deep); box-shadow: 0 1px 2px rgba(10, 95, 119, 0.20), inset 0 1px 0 rgba(255,255,255,0.16); } .primary-lg:not(:disabled):hover { background: linear-gradient(180deg, var(--brand-deep) 0%, #07485A 100%); box-shadow: 0 6px 16px rgba(14, 124, 154, 0.25), inset 0 1px 0 rgba(255,255,255,0.16); } .primary-lg:disabled { background: var(--gray-3); } .primary-icon { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: var(--r-1); background: rgba(255,255,255,0.18); } .primary-icon svg { width: 12px; height: 12px; } .primary:disabled .primary-icon { background: var(--gray-4); color: var(--ink-disabled); } .primary-content { display: flex; flex-direction: column; align-items: flex-start; gap: 1px; } .primary-label { font-weight: 600; } .primary-sub { font-size: 11px; opacity: 0.78; font-weight: 400; letter-spacing: 0; } .primary:disabled .primary-sub { opacity: 1; } .primary-ghost { padding: 8px 16px; border-radius: var(--r-2); border: 1px solid var(--line-strong); background: var(--gray-0); color: var(--ink); font-size: 13px; font-weight: 500; transition: all var(--t-fast) var(--ease); box-shadow: var(--elev-1); } .primary-ghost:hover { background: var(--gray-1); border-color: var(--line-bold); } .ghost { display: inline-flex; align-items: center; gap: 6px; padding: 7px 12px; border-radius: var(--r-2); border: 1px solid var(--line-strong); background: var(--gray-0); color: var(--ink-soft); font-size: 13px; font-weight: 500; transition: all var(--t-fast) var(--ease); box-shadow: var(--elev-1); } .ghost svg { width: 14px; height: 14px; color: currentColor; } .ghost:hover { border-color: var(--brand); color: var(--brand-deep); background: var(--brand-50); } .ghost-sm { padding: 5px 10px; font-size: 12px; } /* ============================================================= PREVIEW */ .preview { margin-top: 20px; padding: 18px 20px; border-radius: var(--r-3); background: var(--gray-1); border: 1px solid var(--line); } .preview-meta { display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 14px; } .preview-meta > div { display: flex; flex-direction: column; gap: 3px; } .meta-label { font-size: 10px; color: var(--ink-faint); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600; } .meta-val { font-size: 14px; font-weight: 600; color: var(--ink-strong); font-variant-numeric: tabular-nums; } .pill { padding: 2px 9px; border-radius: 999px; background: var(--brand-50); color: var(--brand-deep); font-size: 11px; font-weight: 600; width: fit-content; text-transform: capitalize; border: 1px solid var(--brand-100); } .preview-seq { font-family: var(--font-mono); font-size: 12px; color: var(--ink); word-break: break-all; padding: 10px 12px; background: var(--gray-0); border-radius: var(--r-2); border: 1px solid var(--line); letter-spacing: 0.02em; line-height: 1.6; } /* ============================================================ IDENTIFY */ .identify-row { margin-top: 14px; padding-top: 14px; border-top: 1px dashed var(--line-strong); display: flex; align-items: center; gap: 12px; flex-wrap: wrap; } .identify-btn { display: inline-flex; align-items: center; gap: 6px; padding: 7px 12px; border-radius: var(--r-2); border: 1px solid var(--line-strong); background: var(--gray-0); color: var(--ink); font-size: 12px; font-weight: 500; transition: all var(--t-fast) var(--ease); box-shadow: var(--elev-1); } .identify-btn svg { width: 13px; height: 13px; color: currentColor; } .identify-btn:hover { border-color: var(--brand); color: var(--brand-deep); background: var(--brand-50); } .identify-btn:disabled { cursor: progress; opacity: 0.8; } .identify-hint { font-size: 11px; color: var(--ink-faint); line-height: 1.5; } .identify-panel { margin-top: 14px; padding: 14px 16px; background: var(--gray-0); border: 1px solid var(--line); border-radius: var(--r-3); box-shadow: var(--elev-1); } .identify-panel[hidden] { display: none; } .identify-status { display: flex; align-items: center; gap: 10px; font-size: 12px; color: var(--ink-soft); } .identify-spinner { width: 14px; height: 14px; border: 2px solid var(--gray-3); border-top-color: var(--brand); border-radius: 50%; animation: spin 0.75s linear infinite; flex-shrink: 0; } @keyframes spin { to { transform: rotate(360deg); } } .identify-headline { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; padding-bottom: 10px; margin-bottom: 12px; border-bottom: 1px solid var(--line); } .identify-headline strong { font-size: 14px; color: var(--ink-strong); font-weight: 600; } .identify-headline .organism { font-size: 12px; color: var(--ink-soft); font-style: italic; } .identify-headline .id-pct { margin-left: auto; padding: 2px 9px; border-radius: 999px; background: var(--success-light); color: var(--success); font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums; border: 1px solid rgba(4, 120, 87, 0.22); } .identify-headline .id-pct.id-mid { background: var(--warning-light); color: var(--warning); border-color: #FDE68A; } .identify-headline .id-pct.id-low { background: var(--gray-2); color: var(--ink-soft); border-color: var(--line-strong); } .identify-hits { display: flex; flex-direction: column; gap: 8px; } .identify-hit { display: grid; grid-template-columns: auto 1fr auto; gap: 12px; padding: 9px 12px; border: 1px solid var(--line); border-radius: var(--r-2); background: var(--gray-1); font-size: 12px; } .identify-hit-acc { font-family: var(--font-mono); color: var(--brand-deep); font-weight: 600; font-size: 11px; } .identify-hit-desc { color: var(--ink); line-height: 1.5; } .identify-hit-org { color: var(--ink-soft); font-style: italic; } .identify-hit-stats { font-family: var(--font-mono); color: var(--ink-faint); font-size: 11px; text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; } .identify-actions { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; } .identify-error { color: var(--danger); font-size: 12px; line-height: 1.5; } /* AlphaFold structure viewer (Mol*). Shown below the BLAST hits when the top hit has a UniProt accession. Aspect-ratio'd box so the viewer canvas has a stable size regardless of structure dimensions. */ .alphafold-wrap { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); } .alphafold-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; gap: 12px; } .alphafold-head h4 { font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-soft); font-weight: 700; margin: 0; } .alphafold-meta { font-size: 11px; color: var(--ink-faint); font-family: var(--font-mono); } .alphafold-viewer { width: 100%; height: 360px; border-radius: var(--r-3); border: 1px solid var(--line); background: #0E141B; overflow: hidden; position: relative; } .alphafold-viewer .msp-plugin { background: #0E141B !important; } .alphafold-loading { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: var(--gray-3); font-size: 12px; gap: 10px; background: #0E141B; z-index: 2; } .alphafold-loading .identify-spinner { border-color: rgba(255,255,255,0.18); border-top-color: var(--brand-light); } .alphafold-disclaimer { margin-top: 8px; font-size: 11px; color: var(--ink-faint); line-height: 1.5; } .alphafold-disclaimer a { color: var(--brand-deep); text-decoration: underline; text-underline-offset: 2px; } /* ============================================================ CDS PICKER */ .cds-picker { margin-top: 20px; padding-top: 20px; border-top: 1px solid var(--line); } .cds-picker h3 { font-size: 14px; font-weight: 600; color: var(--ink-strong); margin-bottom: 4px; } .muted { color: var(--ink-faint); font-size: 12px; line-height: 1.5; margin-bottom: 12px; } .cds-list { display: flex; flex-direction: column; gap: 6px; } .cds-item { display: flex; align-items: center; gap: 14px; padding: 11px 16px; border-radius: var(--r-2); border: 1px solid var(--line); cursor: pointer; transition: all var(--t-fast) var(--ease); background: var(--gray-0); } .cds-item:hover { border-color: var(--line-bold); background: var(--gray-1); } .cds-item.selected { border-color: var(--brand); background: var(--brand-50); box-shadow: 0 0 0 1px var(--brand); } .cds-name { flex: 1; font-weight: 500; font-family: var(--font-mono); font-size: 13px; color: var(--ink-strong); } .cds-length { color: var(--ink-faint); font-size: 12px; font-variant-numeric: tabular-nums; } .cds-item.marker .cds-name { color: var(--ink-faint); } .cds-item.marker::after { content: 'marker'; font-size: 10px; font-weight: 600; color: var(--warning); padding: 2px 8px; background: var(--warning-light); border-radius: 999px; border: 1px solid #FDE68A; letter-spacing: 0.02em; } /* ============================================================ SETTINGS */ .setting-grid { /* 6-column grid so we can place 3 narrow number fields across row 1 (each spans 2) and 2 wider dropdown fields across row 2 (each spans 3). Result is a 3-up / 2-up arrangement where both rows sum to the full width — no orphan field on the second row. */ display: grid; grid-template-columns: repeat(6, 1fr); gap: 18px 22px; } .setting-grid > .setting:nth-child(1), .setting-grid > .setting:nth-child(2), .setting-grid > .setting:nth-child(3) { grid-column: span 2; } .setting-grid > .setting:nth-child(4), .setting-grid > .setting:nth-child(5) { grid-column: span 3; } @media (max-width: 880px) { /* On narrower viewports the 6-track grid leaves the dropdown labels (e.g. "Saccharomyces cerevisiae") truncated. Collapse to a single column — each label/input pair stacks cleanly, no orphan row. */ .setting-grid { grid-template-columns: 1fr; gap: 14px; } .setting-grid > .setting:nth-child(n) { grid-column: auto; } } .setting { display: flex; flex-direction: column; gap: 5px; } .setting-name { font-size: 13px; font-weight: 600; color: var(--ink-strong); } .setting-hint { font-size: 11px; color: var(--ink-faint); margin-bottom: 6px; line-height: 1.5; } /* ============================================================ PROGRESS */ .progress-shell { margin-top: 18px; display: flex; flex-direction: column; gap: 8px; } .progress-bar { height: 4px; background: var(--gray-3); border-radius: 999px; overflow: hidden; } .progress-fill { height: 100%; width: 0%; background: linear-gradient(90deg, var(--brand), var(--brand-light)); border-radius: 999px; transition: width 400ms var(--ease); box-shadow: 0 0 12px rgba(14, 124, 154, 0.4); } .progress-meta { display: flex; justify-content: space-between; font-size: 11px; color: var(--brand-deep); font-variant-numeric: tabular-nums; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; } .progress-message { font-size: 12px; color: var(--ink-soft); font-family: var(--font-mono); line-height: 1.5; } /* ============================================================ STATS STRIP */ .stats-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: var(--r-3); overflow: hidden; margin-bottom: 18px; } .stat-tile { background: var(--gray-0); padding: 14px 18px; display: flex; flex-direction: column; gap: 4px; } .stat-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.10em; color: var(--ink-faint); font-weight: 600; } .stat-val { font-size: 22px; font-weight: 700; color: var(--ink-strong); font-variant-numeric: tabular-nums; letter-spacing: -0.02em; line-height: 1.1; } .stat-sub { font-size: 11px; color: var(--ink-faint); font-variant-numeric: tabular-nums; } /* ============================================================ MUTATION MAP */ .mutmap-card { background: var(--gray-1); border: 1px solid var(--line); border-radius: var(--r-3); padding: 18px 20px 20px; margin-bottom: 18px; } .mutmap-head { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 14px; gap: 16px; } .mutmap-head h3 { font-size: 13px; font-weight: 600; color: var(--ink-strong); margin-bottom: 2px; } .mutmap-head .muted { margin: 0; font-size: 11px; } .mutmap-legend { display: flex; align-items: center; gap: 8px; font-size: 11px; color: var(--ink-faint); } .lg-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; } .lg-mild { background: var(--brand-200); } .lg-mid { background: var(--brand); } .lg-strong { background: var(--brand-deep); } .mutmap-canvas { background: var(--gray-0); border: 1px solid var(--line); border-radius: var(--r-2); padding: 16px 14px 10px; overflow-x: auto; } .mutmap-canvas svg { display: block; width: 100%; height: 140px; } .mutmap-axis line, .mutmap-axis path { stroke: var(--line-strong); stroke-width: 1; } .mutmap-axis text { font-family: var(--font-mono); font-size: 10px; fill: var(--ink-faint); } .mutmap-stem { stroke: var(--line-strong); stroke-width: 1; } .mutmap-dot { transition: r var(--t-fast) var(--ease), fill var(--t-fast) var(--ease); cursor: pointer; } .mutmap-dot:hover { stroke: var(--ink-strong); stroke-width: 1.5; } .mutmap-baseline { stroke: var(--line-strong); stroke-width: 1; } /* ============================================================== RESULTS */ /* ===== Filter toolbar above the variant table ============================ */ .table-toolbar { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; } .filter-input-wrap { flex: 1; position: relative; display: flex; align-items: center; } .filter-input-wrap svg { position: absolute; left: 11px; width: 14px; height: 14px; color: var(--ink-faint); pointer-events: none; } .filter-input-wrap input { padding-left: 34px; padding-right: 32px; font-family: var(--font-ui); font-size: 13px; } .filter-clear { position: absolute; right: 8px; width: 20px; height: 20px; border-radius: 50%; background: var(--gray-3); color: var(--ink-soft); font-size: 14px; line-height: 1; display: inline-flex; align-items: center; justify-content: center; transition: all var(--t-fast) var(--ease); } .filter-clear:hover { background: var(--gray-4); color: var(--ink); } .table-count { font-size: 12px; color: var(--ink-faint); font-variant-numeric: tabular-nums; white-space: nowrap; font-weight: 500; } /* ===== Run metadata pills + auto methods ================================= */ .run-meta { margin-bottom: 18px; padding: 14px 18px; background: var(--gray-1); border: 1px solid var(--line); border-radius: var(--r-3); display: flex; flex-direction: column; gap: 12px; } .run-meta-pills { display: flex; flex-wrap: wrap; gap: 6px; } .run-pill { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; border-radius: 999px; background: var(--gray-0); border: 1px solid var(--line-strong); font-size: 11px; color: var(--ink-soft); font-variant-numeric: tabular-nums; } .run-pill strong { color: var(--ink-strong); font-weight: 600; } .run-methods { background: var(--gray-0); border: 1px solid var(--line); border-radius: var(--r-2); padding: 12px 14px; font-size: 12px; color: var(--ink); line-height: 1.6; } .run-methods-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; } .run-methods-head h4 { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-faint); } .run-methods-body { font-family: var(--font-ui); } .run-methods-body code { font-family: var(--font-mono); font-size: 11px; background: var(--gray-2); padding: 1px 4px; border-radius: 3px; } .run-methods-body cite { font-style: italic; color: var(--ink-soft); } /* ===== BLOSUM62 mutation classification colors ========================== */ .mut-token { display: inline-block; padding: 1px 6px; border-radius: 4px; font-family: var(--font-mono); font-size: 11px; font-weight: 600; border: 1px solid; cursor: help; } .mut-token + .mut-token { margin-left: 4px; } .mut-cons { background: #ECFDF5; color: #047857; border-color: #A7F3D0; } /* BLOSUM62 ≥ 0 */ .mut-mod { background: #FFFBEB; color: #B45309; border-color: #FDE68A; } /* -2..-1 */ .mut-drastic { background: #FEF2F2; color: #B91C1C; border-color: #FECACA; } /* ≤ -3 */ /* ===== WT/mutant diff in the sequence block ============================= */ .sequence-block .pos-mut { background: var(--brand-50); color: var(--brand-deep); font-weight: 700; padding: 0 2px; border-radius: 3px; cursor: help; } .result-table-wrap { /* Self-contained scroll region — both axes scroll inside this wrapper * rather than at the page level. Sticky header anchors to the wrapper's * top, so no row ever hides behind the page-level topbar (and the very * first row, V0001, is always reachable without jumping past it). */ max-height: 72vh; overflow: auto; border: 1px solid var(--line); border-radius: var(--r-3); background: var(--gray-0); } .result-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 13px; } .result-table th { background: var(--gray-0); text-align: left; padding: 12px 14px; font-weight: 600; font-size: 11px; color: var(--ink-soft); border-bottom: 1px solid var(--line); white-space: nowrap; text-transform: uppercase; letter-spacing: 0.06em; position: sticky; top: 0; z-index: 5; user-select: none; } .result-table th.num { text-align: right; } .result-table th.sortable { cursor: pointer; } .result-table th.sortable:hover { color: var(--ink-strong); background: var(--gray-1); } .result-table th .sort-ind { display: inline-block; margin-left: 5px; color: var(--ink-disabled); font-size: 10px; transition: color var(--t-fast) var(--ease); } .result-table th.sort-active .sort-ind { color: var(--brand); } .result-table td { padding: 11px 14px; border-bottom: 1px solid var(--line); vertical-align: top; font-variant-numeric: tabular-nums; } .result-table tr:last-child > td { border-bottom: none; } .result-table tr:hover td { background: var(--gray-1); } .result-table tr.expandable td { cursor: pointer; } .result-table td.num { text-align: right; font-weight: 500; } .result-table td.mutations { font-family: var(--font-mono); font-size: 12px; color: var(--brand-deep); white-space: nowrap; font-weight: 500; } .result-table td.rank { color: var(--ink-faint); font-weight: 600; font-family: var(--font-mono); font-size: 11px; } .result-table th.expand-col { width: 92px; } /* Per-row action cell — holds the Fold button and the expand chevron side by side. Right-aligned so the chevron sits against the table edge, matching the layout this cell used to have when it held just the chevron. */ .row-actions { display: flex; align-items: center; justify-content: flex-end; gap: 6px; white-space: nowrap; } .fold-btn { /* Inherits .ghost-btn for color + border; tighten the box so it fits inline with the chevron at the table row height. */ padding: 3px 9px; font-size: 12px; line-height: 1.1; letter-spacing: 0.02em; } .expand-toggle { border: 1px solid var(--line-strong); background: var(--gray-0); color: var(--ink-soft); font-size: 13px; line-height: 1; padding: 3px 7px; border-radius: var(--r-1); transition: all var(--t-fast) var(--ease); box-shadow: var(--elev-1); } .expand-toggle:hover { border-color: var(--brand); color: var(--brand-deep); } .expand-toggle.open { transform: rotate(90deg); border-color: var(--brand); color: var(--brand-deep); background: var(--brand-50); } .detail-row td { background: var(--gray-1) !important; padding: 0 !important; border-bottom: 1px solid var(--line) !important; } .detail-panel { display: flex; flex-direction: column; gap: 20px; padding: 22px 24px; animation: fadeIn 200ms var(--ease); } @keyframes fadeIn { from { opacity: 0; transform: translateY(-2px); } to { opacity: 1; transform: translateY(0); } } .detail-section h4 { font-size: 11px; font-weight: 600; color: var(--ink-faint); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 10px; } .pcr-summary { display: flex; flex-wrap: wrap; gap: 28px; padding: 14px 18px; background: var(--gray-0); border: 1px solid var(--line); border-radius: var(--r-2); } .pcr-summary > div { display: flex; flex-direction: column; gap: 3px; } .pcr-summary .pcr-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-faint); font-weight: 600; } .pcr-summary .pcr-val { font-size: 14px; font-weight: 600; color: var(--ink-strong); font-variant-numeric: tabular-nums; } .primer-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 12px; } .primer-card { background: var(--gray-0); border: 1px solid var(--line); border-radius: var(--r-2); padding: 14px 16px; display: flex; flex-direction: column; gap: 10px; box-shadow: var(--elev-1); } .primer-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; } .primer-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-soft); font-weight: 700; } .primer-direction { font-family: var(--font-mono); font-size: 10px; color: var(--ink-faint); } .primer-seq { font-family: var(--font-mono); font-size: 13px; color: var(--ink-strong); background: var(--gray-1); padding: 10px 12px; border-radius: var(--r-1); border: 1px solid var(--line); word-break: break-all; line-height: 1.55; letter-spacing: 0.02em; } .primer-stats { display: flex; gap: 18px; font-size: 11px; color: var(--ink-soft); font-variant-numeric: tabular-nums; } .primer-stats span strong { color: var(--ink-strong); font-weight: 600; margin-left: 4px; } .primer-actions { display: flex; gap: 8px; } .copy-btn { flex: 1; padding: 7px 12px; border-radius: var(--r-1); border: 1px solid var(--line-strong); background: var(--gray-0); font-size: 12px; color: var(--ink-soft); font-weight: 500; transition: all var(--t-fast) var(--ease); box-shadow: var(--elev-1); } .copy-btn:hover { border-color: var(--brand); color: var(--brand-deep); } .copy-btn.copied { background: var(--brand-50); border-color: var(--brand); color: var(--brand-deep); } /* =================================================== CLONING CUSTOMIZER */ .cloning-section { gap: 14px; } .cloning-hint { font-size: 12px; color: var(--ink-soft); line-height: 1.55; margin: 4px 0 8px; max-width: 64ch; } .cloning-controls { display: flex; flex-wrap: wrap; gap: 12px; } .cloning-field { display: flex; flex-direction: column; gap: 5px; flex: 1; min-width: 240px; } .cloning-field-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-faint); font-weight: 600; } .cloning-field select { width: 100%; } .cloning-preview { margin-top: 4px; border: 1px solid var(--line); border-radius: var(--r-2); background: var(--gray-0); overflow: hidden; } .cloning-preview:empty { display: none; } .cloning-meta { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; padding: 10px 14px; background: var(--gray-1); border-bottom: 1px solid var(--line); font-size: 12px; color: var(--ink-soft); font-variant-numeric: tabular-nums; } .cloning-meta strong { color: var(--ink-strong); font-weight: 700; margin-right: 2px; } .cloning-legend { display: inline-flex; align-items: center; gap: 10px; margin-left: auto; font-size: 11px; color: var(--ink-faint); } .legend-swatch { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 3px; vertical-align: middle; } .legend-flank { background: #FEF3C7; border: 1px solid #FDE68A; } .legend-cds { background: var(--brand-50); border: 1px solid var(--brand-100); } .cloning-preview-block { font-family: var(--font-mono); font-size: 12px; color: var(--ink-strong); background: var(--gray-0); padding: 14px 16px; line-height: 1.65; white-space: pre; overflow: auto; max-height: 240px; letter-spacing: 0.04em; } .cloning-preview-block .sec-flank, .cloning-preview-block .sec-vector_5p, .cloning-preview-block .sec-vector_3p { background: #FEF3C7; color: #92400E; border-radius: 1px; } .cloning-preview-block .sec-cds { background: var(--brand-50); color: var(--brand-deep); border-radius: 1px; } .cloning-preview-block .sec-n_tag, .cloning-preview-block .sec-c_tag { background: #FCE7F3; /* pink-50: distinct from amber + brand */ color: #9D174D; border-radius: 1px; } .cloning-preview-block .sec-linker_n, .cloning-preview-block .sec-linker_c { background: #E0E7FF; /* indigo-50: linker / cleavage site */ color: #3730A3; border-radius: 1px; } /* ───── Designer — multi-step picker ──────────────────────────────────── */ .cloning-designer { display: flex; flex-direction: column; gap: 14px; margin-top: 4px; } .designer-controls { display: flex; flex-direction: column; gap: 12px; padding: 16px 18px; background: var(--gray-1); border: 1px solid var(--line); border-radius: var(--r-3); } .designer-step { display: flex; flex-direction: column; gap: 6px; } .designer-step-row { flex-direction: row; flex-wrap: wrap; gap: 12px; } .designer-step-row > div { flex: 1; min-width: 180px; display: flex; flex-direction: column; gap: 6px; } .designer-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-faint); font-weight: 600; } .designer-hint { font-size: 12px; color: var(--ink-soft); line-height: 1.55; padding-top: 2px; } .designer-hint strong { color: var(--ink-strong); font-weight: 600; } .designer-preview { display: flex; flex-direction: column; gap: 0; } .legend-vector { background: #FEF3C7; border: 1px solid #FDE68A; } .legend-tag { background: #FCE7F3; border: 1px solid #FBCFE8; } .legend-linker { background: #E0E7FF; border: 1px solid #C7D2FE; } .legend-cdsBrand { background: var(--brand-50); border: 1px solid var(--brand-100); } .cloning-legend { gap: 10px; flex-wrap: wrap; } .designer-warnings { padding: 12px 14px; background: var(--gray-0); border-top: 1px solid var(--line); font-size: 12px; line-height: 1.6; } .designer-warnings h5 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-faint); font-weight: 700; margin-bottom: 8px; } .designer-warnings ul { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 6px; } .designer-warnings li { padding: 8px 10px; border-radius: var(--r-1); border-left: 3px solid; } .designer-warnings .warn-high { background: #FEF2F2; border-color: var(--danger); color: #7F1D1D; } .designer-warnings .warn-medium { background: #FFFBEB; border-color: #D97706; color: #78350F; } .designer-warnings .warn-low { background: var(--gray-1); border-color: var(--gray-5); color: var(--ink-soft); } .designer-warnings.warn-pass { color: var(--success); font-weight: 600; padding: 10px 14px; } @media (max-width: 959px) { .designer-controls { padding: 14px 14px; gap: 10px; } .designer-step-row { flex-direction: column; gap: 10px; } .designer-step-row > div { min-width: 0; } } .cloning-notes, .cloning-sublabel { padding: 0 14px 12px; font-size: 12px; color: var(--ink-soft); line-height: 1.6; } .cloning-notes strong { color: var(--ink-strong); font-weight: 700; } .cloning-sublabel { color: var(--ink-faint); } @media (max-width: 959px) { .cloning-meta { padding: 9px 12px; font-size: 11px; gap: 10px; } .cloning-legend { width: 100%; margin-left: 0; } .cloning-preview-block { font-size: 11px; padding: 12px 12px; } .cloning-notes, .cloning-sublabel { padding: 0 12px 12px; font-size: 11px; } } /* ====================================== SYNTH ROW (per-row vendor buttons) */ .synth-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 14px; padding: 14px 16px; background: var(--gray-0); border: 1px solid var(--line); border-radius: var(--r-2); } .synth-row-label { font-size: 11px; color: var(--ink-faint); font-weight: 600; margin-right: 6px; text-transform: uppercase; letter-spacing: 0.08em; } .synth-row .synth-btn { flex: 1; min-width: 130px; max-width: 220px; display: inline-flex; align-items: center; justify-content: center; gap: 6px; background: linear-gradient(180deg, var(--brand) 0%, var(--brand-deep) 100%); border-color: var(--brand-deep); color: var(--gray-0); font-weight: 600; box-shadow: 0 1px 2px rgba(10, 95, 119, 0.22), inset 0 1px 0 rgba(255,255,255,0.18); } .synth-row .synth-btn:hover { background: linear-gradient(180deg, var(--brand-deep) 0%, #07485A 100%); transform: translateY(-1px); box-shadow: 0 4px 10px rgba(14, 124, 154, 0.30), inset 0 1px 0 rgba(255,255,255,0.18); } .synth-row .synth-btn.copied { background: var(--success); border-color: var(--success); color: var(--gray-0); } .synth-arrow { font-weight: 400; opacity: 0.85; display: inline-block; transition: transform var(--t-fast) var(--ease); } .synth-row .synth-btn:hover .synth-arrow { transform: translate(2px, -2px); opacity: 1; } .gc-warn { display: inline-block; padding: 1px 7px; border-radius: 999px; background: var(--warning-light); color: var(--warning); font-size: 10px; font-weight: 600; margin-left: 6px; border: 1px solid #FDE68A; letter-spacing: 0.02em; } .sequence-block { font-family: var(--font-mono); font-size: 12px; color: var(--ink-strong); background: var(--gray-0); border: 1px solid var(--line); border-radius: var(--r-2); padding: 14px 16px; line-height: 1.65; white-space: pre; overflow: auto; max-height: 280px; letter-spacing: 0.04em; } .seq-num { color: var(--ink-faint); font-weight: 500; user-select: none; } /* ----- WT pseudo-row in the variant table ----------------------------- */ .variant-wt-row td { background: var(--brand-50, #ECFEFA); border-top: 2px solid var(--brand, #0E7C9A) !important; } .variant-wt-row .rank-wt { display: inline-block; font-weight: 700; color: var(--brand-deep, #095568); letter-spacing: 0.06em; font-size: 11px; text-transform: uppercase; } .wt-badge { display: inline-block; padding: 3px 8px; border-radius: 999px; background: var(--brand, #0E7C9A); color: #FFF; font-size: 10.5px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; } .detail-panel-wt { border-left: 3px solid var(--brand, #0E7C9A); padding-left: 14px; } /* ----- Restriction-site warning state ----------------------------------- * * * A variant whose Restriction_Sites_Unresolved > 0 is one whose codon- * optimized DNA still contains a BsaI / BsmBI / NotI recognition site * that synonymous scrubbing couldn't break (typically because the * overlapping codons have no synonymous alternatives that don't * recreate the site). Golden Gate / Type IIS assemblies will cut the * insert at these positions. We surface this as: * - Red left border on the table row. * - Banner at the top of the detail panel naming the count + risk. * - Synthesize buttons styled as warnings; click intercepts with a * confirm() so a hurried click can't ship a broken insert. */ .variant-row-re-warn td { box-shadow: inset 3px 0 0 0 var(--danger, #DC2626); } .detail-panel-re-warn { border-left: 3px solid var(--danger, #DC2626); padding-left: 14px; } .re-warn-banner { margin-bottom: 16px; padding: 12px 14px; border: 1px solid var(--danger, #DC2626); background: #FEF2F2; color: #7F1D1D; border-radius: var(--r-2); font-size: 13px; line-height: 1.5; } .re-warn-banner strong { display: block; margin-bottom: 4px; font-family: var(--font-display, Georgia, serif); font-size: 15px; font-weight: 700; color: #7F1D1D; } .re-warn-banner p { margin: 0; } .synth-btn.synth-btn-warn, .copy-btn.synth-btn-warn { border-color: var(--danger, #DC2626); color: #7F1D1D; } .synth-btn.synth-btn-warn::after { content: ' ⚠'; margin-left: 4px; font-size: 12px; } /* ----- Manual DNA edit -------------------------------------------------- */ .dna-edit-section .dna-edit-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 8px; } .dna-edit-actions { display: flex; gap: 6px; flex-wrap: wrap; } .ghost-btn { appearance: none; -webkit-appearance: none; background: #FFF; border: 1px solid var(--line); border-radius: var(--r-2); color: var(--ink-strong); padding: 6px 12px; font-size: 12px; font-weight: 500; cursor: pointer; transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease), background var(--t-fast) var(--ease); font-family: inherit; } .ghost-btn:hover { border-color: var(--brand); color: var(--brand-deep); } .dna-edit-textarea { width: 100%; box-sizing: border-box; font-family: var(--font-mono); font-size: 12px; line-height: 1.6; letter-spacing: 0.05em; color: var(--ink-strong); background: #FFF; border: 1px solid var(--line); border-radius: var(--r-2); padding: 12px 14px; resize: vertical; min-height: 160px; word-break: break-all; white-space: pre-wrap; } .dna-edit-textarea:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px rgba(14, 124, 154, 0.12); } .dna-edit-stats { margin-top: 10px; display: flex; flex-direction: column; gap: 8px; } .dna-stat-pill { display: inline-block; padding: 3px 8px; border-radius: 999px; background: var(--gray-1); border: 1px solid var(--line); font-size: 11px; color: var(--ink-soft); margin-right: 4px; font-family: var(--font-mono); } .dna-edit-warnings { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; } .dna-edit-warnings li { padding: 6px 10px; border-radius: var(--r-2); border: 1px solid var(--line); font-size: 12px; line-height: 1.45; } .dna-edit-warnings .warn-high { background: #FEF2F2; border-color: var(--danger, #DC2626); color: #7F1D1D; } .dna-edit-warnings .warn-medium { background: #FFFBEB; border-color: #D97706; color: #78350F; } .dna-edit-warnings .warn-low { background: var(--gray-1); border-color: var(--gray-5); color: var(--ink-soft); } .dna-edit-ok { padding: 6px 10px; border-radius: var(--r-2); background: var(--brand-50, #ECFEFA); border: 1px solid var(--brand, #0E7C9A); color: var(--brand-deep, #095568); font-size: 12px; } /* Inline state line beneath the warnings — used for "Saving…" feedback * during the atomic /api/variants POST and for inline error messages * surfaced from the server when validation fails. .dna-edit-saving picks * up brand color to read as a progress signal, not an error. */ .dna-edit-extra { margin-top: 10px; font-family: var(--font-mono); font-size: 12px; color: var(--ink-soft); line-height: 1.5; } .dna-edit-extra .dna-edit-saving { display: inline-flex; align-items: center; gap: 8px; color: var(--brand-deep, #095568); } .dna-edit-extra .dna-edit-saving::before { content: ''; width: 10px; height: 10px; border: 1.5px solid var(--brand); border-top-color: transparent; border-radius: 50%; display: inline-block; animation: dna-edit-spin 0.9s linear infinite; } .dna-edit-extra .warn-high { color: #7F1D1D; } @keyframes dna-edit-spin { to { transform: rotate(360deg); } } /* Save button disables while a /api/variants POST is in flight or when * the current textarea contents fail high-severity validation. Visually * reads as "not now" rather than "broken". */ .ghost-btn.dna-edit-toggle:disabled, .ghost-btn.dna-edit-toggle[disabled] { opacity: 0.55; cursor: not-allowed; pointer-events: none; } /* ============================================================ DOWNLOAD MENU */ .download-menu { position: relative; display: inline-block; } .download-trigger .caret { margin-left: 4px; font-size: 10px; color: var(--ink-faint); transition: transform var(--t-fast) var(--ease); display: inline-block; } .download-trigger.open .caret { transform: rotate(180deg); color: var(--brand); } .download-menu-items { position: absolute; top: calc(100% + 6px); right: 0; min-width: 280px; background: var(--gray-0); border: 1px solid var(--line); border-radius: var(--r-3); box-shadow: var(--elev-4); padding: 6px; z-index: 50; animation: menuIn 140ms var(--ease); } @keyframes menuIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } } .download-item { display: flex; flex-direction: column; gap: 1px; padding: 9px 12px; border-radius: var(--r-2); color: var(--ink-strong); cursor: pointer; transition: background var(--t-fast) var(--ease); } .download-item:hover { background: var(--gray-2); } .dl-name { font-size: 13px; font-weight: 600; color: var(--ink-strong); } .dl-desc { font-size: 11px; color: var(--ink-faint); } /* ============================================================ VIEWS ROUTER */ .view { display: flex; flex-direction: column; gap: 20px; } .view[hidden] { display: none; } /* ============================================================ LIBRARY LIST */ .library-list { display: flex; flex-direction: column; gap: 8px; } .lib-row { display: grid; grid-template-columns: 1fr auto auto auto auto; gap: 16px; align-items: center; padding: 14px 16px; background: var(--gray-0); border: 1px solid var(--line); border-radius: var(--r-2); transition: all var(--t-fast) var(--ease); } .lib-row:hover { border-color: var(--line-bold); background: var(--gray-1); } .lib-row-id { display: flex; flex-direction: column; gap: 3px; min-width: 0; } .lib-row-name { font-weight: 600; color: var(--ink-strong); font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .lib-row-meta { font-size: 11px; color: var(--ink-faint); font-variant-numeric: tabular-nums; } .lib-row-stat { text-align: right; font-variant-numeric: tabular-nums; } .lib-row-stat-num { font-size: 14px; font-weight: 600; color: var(--ink-strong); } .lib-row-stat-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-faint); font-weight: 600; } .lib-row-actions { display: flex; gap: 6px; } .lib-row-actions .ghost { padding: 5px 10px; font-size: 12px; } .lib-row-actions .ghost-danger:hover { border-color: var(--danger); color: var(--danger); background: var(--danger-light); } /* ============================================================ HISTORY */ .history-timeline { display: flex; flex-direction: column; gap: 2px; padding-left: 8px; position: relative; } .history-timeline::before { content: ''; position: absolute; top: 12px; bottom: 12px; left: 10px; width: 1px; background: var(--line); } .hist-item { display: grid; grid-template-columns: 24px 1fr auto; gap: 14px; align-items: center; padding: 10px 14px 10px 8px; border-radius: var(--r-2); background: var(--gray-0); border: 1px solid var(--line); margin-left: 12px; position: relative; transition: all var(--t-fast) var(--ease); } .hist-item:hover { border-color: var(--line-bold); } .hist-item::before { content: ''; position: absolute; left: -16px; top: 50%; transform: translateY(-50%); width: 8px; height: 8px; border-radius: 50%; background: var(--brand); border: 2px solid var(--gray-0); box-shadow: 0 0 0 1px var(--line); } .hist-dot { width: 28px; height: 28px; border-radius: var(--r-2); background: var(--brand-50); color: var(--brand-deep); display: inline-flex; align-items: center; justify-content: center; } .hist-dot svg { width: 14px; height: 14px; } .hist-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; } .hist-title { font-size: 13px; font-weight: 500; color: var(--ink-strong); } .hist-meta { font-size: 11px; color: var(--ink-faint); font-variant-numeric: tabular-nums; } .hist-time { font-size: 11px; color: var(--ink-faint); font-variant-numeric: tabular-nums; white-space: nowrap; } /* ============================================================ DOCS */ .docs-card { max-width: 760px; } .docs-body { display: flex; flex-direction: column; gap: 28px; } .docs-body section { display: flex; flex-direction: column; gap: 10px; } .docs-body h3 { font-size: 15px; font-weight: 600; color: var(--ink-strong); letter-spacing: -0.005em; border-bottom: 1px solid var(--line); padding-bottom: 6px; margin-bottom: 4px; } .docs-body p { font-size: 13px; line-height: 1.65; color: var(--ink); } .docs-list { padding-left: 22px; font-size: 13px; line-height: 1.65; color: var(--ink); } .docs-list li + li { margin-top: 6px; } .docs-list code, .docs-body code { font-family: var(--font-mono); font-size: 12px; background: var(--gray-2); padding: 1px 6px; border-radius: var(--r-1); color: var(--ink-strong); } .docs-callout { margin-top: 6px; padding: 12px 14px; background: var(--brand-50); border: 1px solid var(--brand-100); border-radius: var(--r-2); font-size: 12px; color: var(--brand-deep); line-height: 1.6; } .docs-table { border-collapse: collapse; font-size: 13px; width: 100%; max-width: 480px; } .docs-table th, .docs-table td { text-align: left; padding: 7px 12px; border-bottom: 1px solid var(--line); } .docs-table th { background: var(--gray-1); font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-soft); font-weight: 600; } .docs-citation { font-size: 12px !important; color: var(--ink-soft) !important; line-height: 1.6; padding-left: 14px; border-left: 2px solid var(--line-strong); } .docs-citation cite { font-style: italic; color: var(--ink); } /* ======================================================= EMPTY STATE */ .empty-state { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 56px 32px 64px; background: var(--bg-card); border: 1px dashed var(--line-strong); border-radius: var(--r-4); gap: 18px; } .empty-state[hidden] { display: none; } .empty-art { width: 220px; max-width: 100%; margin-bottom: 8px; } .empty-art img, .empty-art .empty-art-fallback { width: 100%; height: auto; display: block; } .empty-title { font-size: 17px; font-weight: 600; color: var(--ink-strong); letter-spacing: -0.01em; } .empty-sub { font-size: 13px; color: var(--ink-soft); max-width: 52ch; line-height: 1.6; } .empty-sub strong { color: var(--ink-strong); font-weight: 600; } /* ============================================================== ERROR */ .error-banner { background: var(--danger-light); border: 1px solid #FECACA; border-left: 3px solid var(--danger); color: var(--danger); padding: 14px 18px; border-radius: var(--r-2); margin-top: 16px; font-size: 13px; line-height: 1.55; } .error-banner strong { display: block; margin-bottom: 4px; font-weight: 700; } .error-banner > div { margin-top: 6px; color: var(--ink-soft); } .error-banner code { font-family: var(--font-mono); background: rgba(185, 28, 28, 0.10); padding: 1px 6px; border-radius: var(--r-1); font-size: 12px; color: var(--danger); font-weight: 500; } .error-hint { margin-top: 10px !important; padding-top: 10px; border-top: 1px dashed #FECACA; color: var(--ink-soft); font-size: 12px; line-height: 1.55; } /* ============================================================== MODAL (Twist) */ .modal { position: fixed; inset: 0; z-index: 200; display: flex; align-items: center; justify-content: center; padding: 24px; } .modal[hidden] { display: none; } .modal-backdrop { position: absolute; inset: 0; background: rgba(14, 20, 27, 0.45); backdrop-filter: blur(2px); animation: backdropIn 180ms var(--ease); } @keyframes backdropIn { from { opacity: 0; } to { opacity: 1; } } .modal-panel { position: relative; background: var(--gray-0); border: 1px solid var(--line); border-radius: var(--r-4); box-shadow: var(--elev-5); max-width: 640px; width: 100%; max-height: calc(100vh - 48px); display: flex; flex-direction: column; animation: panelIn 220ms var(--ease); } @keyframes panelIn { from { opacity: 0; transform: translateY(8px) scale(0.985); } to { opacity: 1; transform: translateY(0) scale(1); } } .modal-header { display: flex; justify-content: space-between; align-items: flex-start; padding: 18px 22px 14px; border-bottom: 1px solid var(--line); gap: 16px; } .modal-header h2 { font-size: 16px; font-weight: 600; color: var(--ink-strong); margin-bottom: 2px; letter-spacing: -0.005em; } .modal-sub { font-size: 12px; color: var(--ink-faint); } .modal-close { width: 30px; height: 30px; border-radius: var(--r-2); background: transparent; color: var(--ink-faint); font-size: 22px; line-height: 1; transition: all var(--t-fast) var(--ease); flex-shrink: 0; } .modal-close:hover { background: var(--gray-2); color: var(--ink); } .modal-body { padding: 18px 22px 22px; overflow-y: auto; } .modal-loading { display: flex; align-items: center; gap: 12px; padding: 20px 0; color: var(--ink-soft); font-size: 13px; } /* ===== Twist quote display ============================================ */ .quote-hero { background: linear-gradient(180deg, var(--brand-50) 0%, var(--gray-0) 100%); border: 1px solid var(--brand-100); border-radius: var(--r-3); padding: 14px 18px 16px; margin-bottom: 16px; } .quote-hero-row { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 10px; } .quote-hero-numbers { display: grid; grid-template-columns: 1fr auto 1fr; gap: 14px; align-items: center; } .quote-hero-num { display: flex; flex-direction: column; gap: 2px; } .quote-hero-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-faint); font-weight: 600; } .quote-hero-val { font-size: 30px; font-weight: 700; color: var(--ink-strong); font-variant-numeric: tabular-nums; letter-spacing: -0.02em; line-height: 1.1; } .quote-hero-unit { font-size: 16px; font-weight: 500; color: var(--ink-soft); letter-spacing: 0; margin-left: 2px; } .quote-hero-divider { width: 1px; height: 36px; background: var(--line); } .quote-headline { display: grid; grid-template-columns: 1fr auto; gap: 12px; padding-bottom: 14px; border-bottom: 1px solid var(--line); margin-bottom: 16px; } .quote-id { font-family: var(--font-mono); font-size: 11px; color: var(--ink-faint); text-transform: uppercase; letter-spacing: 0.06em; } .quote-total { font-size: 22px; font-weight: 700; color: var(--ink-strong); font-variant-numeric: tabular-nums; letter-spacing: -0.02em; } .quote-total-label { font-size: 11px; color: var(--ink-faint); text-transform: uppercase; letter-spacing: 0.06em; } .quote-status-pill { align-self: start; padding: 4px 10px; border-radius: 999px; font-size: 11px; font-weight: 600; border: 1px solid; background: var(--success-light); color: var(--success); border-color: rgba(4, 120, 87, 0.22); } .quote-status-pill.review { background: var(--warning-light); color: var(--warning); border-color: #FDE68A; } .quote-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: var(--r-2); overflow: hidden; margin-bottom: 16px; } .quote-tile { background: var(--gray-0); padding: 12px 14px; display: flex; flex-direction: column; gap: 3px; } .quote-tile .label { font-size: 10px; color: var(--ink-faint); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600; } .quote-tile .val { font-size: 14px; font-weight: 600; color: var(--ink-strong); font-variant-numeric: tabular-nums; } .quote-lineitems { border: 1px solid var(--line); border-radius: var(--r-2); overflow: hidden; margin-bottom: 16px; } .qli { display: grid; grid-template-columns: 1fr auto auto; gap: 12px; padding: 10px 14px; border-bottom: 1px solid var(--line); align-items: center; } .qli:last-child { border-bottom: none; } .qli-name { font-family: var(--font-mono); font-size: 12px; color: var(--ink-strong); font-weight: 600; } .qli-meta { font-size: 11px; color: var(--ink-faint); font-variant-numeric: tabular-nums; } .qli-price { font-weight: 600; color: var(--ink-strong); font-variant-numeric: tabular-nums; } .complexity-row { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; } .complexity-flag { display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; background: var(--warning-light); color: var(--warning); border: 1px solid #FDE68A; } .complexity-flag.biosec { background: var(--danger-light); color: var(--danger); border-color: #FECACA; } .quote-actions { display: flex; gap: 8px; justify-content: flex-end; } /* ===== Twist order form =============================================== */ .order-form { display: flex; flex-direction: column; gap: 14px; } .order-form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; } .order-form-grid .field-full { grid-column: 1 / -1; } .order-field { display: flex; flex-direction: column; gap: 4px; } .order-field label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-faint); font-weight: 600; } .order-field input { font-family: var(--font-ui); font-size: 13px; } .order-terms { display: flex; gap: 8px; align-items: flex-start; padding: 10px 12px; background: var(--gray-1); border: 1px solid var(--line); border-radius: var(--r-2); font-size: 12px; color: var(--ink-soft); line-height: 1.5; } .order-terms input { width: 16px; height: 16px; margin-top: 2px; box-shadow: none; } /* ===== Order confirmation =============================================== */ .order-confirm { text-align: center; padding: 12px 8px; } .order-confirm .checkmark { width: 56px; height: 56px; margin: 8px auto 14px; border-radius: 50%; background: var(--success-light); color: var(--success); display: flex; align-items: center; justify-content: center; border: 1px solid rgba(4,120,87,0.22); } .order-confirm .checkmark svg { width: 28px; height: 28px; } .order-confirm h3 { font-size: 18px; font-weight: 600; color: var(--ink-strong); margin-bottom: 8px; } .order-confirm .order-id { font-family: var(--font-mono); font-size: 14px; font-weight: 700; color: var(--brand-deep); background: var(--brand-50); padding: 4px 12px; border-radius: var(--r-2); display: inline-block; margin-bottom: 14px; } .order-confirm .order-detail { font-size: 13px; color: var(--ink-soft); line-height: 1.6; margin-bottom: 14px; } /* ============================================================= TOAST */ .toast { position: fixed; top: 0; left: 50%; transform: translate(-50%, -100%); z-index: 1000; background: var(--ink-strong); color: var(--gray-0); padding: 12px 22px; border-radius: 999px; margin-top: 20px; font-size: 13px; font-weight: 500; box-shadow: var(--elev-5); display: flex; align-items: center; gap: 12px; max-width: calc(100vw - 64px); opacity: 0; transition: transform 280ms var(--ease), opacity 200ms var(--ease); pointer-events: none; } .toast.toast-in { transform: translate(-50%, 0); opacity: 1; } .toast.toast-out { transform: translate(-50%, -120%); opacity: 0; } .toast-check { width: 20px; height: 20px; border-radius: 50%; background: var(--brand); color: var(--gray-0); display: inline-flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; flex-shrink: 0; } .toast-msg { line-height: 1.4; } .toast-warn { background: #78350F; } .toast-warn .toast-check { background: var(--warning); } /* Error toasts get the most aggressive treatment — red border + saturated * background — because they're shown when the clipboard write fails, which * is one of our highest-stakes user errors: failing to copy DNA before * opening a vendor's order form means whatever was previously on the * clipboard gets pasted instead. The user MUST see this. */ .toast-error { background: #7F1D1D; border: 1px solid #DC2626; box-shadow: 0 6px 24px rgba(220, 38, 38, 0.32); } .toast-error .toast-check { background: var(--danger, #DC2626); content: '!'; } /* =========================================================== RESPONSIVE * Mobile-first stack: * < 960 px (phone + small tablet) : sidebar becomes a slide-out drawer * triggered from a hamburger button in * the topbar; topbar collapses; cards, * stats strip, modals, tables all tighten. * ≥ 960 px (desktop) : full layout above this block. */ /* Hamburger button — surfaced only on phone/tablet. */ .nav-toggle { display: none; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: var(--r-2); color: var(--ink-soft); border: 1px solid var(--line-strong); background: var(--gray-0); box-shadow: var(--elev-1); margin-right: 10px; -webkit-tap-highlight-color: transparent; transition: all var(--t-fast) var(--ease); } .nav-toggle:hover, .nav-toggle:active { border-color: var(--brand); color: var(--brand-deep); background: var(--brand-50); } .nav-toggle svg { width: 18px; height: 18px; } /* Backdrop that covers the workspace when the drawer is open. */ .sidebar-scrim { position: fixed; inset: 0; background: rgba(14, 20, 27, 0.45); z-index: 49; opacity: 0; transition: opacity var(--t-base) var(--ease); pointer-events: none; } .sidebar-scrim[hidden] { display: block; } /* keep box for transition */ body.sidebar-open .sidebar-scrim { opacity: 1; pointer-events: auto; } @media (max-width: 959px) { .app { grid-template-columns: 1fr; } /* Sidebar = slide-in drawer from the left. Fixed positioning so it overlays the workspace instead of pushing content. */ .sidebar { position: fixed; top: 0; left: 0; bottom: 0; height: 100vh; height: 100dvh; width: 80vw; max-width: 280px; z-index: 50; transform: translateX(-100%); transition: transform var(--t-slow) var(--ease); box-shadow: var(--elev-4); padding-top: max(16px, env(safe-area-inset-top)); padding-bottom: max(18px, env(safe-area-inset-bottom)); padding-left: max(14px, env(safe-area-inset-left)); flex-direction: column; overflow-y: auto; -webkit-overflow-scrolling: touch; } body.sidebar-open .sidebar { transform: translateX(0); } .nav-toggle { display: inline-flex; flex-shrink: 0; } .topbar { padding: 0 14px; background: var(--bg-app); backdrop-filter: none; -webkit-backdrop-filter: none; } .breadcrumb { font-size: 12px; min-width: 0; overflow: hidden; flex: 1; } .crumb { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .topbar-actions { margin-left: auto; flex-shrink: 0; } /* Search trigger: icon only on phones — label + ⌘K kbd hidden. */ .cmd-trigger { padding: 7px 10px; min-width: 40px; min-height: 40px; } .cmd-trigger .cmd-trigger-text, .cmd-trigger kbd { display: none; } .workspace { padding: 18px 14px 28px; max-width: 100%; } .card { padding: 18px 16px; border-radius: var(--r-3); } .card-header { gap: 10px; margin-bottom: 14px; padding-bottom: 12px; } .card-header h2 { font-size: 15px; } .card-sub { font-size: 11px; } .step-num { width: 26px; height: 26px; font-size: 11px; } .hero { grid-template-columns: 1fr; padding: 20px 18px; gap: 14px; } .hero h1 { font-size: 22px; line-height: 1.18; } .hero-lede { font-size: 13px; } .hero-pipeline { display: none; } /* Stats strip: 2-column on phone instead of 5+ slivers. */ .stats-strip { grid-template-columns: repeat(2, 1fr); } .stat-tile { padding: 12px 14px; } .stat-val { font-size: 19px; } .stat-sub { font-size: 10px; } /* Input tabs: horizontal scroll if they don't fit. */ .input-tabs { max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; } .input-tabs::-webkit-scrollbar { display: none; } .tab { flex-shrink: 0; } .dropzone { padding: 28px 18px; gap: 10px; } .dz-icon { width: 38px; height: 38px; } .dz-icon svg { width: 18px; height: 18px; } .dz-copy strong { font-size: 13px; } .dz-copy span { font-size: 12px; } .dz-formats { font-size: 10px; gap: 3px; } .paste-shell { font-size: 12px; } .paste-gutter { min-width: 36px; padding: 9px 8px 9px 10px; font-size: 11px; } .paste-shell textarea { font-size: 12px; } .paste-actions { flex-direction: column; align-items: stretch; gap: 8px; } .paste-stats { text-align: center; } .preview { padding: 14px 16px; } .preview-meta { gap: 14px; } .setting-grid { grid-template-columns: 1fr; gap: 14px; } .primary-lg { padding: 14px 18px; font-size: 13px; min-height: 50px; } .card-header.results-header { flex-wrap: wrap; } .results-actions { width: 100%; justify-content: flex-end; flex-wrap: wrap; } .table-toolbar { flex-direction: column; align-items: stretch; gap: 8px; } .table-count { text-align: right; } .result-table-wrap { max-height: 60vh; max-height: 60dvh; } .result-table th, .result-table td { padding: 8px 10px; font-size: 12px; } .result-table td.mutations { font-size: 11px; } .detail-panel { padding: 16px 14px; gap: 16px; } .pcr-summary { gap: 14px; padding: 12px 14px; } .primer-grid { grid-template-columns: 1fr; } .primer-actions { flex-direction: column; } .synth-row { padding: 12px 12px; } .synth-row .synth-btn { max-width: 100%; } .mutmap-card { padding: 14px 14px 16px; } .mutmap-head { flex-direction: column; align-items: flex-start; gap: 8px; } .mutmap-canvas svg { height: 160px; } .run-meta { padding: 12px 14px; } .run-meta-pills { gap: 5px; } .run-methods { padding: 11px 13px; font-size: 11px; } /* Twist gateway modal: bottom sheet on phones. */ .modal { padding: 0; align-items: flex-end; } .modal-panel { width: 100%; max-width: 100%; max-height: 92vh; max-height: 92dvh; border-radius: var(--r-4) var(--r-4) 0 0; padding-bottom: env(safe-area-inset-bottom); animation: panelInMobile 240ms var(--ease); } @keyframes panelInMobile { from { transform: translateY(12px); opacity: 0; } to { transform: translateY(0); opacity: 1; } } .modal-header { padding: 16px 18px 12px; } .modal-body { padding: 14px 18px 22px; } .quote-hero-numbers { grid-template-columns: 1fr; gap: 12px; } .quote-hero-divider { display: none; } .quote-hero-val { font-size: 24px; } .quote-grid { grid-template-columns: 1fr 1fr; } .order-form-grid { grid-template-columns: 1fr; } .lib-row { grid-template-columns: 1fr; gap: 8px; } .lib-row-actions { justify-content: flex-end; } .hist-item { grid-template-columns: auto 1fr; } .hist-time { grid-column: 2 / -1; } .empty-art { width: 160px; } .empty-title { font-size: 15px; } .empty-sub { font-size: 12px; } .identify-row { flex-direction: column; align-items: stretch; gap: 8px; } .identify-btn { justify-content: center; } .identify-hit { grid-template-columns: auto 1fr; gap: 8px; } .identify-hit-stats { grid-column: 1 / -1; text-align: left; font-size: 10px; } .alphafold-viewer { height: 280px; height: 32dvh; } /* Toast at the bottom on phones — fits better with thumb position. */ .toast { top: auto; bottom: 18px; margin-top: 0; margin-bottom: env(safe-area-inset-bottom); max-width: calc(100vw - 32px); transform: translate(-50%, 120%); } .toast.toast-in { transform: translate(-50%, 0); } .toast.toast-out { transform: translate(-50%, 120%); } } /* Larger phones / small tablets — give more breathing room. */ @media (min-width: 540px) and (max-width: 959px) { .workspace { padding: 22px 22px 36px; } .card { padding: 22px 22px; } .hero h1 { font-size: 26px; } .stats-strip { grid-template-columns: repeat(3, 1fr); } .quote-grid { grid-template-columns: repeat(4, 1fr); } .order-form-grid { grid-template-columns: 1fr 1fr; } } /* ════════════════════════════════════════════════════════════════════════ * EDITORIAL OVERLAY * ----------------------------------------------------------------------- * Final-pass overrides that shift specific components from "tool app * chrome" to "Stripe Press × Nature Methods editorial". Loaded last so * they win the cascade over the prior rules without us having to delete * anything functional. Touches typography, the brand wordmark, primary + * ghost buttons, step numerals, panel cards, hero, modals, table headers. * Tokens already shifted at :root — this section adds the typographic and * structural flourishes that the palette change alone can't carry. * ════════════════════════════════════════════════════════════════════════ */ /* ── Body type at slightly larger size; Source Serif at 14 px is technically readable but visually thin. 15 px gives it the editorial weight the landing page has. Numeric data stays mono so the table doesn't lose its grid. */ html, body { font-size: 15px; line-height: 1.6; font-feature-settings: normal; } body { font-family: var(--font-body); } /* ── Display headlines: Playfair Display italic. Hero / workspace title / modal title / card titles all read as opening sentences of an article. */ h1, .hero h1, .hero-title, .workspace-title, .modal-header h2 { font-family: var(--font-display); font-style: italic; font-weight: 700; letter-spacing: -0.01em; color: var(--ink-strong); } h2, .card-title, .results-title, .panel-title { font-family: var(--font-display); font-weight: 600; letter-spacing: -0.005em; color: var(--ink-strong); } h3, h4 { font-family: var(--font-display); font-weight: 600; color: var(--ink-strong); } .hero p, .card-sub, .panel-sub, .modal-sub { font-family: var(--font-body); color: var(--ink-soft); } /* ── Brand wordmark — "TuringDNA" in the sidebar reads as a publication masthead, not a SaaS logo. Italic Playfair sets the tone for the rest of the app the instant a user looks at the sidebar. */ .brand-word { font-family: var(--font-display); font-style: italic; font-weight: 700; font-size: 17px; letter-spacing: 0.005em; color: var(--ink-strong); } /* ── Step numerals — replace the circled "1 / 2 / 3" pucks with mono section numerals in the "§ 1" style used on the landing. Done state keeps its archival-blue tick. The .step-num CSS box width still wraps in the layout — we keep the geometry, change the rendering. */ .step-num { background: transparent; border: none; border-radius: 0; color: var(--ink-faint); font-family: var(--font-mono); font-size: 13px; font-weight: 500; letter-spacing: 0.04em; width: auto; min-width: 28px; padding: 0 4px; } .step-num::before { content: '§ '; color: var(--ink-faint); opacity: 0.75; font-weight: 400; } .step-num.done { background: transparent; border-color: transparent; color: var(--brand); } .step-num.done::before { color: var(--brand); opacity: 0.85; } .step-num.done svg { display: none; } /* ── Sidebar — paper-soft, no shadow, hairline divider. */ .sidebar { border-right: 1px solid var(--line); box-shadow: none; } .nav-section { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.16em; color: var(--ink-faint); } .nav-item { font-family: var(--font-body); font-size: 14px; font-weight: 400; color: var(--ink-soft); border-radius: var(--r-1); } .nav-item.active { background: transparent; color: var(--brand); font-weight: 600; border-left: 2px solid var(--brand); border-radius: 0; padding-left: 8px; } .nav-item.active .nav-tag { background: var(--brand-50); color: var(--brand); } .model-card { background: var(--bg-card); border: 1px solid var(--line); border-radius: var(--r-2); } .model-card-label { font-family: var(--font-mono); letter-spacing: 0.14em; } .model-card-meta { font-family: var(--font-mono); } /* ── Topbar — flat paper, mono breadcrumb tone. */ .topbar { background: var(--bg-app); border-bottom: 1px solid var(--line); } .breadcrumb { font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.04em; } /* ── Cards: thin hairline, no heavy shadow. The old design used --elev-3 cards which read as boxed UI panels; editorial pages prefer a single hairline rule. Keep elev-1 for slight separation. */ .card, .results-card, .design-card { background: var(--bg-card); border: 1px solid var(--line); border-radius: var(--r-3); box-shadow: var(--elev-1); } /* ── Primary CTA — archival-blue rectangle with no gradient. Subtle shadow only. Replaces the old "ink-black filled button" treatment so it reads as the editorial accent rather than a generic Stripe button. */ .primary { background: var(--brand); border-color: var(--brand); color: #FFFFFF; border-radius: var(--r-2); box-shadow: 0 1px 2px rgba(30, 58, 138, 0.18); font-family: var(--font-body); font-weight: 600; letter-spacing: 0; text-transform: none; } .primary:not(:disabled):hover { background: var(--brand-deep); border-color: var(--brand-deep); box-shadow: 0 4px 12px rgba(30, 58, 138, 0.22); } .primary-lg { background: var(--brand); border-color: var(--brand); box-shadow: 0 1px 2px rgba(30, 58, 138, 0.18); } .primary-lg:not(:disabled):hover { background: var(--brand-deep); box-shadow: 0 6px 18px rgba(30, 58, 138, 0.24); } .primary-icon { background: rgba(255,255,255,0.16); } /* ── Ghost / secondary — keep the thin border but tighten typography and make it feel like a text-link with a frame. */ .ghost, .primary-ghost, .ghost-btn, .copy-btn { background: var(--bg-card); border: 1px solid var(--line-strong); border-radius: var(--r-2); font-family: var(--font-body); font-weight: 500; color: var(--ink); } .ghost:hover, .primary-ghost:hover, .ghost-btn:hover, .copy-btn:hover { border-color: var(--brand); color: var(--brand); background: var(--bg-card); } /* ── Fold button on each variant row — a touch more presence than other ghosts, since it triggers a network call to ESMFold. Archival-blue text on hover, mono tracking so it sits next to the chevron cleanly. */ .fold-btn { font-family: var(--font-mono); font-size: 11px; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-soft); } .fold-btn:hover { color: var(--brand); border-color: var(--brand); background: var(--brand-50); } /* ── Tabs — flatten the pill-segment look to a hairline row. */ .input-tabs { background: transparent; border: none; border-bottom: 1px solid var(--line); border-radius: 0; padding: 0; gap: 18px; } .tab { background: transparent; border-radius: 0; border-bottom: 2px solid transparent; padding: 8px 2px; font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-faint); } .tab:hover { color: var(--ink); background: transparent; } .tab.active { background: transparent; color: var(--ink-strong); border-bottom-color: var(--ink-strong); box-shadow: none; } /* ── Dropzone — thin border, paper background. */ .dropzone { border-width: 1px; border-color: var(--line-strong); background: var(--bg-card); } .dropzone:hover, .dropzone.drag { border-color: var(--brand); background: var(--brand-50); } .dz-icon { background: var(--bg-app); border: 1px solid var(--line); box-shadow: none; } /* ── Results table — header row in mono small-caps, identifier column in mono. Numbers stay tabular. Hairline borders. */ .result-table thead th { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-soft); font-weight: 500; } .result-table td { font-family: var(--font-body); } .result-table td.rank, .result-table td.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; } .result-table tbody tr.expandable:hover { background: var(--brand-50); } .expand-toggle { background: transparent; border-color: var(--line); color: var(--ink-faint); box-shadow: none; } .expand-toggle:hover { color: var(--brand); border-color: var(--brand); } .expand-toggle.open { background: var(--brand-50); color: var(--brand); border-color: var(--brand); } /* ── Modal — paper panel with hairline header. Matches the landing's chapter aesthetic; the Fold modal + Twist modal both pick this up. */ .modal-panel { background: var(--bg-card); border: 1px solid var(--line); border-radius: var(--r-4); box-shadow: var(--elev-4); } .modal-header { border-bottom: 1px solid var(--line); } .modal-header h2 { font-family: var(--font-display); font-style: italic; font-weight: 700; letter-spacing: -0.01em; } .modal-sub { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-faint); } .modal-close { background: transparent; color: var(--ink-faint); } .modal-close:hover { color: var(--ink); background: transparent; } /* ── Mol* / structure viewer disclaimers — stay editorial, mono kicker. */ .alphafold-head h4 { font-family: var(--font-display); font-style: italic; font-weight: 700; } .alphafold-meta { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-faint); } .alphafold-disclaimer { font-family: var(--font-body); color: var(--ink-soft); } .alphafold-disclaimer a { color: var(--brand); text-decoration: underline; text-underline-offset: 3px; } /* ── Footer note — mono colophon. */ .footer-note { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.10em; text-transform: uppercase; color: var(--ink-faint); } /* ── Selection — archival blue on paper. */ ::selection { background: var(--brand); color: var(--bg-card); } /* ── Fold consent panel — paper card that overlays the dark Mol* viewer slot before the first fold of a session. Editorial chapter anatomy: mono kicker, italic Playfair headline, hairline rule, body in Source Serif, archival-blue primary CTA. Once the user accepts, this panel is replaced by the loading spinner and then Mol*. */ .fold-consent { position: absolute; inset: 0; background: var(--bg-card); display: flex; flex-direction: column; justify-content: center; padding: 28px 36px; text-align: left; overflow-y: auto; } .fold-consent-kicker { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-faint); margin: 0 0 8px; } .fold-consent-title { font-family: var(--font-display); font-style: italic; font-weight: 700; font-size: clamp(1.4rem, 3.5vw, 1.9rem); line-height: 1.15; letter-spacing: -0.01em; color: var(--ink-strong); margin: 0 0 14px; max-width: 22ch; } .fold-consent-title em { font-weight: 900; font-style: italic; } .fold-consent-body { font-family: var(--font-body); font-size: 14px; line-height: 1.55; color: var(--ink-soft); margin: 0 0 10px; max-width: 56ch; } .fold-consent-body code { font-family: var(--font-mono); font-size: 0.92em; color: var(--ink); background: var(--bg-subtle); padding: 1px 5px; border-radius: var(--r-1); } .fold-consent-actions { display: flex; gap: 10px; margin-top: 18px; flex-wrap: wrap; align-items: center; } @media (max-width: 540px) { .fold-consent { padding: 20px 22px; } .fold-consent-body { font-size: 13px; } .fold-consent-actions { gap: 8px; } .fold-consent-actions .primary, .fold-consent-actions .ghost-btn { width: 100%; } }