@@ -1362,6 +1362,7 @@ class ApiHandler(BaseHTTPRequestHandler):
|
||||
item=row_json(raw); meta=adapter_meta.get(item.get("source_code") or item.get("kind"), {})
|
||||
try: config=json.loads(raw["config_json"] or "{}")
|
||||
except (TypeError,ValueError): config={}
|
||||
if not meta and config.get("provider"): meta=adapter_meta.get(str(config.get("provider")).lower(), {})
|
||||
try: policy=json.loads(raw["policy_json"] or "{}")
|
||||
except (TypeError,ValueError): policy={}
|
||||
credential_exists = bool(db.execute("SELECT 1 FROM source_credentials WHERE source_id=? AND organization_id=? AND provider=? AND key_name=?", (raw["id"], org, "google_places", "api_key")).fetchone())
|
||||
@@ -1402,7 +1403,7 @@ class ApiHandler(BaseHTTPRequestHandler):
|
||||
name=str(payload.get('name','')).strip(); requested=str(payload.get('source_code') or payload.get('kind','')).strip().lower(); config=payload.get('config',{})
|
||||
provider_kinds={'openstreetmap','wikidata','common_crawl'}
|
||||
kind='approved_directory' if requested in provider_kinds else requested
|
||||
source_code=requested
|
||||
source_code=str(config.get('provider','')).strip().lower() if requested == 'approved_directory' and str(config.get('provider','')).strip().lower() in {'openstreetmap','wikidata','common_crawl'} else requested
|
||||
if not name or requested not in ('csv','manual','google_places','bing_local','approved_directory','openstreetmap','wikidata','common_crawl','public_website','permitted_social','ct_logs','dns','rdap') or not isinstance(config,dict):return self.send_json(400,{"error":"invalid_source"})
|
||||
if contains_secret(config):return self.send_json(400,{"error":"secret_not_permitted"})
|
||||
try:
|
||||
|
||||
@@ -418,4 +418,4 @@ def available_adapters() -> list[dict[str, object]]:
|
||||
"available": bool(getattr(cls, "available", False)),
|
||||
"optional": bool(getattr(cls, "optional", False)),
|
||||
"requires_credentials": bool(getattr(cls, "requires_credentials", False))}
|
||||
for cls in ADAPTERS.values()]
|
||||
for cls in ADAPTERS.values() if cls.source_code != "approved_directory"]
|
||||
|
||||
@@ -12,8 +12,9 @@ class SourceAdapterTests(unittest.TestCase):
|
||||
for code in ('bing_local', 'permitted_social'):
|
||||
self.assertFalse(catalog[code]['available'], code)
|
||||
self.assertTrue(catalog[code]['optional'], code)
|
||||
self.assertTrue(catalog['approved_directory']['available'])
|
||||
self.assertFalse(catalog['approved_directory']['optional'])
|
||||
for code in ('openstreetmap','wikidata','common_crawl'):
|
||||
self.assertTrue(catalog[code]['available'], code)
|
||||
self.assertFalse(catalog[code]['optional'], code)
|
||||
self.assertTrue(catalog['google_places']['available'])
|
||||
self.assertTrue(catalog['google_places']['optional'])
|
||||
|
||||
|
||||
+1
-1
@@ -330,7 +330,7 @@
|
||||
$('interactionForm').addEventListener('submit',saveInteraction);$('suppressionForm').addEventListener('submit',addSuppression);$('aiProviderForm').addEventListener('submit',saveAiProviderSettings);$('aiProvider').addEventListener('change',updateAiProviderForm);$('aiProviderRefreshBtn').addEventListener('click',loadAiProviderSettings);$('testAiProviderBtn').addEventListener('click',testAiProviderConnection);$('crmRefreshBtn').addEventListener('click',()=>{loadCrmPipeline();loadInteractions(selectedId);});$('reportsRefreshBtn').addEventListener('click',loadReports);$('suppressionRefreshBtn').addEventListener('click',loadSuppressions);$('pipelineViewToggle').addEventListener('click',()=>{crmListMode=!crmListMode;$('pipelineViewToggle').textContent=crmListMode?'▦ Board view':'☷ List view';$('pipelineViewToggle').setAttribute('aria-pressed',String(crmListMode));renderPipeline();});$('pipelineBoard').addEventListener('click',e=>{const selectButton=e.target.closest?.('[data-crm-select]');if(selectButton){selectedId=Number(selectButton.dataset.crmSelect);selectProspect(selectedId);loadInteractions(selectedId);$('crmActivity').scrollIntoView({behavior:'smooth',block:'start'});}const stageButton=e.target.closest?.('[data-crm-save-stage]');if(stageButton)saveCrmStage(stageButton.dataset.crmSaveStage);if(e.target.id==='retryCrmBtn')loadCrmPipeline();if(e.target.id==='retryInteractionsBtn'&&selectedId)loadInteractions(selectedId);});$('suppressionState').addEventListener('change',e=>{const input=e.target.closest?.('[data-suppression-id]');if(input){if(input.checked)selectedSuppressionIds.add(String(input.dataset.suppressionId));else selectedSuppressionIds.delete(String(input.dataset.suppressionId));updateSuppressionSelection();}});$('suppressionState').addEventListener('click',e=>{const button=e.target.closest?.('[data-remove-suppression]');if(button)removeSuppression(button.dataset.removeSuppression);if(e.target.id==='retrySuppressionsBtn')loadSuppressions();});$('selectAllSuppressions').addEventListener('change',e=>{suppressions.forEach(item=>e.target.checked?selectedSuppressionIds.add(String(item.id)):selectedSuppressionIds.delete(String(item.id)));renderSuppressions();});$('bulkReviewSuppressionsBtn').addEventListener('click',bulkReviewSuppressions);$('discoveryWorkspace').addEventListener('click',e=>{const action=e.target.closest?.('[data-run-action]');if(action){const run=discoveryRuns.find(item=>String(item.id)===String(selectedDiscoveryRunId));discoveryAction(action.dataset.runAction,run);}});
|
||||
$('aiProviderForm').addEventListener('submit',saveAiProviderSettings);$('aiProvider').addEventListener('change',updateAiProviderForm);$('aiProviderRefreshBtn').addEventListener('click',loadAiProviderSettings);$('testAiProviderBtn').addEventListener('click',testAiProviderConnection);$('crmRefreshBtn').addEventListener('click',()=>{loadCrmPipeline();loadInteractions(selectedId);});$('reportsRefreshBtn').addEventListener('click',loadReports);$('suppressionRefreshBtn').addEventListener('click',loadSuppressions);$('pipelineViewToggle').addEventListener('click',()=>{crmListMode=!crmListMode;$('pipelineViewToggle').textContent=crmListMode?'▦ Board view':'☷ List view';$('pipelineViewToggle').setAttribute('aria-pressed',String(crmListMode));renderPipeline();});$('pipelineBoard').addEventListener('click',e=>{const selectButton=e.target.closest?.('[data-crm-select]');if(selectButton){selectedId=Number(selectButton.dataset.crmSelect);selectProspect(selectedId);loadInteractions(selectedId);$('crmActivity').scrollIntoView({behavior:'smooth',block:'start'});}const stageButton=e.target.closest?.('[data-crm-save-stage]');if(stageButton)saveCrmStage(stageButton.dataset.crmSaveStage);if(e.target.id==='retryCrmBtn')loadCrmPipeline();if(e.target.id==='retryInteractionsBtn'&&selectedId)loadInteractions(selectedId);});$('suppressionState').addEventListener('change',e=>{const input=e.target.closest?.('[data-suppression-id]');if(input){if(input.checked)selectedSuppressionIds.add(String(input.dataset.suppressionId));else selectedSuppressionIds.delete(String(input.dataset.suppressionId));updateSuppressionSelection();}});$('suppressionState').addEventListener('click',e=>{const button=e.target.closest?.('[data-remove-suppression]');if(button)removeSuppression(button.dataset.removeSuppression);if(e.target.id==='retrySuppressionsBtn')loadSuppressions();});$('selectAllSuppressions').addEventListener('change',e=>{suppressions.forEach(item=>e.target.checked?selectedSuppressionIds.add(String(item.id)):selectedSuppressionIds.delete(String(item.id)));renderSuppressions();});$('bulkReviewSuppressionsBtn').addEventListener('click',bulkReviewSuppressions);$('outreachPolicyRefreshBtn').addEventListener('click',loadProviderPolicy);
|
||||
const viewMap = {dashboard:'dashboard', explorer:'prospects', add:'add', discoveryWorkspace:'discovery', jobs:'jobs', sources:'sources', crmPipeline:'crm', crmActivity:'crm', crmReports:'crm', suppressionCenter:'settings', outreachSettings:'settings', aiProviderSettings:'settings', scoreRules:'dashboard'};
|
||||
function activateView(view) { document.body.dataset.view = view; document.querySelectorAll('.content > section').forEach(section => section.classList.toggle('view-active', viewMap[section.id] === view)); window.scrollTo({top:0, behavior:'smooth'}); }
|
||||
function activateView(view) { document.body.dataset.view = view; document.querySelectorAll('.content > section').forEach(section => section.classList.toggle('view-active', viewMap[section.id] === view)); const labels = {dashboard:'Dashboard', prospects:'Prospect explorer', add:'Add prospects', discovery:'Discovery runs', jobs:'Jobs', sources:'Sources', crm:'CRM pipeline', settings:'Settings'}; const page = document.getElementById('topbarPage'); if (page) page.textContent = labels[view] || 'Workspace'; window.scrollTo({top:0, behavior:'smooth'}); }
|
||||
document.body.dataset.view = 'dashboard';
|
||||
const sourceSetup = document.querySelector('.source-config-panel');
|
||||
if (sourceSetup) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"version": "phase-26",
|
||||
"version": "phase-27",
|
||||
"entrypoints": [
|
||||
"config.js",
|
||||
"app.js",
|
||||
@@ -13,10 +13,10 @@
|
||||
"healthz"
|
||||
],
|
||||
"integrity": {
|
||||
"config.js": "sha256-036e9b1a2d16cf89ffc1cb23f22aab71608b1330e8323d2d42a6c6b86051698c",
|
||||
"app.js": "sha256-44809719afe72cd0ddc30819b3c73f36019319bfbda4e59d62055fd040b5c6c0",
|
||||
"styles.css": "sha256-a9fd194b001c0de98775bcf5fbba5da676ccfdd607a889eae84bd30a54c15e08",
|
||||
"index.html": "sha256-f48882419575ac49c389f07ba25791115981330a1dd623ce3c21b026a0dedd3c",
|
||||
"config.js": "sha256-95293871c068b38098cead4af5de4b17ee505a0b051b9379567a716dc5364490",
|
||||
"app.js": "sha256-3bd0215c904f7a8b0ebce3b96c799944880a3ce123694e3680db4516b690e490",
|
||||
"styles.css": "sha256-ba90290ab11e82a6b2639dfd70d1e74502c1cacb2b26cf1db92b45beb67ac03f",
|
||||
"index.html": "sha256-f5d2ab03e5c676f8af8a9bfd201e6d4fb3bfb19ba10098e93eae5680122fa2cc",
|
||||
"health.html": "sha256-c352a6f37aa24628cfc8d5709a70ff2d94181d192ed5fe00916cca9378a61d81",
|
||||
"error.html": "sha256-f3cc28d2dfc9e8af112d0257c6ee47b9e9146e5da8590a754dd589bf43702aaf",
|
||||
"healthz": "sha256-dc51b8c96c2d745df3bd5590d990230a482fd247123599548e0632fdbf97fc22"
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/* Public, non-secret runtime configuration. Replace this file at deploy time if needed. */
|
||||
window.__PROSPECT_CONFIG__ = Object.freeze({
|
||||
apiBase: '',
|
||||
assetVersion: 'phase-26'
|
||||
assetVersion: 'phase-27'
|
||||
});
|
||||
|
||||
+24
-16
@@ -5,7 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>ProspectOS · Pipeline intelligence</title>
|
||||
<meta name="description" content="Prospect discovery and review dashboard">
|
||||
<link rel="stylesheet" href="styles.css?v=phase-26">
|
||||
<link rel="stylesheet" href="styles.css?v=phase-27">
|
||||
</head>
|
||||
<body>
|
||||
<section class="login-screen" id="loginScreen" aria-labelledby="loginTitle">
|
||||
@@ -27,23 +27,31 @@
|
||||
<aside class="sidebar">
|
||||
<a class="brand" href="#top" aria-label="ProspectOS home"><span class="brand-mark">✦</span><span>Prospect<span class="brand-light">OS</span></span></a>
|
||||
<nav aria-label="Primary navigation">
|
||||
<a class="nav-item active" href="#dashboard"><span>▦</span> Dashboard</a>
|
||||
<a class="nav-item" href="#explorer"><span>⌕</span> Prospect explorer</a>
|
||||
<a class="nav-item" href="#add"><span>+</span> Add prospects</a>
|
||||
<a class="nav-item" href="#discoveryWorkspace" data-nav="discovery"><span>⌁</span> Discovery runs</a>
|
||||
<a class="nav-item" href="#jobs" data-nav="jobs"><span>◷</span> Jobs</a>
|
||||
<a class="nav-item" href="#sources" data-nav="sources"><span>⌁</span> Sources</a>
|
||||
<a class="nav-item" href="#crmPipeline" data-nav="crm"><span>◫</span> CRM pipeline</a>
|
||||
<a class="nav-item" href="#crmReports" data-nav="reports"><span>▤</span> Reports</a>
|
||||
<a class="nav-item" href="#suppressionCenter" data-nav="suppression"><span>⊘</span> Suppressions</a>
|
||||
<a class="nav-item" href="#outreachSettings" data-nav="outreach-settings"><span>⚙</span> Outreach policy</a>
|
||||
<a class="nav-item" href="#aiProviderSettings" data-nav="ai-provider-settings"><span>✧</span> AI providers</a>
|
||||
<a class="nav-item" href="#scoreRules" data-nav="score-rules"><span>◈</span> Score rules</a>
|
||||
<div class="nav-group"><p class="nav-label">Workspace</p>
|
||||
<a class="nav-item active" href="#dashboard"><span>▦</span> Dashboard</a>
|
||||
<a class="nav-item" href="#explorer"><span>⌕</span> Prospect explorer</a>
|
||||
<a class="nav-item" href="#add"><span>+</span> Add prospects</a>
|
||||
</div>
|
||||
<div class="nav-group"><p class="nav-label">Discovery</p>
|
||||
<a class="nav-item" href="#discoveryWorkspace" data-nav="discovery"><span>⌁</span> Discovery runs</a>
|
||||
<a class="nav-item" href="#jobs" data-nav="jobs"><span>◷</span> Jobs</a>
|
||||
<a class="nav-item" href="#sources" data-nav="sources"><span>⌁</span> Sources</a>
|
||||
</div>
|
||||
<div class="nav-group"><p class="nav-label">Pipeline</p>
|
||||
<a class="nav-item" href="#crmPipeline" data-nav="crm"><span>◫</span> CRM pipeline</a>
|
||||
<a class="nav-item" href="#crmReports" data-nav="reports"><span>▤</span> Reports</a>
|
||||
</div>
|
||||
<div class="nav-group"><p class="nav-label">Controls</p>
|
||||
<a class="nav-item" href="#suppressionCenter" data-nav="suppression"><span>⊘</span> Suppressions</a>
|
||||
<a class="nav-item" href="#outreachSettings" data-nav="outreach-settings"><span>⚙</span> Outreach policy</a>
|
||||
<a class="nav-item" href="#aiProviderSettings" data-nav="ai-provider-settings"><span>✧</span> AI providers</a>
|
||||
<a class="nav-item" href="#scoreRules" data-nav="score-rules"><span>◈</span> Score rules</a>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="sidebar-foot"><div class="live-dot"></div><div><strong>Workspace live</strong><small>Data sync is healthy</small></div></div>
|
||||
</aside>
|
||||
<main class="main" id="top">
|
||||
<header class="topbar"><button class="mobile-menu" id="menuBtn" aria-label="Toggle navigation">☰</button><div class="crumb">Workspace <span>/</span> Growth pipeline</div><div class="top-actions"><span class="api-status" id="apiStatus">● Connecting…</span><span class="user-identity" id="userIdentity"></span><button class="icon-button" aria-label="Notifications">♢</button><button class="logout-button" id="logoutBtn" type="button">Log out</button><div class="avatar" id="userAvatar">?</div></div></header>
|
||||
<header class="topbar"><button class="mobile-menu" id="menuBtn" aria-label="Toggle navigation">☰</button><div class="crumb"><strong>Growth workspace</strong><span>/</span><span id="topbarPage">Dashboard</span></div><div class="top-actions"><span class="api-status" id="apiStatus">● Connecting…</span><span class="user-identity" id="userIdentity"></span><button class="icon-button" aria-label="Notifications">♢</button><button class="logout-button" id="logoutBtn" type="button">Log out</button><div class="avatar" id="userAvatar">?</div></div></header>
|
||||
<div class="content">
|
||||
<section class="hero" id="dashboard"><div><p class="eyebrow">EVIDENCE-LED PROSPECTING</p><h1>Good morning, <span id="userGreetingName">there</span> <span>✦</span></h1><p class="hero-sub">Your pipeline has <strong id="heroCount">0 prospects</strong> ready for review.</p></div><button class="button primary" data-scroll="#add">+ Add prospect</button></section>
|
||||
<section class="direct-discovery-section" id="discoveryWorkspace" aria-labelledby="discoveryWorkspaceTitle" data-smoke="direct-discovery">
|
||||
@@ -130,7 +138,7 @@
|
||||
<div class="merge-dialog" id="mergeDialog" hidden role="dialog" aria-modal="true" aria-labelledby="mergeDialogTitle">
|
||||
<div class="merge-dialog-card"><div class="panel-heading"><div><p class="eyebrow">REVIEW REQUIRED</p><h2 id="mergeDialogTitle">Confirm merge</h2></div><button class="icon-button" id="cancelMergeBtn" type="button" aria-label="Close merge confirmation">×</button></div><p id="mergeDialogCopy"></p><div class="merge-warning"><strong>This action is reversible.</strong> The merge will be recorded in history and can be reversed later.</div><p id="mergeDialogMessage" class="form-message" role="alert" aria-live="polite"></p><div class="merge-dialog-actions"><button class="button ghost" id="cancelMergeBtnSecondary" type="button">Cancel</button><button class="button primary" id="confirmMergeBtn" type="button">Confirm merge</button></div></div>
|
||||
</div>
|
||||
<script src="config.js?v=phase-26"></script>
|
||||
<script src="app.js?v=phase-26"></script>
|
||||
<script src="config.js?v=phase-27"></script>
|
||||
<script src="app.js?v=phase-27"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -347,3 +347,59 @@ body[data-view="settings"] #crmReports { display: none; }
|
||||
.discovery-controls { display:flex; align-items:center; flex-wrap:wrap; gap:8px; margin-bottom:14px; padding-bottom:12px; border-bottom:1px solid var(--line); }.discovery-progress { padding:15px 0; }.discovery-stat-grid { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:8px; margin:0 0 14px; }.discovery-stat-grid > div { padding:11px; border:1px solid var(--line); border-radius:9px; background:#f8fbfa; }.discovery-stat-grid small { display:block; color:var(--muted); font-size:10px; text-transform:uppercase; letter-spacing:.06em; }.discovery-stat-grid strong { display:block; margin-top:3px; color:var(--ink-strong); font-size:20px; }.discovery-two-col { display:grid; grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr); gap:14px; margin-top:16px; }.live-log { min-width:0; }.live-log h4, .source-health h4 { margin:0 0 10px; }.live-log ol { max-height:240px; overflow:auto; display:grid; gap:11px; margin:0; padding:12px; border:1px solid var(--line); border-radius:9px; background:#f8fbfa; list-style:none; }.live-log li { display:flex; gap:9px; font-size:12px; }.live-log small { display:block; color:var(--muted); font-size:10px; }.source-health { padding:13px; border:1px solid #c8e2de; border-radius:10px; background:var(--teal-soft); }.subheading { display:flex; justify-content:space-between; gap:10px; align-items:center; }.source-health-grid { display:grid; gap:8px; }.source-health-grid article { display:grid; gap:3px; padding:10px; border:1px solid #c8e2de; border-radius:8px; background:#fff; }.source-health-grid small { color:var(--muted); font-size:10px; }.source-health-status { width:fit-content; padding:3px 7px; border-radius:99px; color:var(--green); background:var(--green-soft); font-size:10px; font-weight:800; }.source-health-actions { display:flex; gap:6px; margin-top:5px; }.advanced-discovery { padding:10px 0; border-top:1px solid var(--line); border-bottom:1px solid var(--line); }.advanced-discovery summary { cursor:pointer; color:var(--teal); font-size:12px; font-weight:750; margin-bottom:9px; }.discovery-partial p { margin: 4px 0 0; }.discovery-empty { padding: 30px 10px; color: var(--muted); text-align: center; }
|
||||
.ai-facts { display: grid; grid-template-columns: repeat(4, 1fr); gap: 7px; margin: 13px 0; }.ai-facts div { padding: 8px; border: 1px solid var(--line); border-radius: 8px; background: #fff; }.ai-facts dt { color: var(--muted); font-size: 9px; font-weight: 800; text-transform: uppercase; }.ai-facts dd { margin: 3px 0 0; overflow-wrap: anywhere; font-size: 11px; }
|
||||
@media(max-width:700px) { .discovery-hero { flex-direction: column; }.discovery-workspace-grid { grid-template-columns: 1fr; }.discovery-limit-grid, .ai-facts, .discovery-stat-grid { grid-template-columns: 1fr 1fr; }.discovery-two-col { grid-template-columns: 1fr; }.discovery-run-row { grid-template-columns: 1fr auto; }.discovery-run-row > small { grid-column: 1 / -1; } }
|
||||
|
||||
/* ProspectOS UI refresh: clearer hierarchy, calmer surfaces, and stronger action states. */
|
||||
:root {
|
||||
--ink: #172033;
|
||||
--ink-strong: #0b1220;
|
||||
--muted: #667085;
|
||||
--muted-soft: #98a2b3;
|
||||
--line: #e4e7ec;
|
||||
--line-strong: #cfd5df;
|
||||
--surface: #ffffff;
|
||||
--surface-alt: #f8fafc;
|
||||
--bg: #f4f6fa;
|
||||
--navy: #111827;
|
||||
--navy-2: #1f2a44;
|
||||
--navy-3: #344054;
|
||||
--accent: #635bff;
|
||||
--accent-dark: #5048d8;
|
||||
--accent-soft: #eeedff;
|
||||
--teal: #0f766e;
|
||||
--teal-soft: #e8f7f4;
|
||||
--shadow-sm: 0 1px 3px rgba(16,24,40,.06), 0 1px 2px rgba(16,24,40,.03);
|
||||
--shadow: 0 12px 30px rgba(16,24,40,.08);
|
||||
--radius: 16px;
|
||||
}
|
||||
body { background: var(--bg); color: var(--ink); }
|
||||
.sidebar { width: 264px; padding: 24px 14px 18px; background: linear-gradient(180deg,#111827 0%,#172033 100%); box-shadow: 14px 0 34px rgba(16,24,40,.10); }
|
||||
.main { margin-left: 264px; }
|
||||
.brand { padding: 0 12px 30px; font-size: 20px; }
|
||||
.brand-mark { width: 32px; height: 32px; border-radius: 10px; color: #fff; background: var(--accent); box-shadow: 0 7px 18px rgba(99,91,255,.34); }
|
||||
.nav-group { margin: 0 0 22px; }
|
||||
.nav-label { margin: 0 12px 7px; color: #7f8aa3; font-size: 10px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; }
|
||||
.nav-item { padding: 10px 12px; border-radius: 10px; color: #aeb8cb; }
|
||||
.nav-item span { color: #8390a8; }
|
||||
.nav-item:hover, .nav-item.active { color: #fff; background: rgba(255,255,255,.09); border-color: rgba(255,255,255,.08); }
|
||||
.nav-item.active { box-shadow: inset 3px 0 var(--accent); }
|
||||
.nav-item.active span { color: #a9a4ff; }
|
||||
.topbar { height: 76px; padding: 0 clamp(22px,4vw,58px); background: rgba(255,255,255,.88); border-bottom-color: #eaecf0; }
|
||||
.crumb { color: var(--muted); font-size: 13px; }.crumb strong { color: var(--ink); }.crumb span { margin: 0 9px; }
|
||||
.content { padding-top: 38px; }
|
||||
.hero { margin-bottom: 25px; }.hero h1 { font-size: clamp(32px,4vw,48px); letter-spacing: -.055em; }.hero h1 span:last-child { color: var(--accent); }.hero-sub { margin-top: 10px; }
|
||||
.button { min-height: 40px; border-radius: 10px; border-color: var(--line-strong); background: #fff; }.button.primary { border-color: var(--accent); background: var(--accent); box-shadow: 0 5px 12px rgba(99,91,255,.18); }.button.primary:hover { background: var(--accent-dark); }
|
||||
.panel, .metric-card { border-color: var(--line); border-radius: var(--radius); box-shadow: var(--shadow-sm); }
|
||||
.metric-card { padding: 18px; background: #fff; }.metric-card:first-child { border-color: #d9d6ff; background: linear-gradient(135deg,#fff,#f5f4ff); }.metric-icon { border-radius: 11px; background: var(--accent-soft); }.metric-icon.violet, .metric-icon.blue { color: var(--accent); background: var(--accent-soft); }
|
||||
.score-overview { margin-top: 16px; }.score-distribution-panel, .score-rules-section .panel { padding: 22px; }
|
||||
.eyebrow { color: var(--accent); letter-spacing: .13em; }
|
||||
input, select, textarea { border-color: #d0d5dd; border-radius: 10px; background: #fff; }.filters { margin-top: 18px; }.filters select, .search-wrap input { min-height: 42px; }
|
||||
th { background: #f8fafc; } td { padding-top: 14px; padding-bottom: 14px; }
|
||||
.review-queue { border-color: #d9d6ff; background: #fafaff; }.queue-row:hover, tbody tr:hover, tbody tr.selected, .job-row:hover, .job-row.selected, .discovery-run-row:hover, .discovery-run-row.selected { background: #f5f4ff; }
|
||||
.source-safety, .suppression-warning { border-color: #f5d48a; background: #fffbeb; }.source-health, .score-safety, .crm-safety, .outreach-safety, .outreach-settings-safety, .contact-extraction-safety, .website-scan-safety, .domain-safety, .ai-provider-safety { background: #effaf8; border-color: #b7e4dc; }
|
||||
.discovery-badge, .source-health-status { color: var(--teal); background: var(--teal-soft); }
|
||||
.source-row, .job-row, .discovery-run-row { border-color: #edf0f4; }.source-type { background: #f8fafc; }
|
||||
.detail-panel, .jobs-list, .job-detail, .sources-list, .source-records, .source-config-panel, .discovery-panel, .add-panel, .csv-panel, .interaction-panel, .reports-grid > article, .direct-discovery-form-panel, .discovery-runs-panel, .discovery-results-panel { background: #fff; }
|
||||
.sidebar-foot { border-top-color: rgba(255,255,255,.10); }.live-dot { background: #53d1ae; }
|
||||
footer { border-color: #e4e7ec; }
|
||||
@media (max-width: 820px) { .sidebar { width: 232px; } .main { margin-left: 232px; } }
|
||||
@media (max-width: 700px) { .main { margin-left: 0; } .topbar { height: 68px; } .content { padding-top: 26px; } }
|
||||
|
||||
Reference in New Issue
Block a user