Redesign CORS tester interface
This commit is contained in:
162
src/App.tsx
162
src/App.tsx
@@ -623,34 +623,26 @@ export default function App() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="app-shell">
|
<main className="app-shell">
|
||||||
<section className="hero">
|
<header className="topbar">
|
||||||
<div>
|
<div>
|
||||||
<p className="eyebrow">Static browser CORS diagnostics</p>
|
<p className="eyebrow">Browser CORS tester</p>
|
||||||
<h1>CORS Test Lab</h1>
|
<h1>CORS Test Lab</h1>
|
||||||
<p className="hero-copy">
|
|
||||||
Test real browser CORS behavior from this page origin, classify preflights, inspect readable responses,
|
|
||||||
and generate reports for API owners.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="origin-panel">
|
<div className="origin-badge">
|
||||||
<span>Testing origin</span>
|
<span>Actual origin</span>
|
||||||
<strong>{getOriginLabel()}</strong>
|
<strong>{getOriginLabel()}</strong>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</header>
|
||||||
|
|
||||||
<section className="dashboard">
|
<section className="workspace">
|
||||||
<div className="request-panel">
|
<div className="tester-card">
|
||||||
<div className="section-heading">
|
<div className="card-heading">
|
||||||
<div>
|
<h2>Test an API endpoint</h2>
|
||||||
<p className="eyebrow">Request builder</p>
|
<p>Run a real browser request from the origin shown above. The app cannot spoof the Origin header.</p>
|
||||||
<h2>Configure a test</h2>
|
|
||||||
</div>
|
|
||||||
<button className="primary" disabled={isRunning} onClick={() => void runTest()}>
|
|
||||||
{isRunning ? 'Running...' : 'Run test'}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label className="field field-full">
|
<div className="url-row">
|
||||||
|
<label className="field">
|
||||||
<span>API URL</span>
|
<span>API URL</span>
|
||||||
<input
|
<input
|
||||||
value={draft.url}
|
value={draft.url}
|
||||||
@@ -659,8 +651,12 @@ export default function App() {
|
|||||||
inputMode="url"
|
inputMode="url"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
<button className="primary run-button" disabled={isRunning} onClick={() => void runTest()}>
|
||||||
|
{isRunning ? 'Running...' : 'Run test'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="control-grid">
|
<div className="request-grid">
|
||||||
<label className="field">
|
<label className="field">
|
||||||
<span>Method</span>
|
<span>Method</span>
|
||||||
<select value={draft.method} onChange={(event) => patchDraft({ method: event.target.value as HttpMethod })}>
|
<select value={draft.method} onChange={(event) => patchDraft({ method: event.target.value as HttpMethod })}>
|
||||||
@@ -706,7 +702,7 @@ export default function App() {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="status-strip">
|
<div className="summary-strip">
|
||||||
<span className={preflightExpected ? 'pill warning' : 'pill ok'}>
|
<span className={preflightExpected ? 'pill warning' : 'pill ok'}>
|
||||||
{preflightExpected ? 'Preflight expected' : 'Simple request shape'}
|
{preflightExpected ? 'Preflight expected' : 'Simple request shape'}
|
||||||
</span>
|
</span>
|
||||||
@@ -714,6 +710,29 @@ export default function App() {
|
|||||||
<span className="pill">Credentials: {draft.credentials}</span>
|
<span className="pill">Credentials: {draft.credentials}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="quick-probes">
|
||||||
|
<div>
|
||||||
|
<h3>Quick probes</h3>
|
||||||
|
<p>Use the same URL with common CORS shapes.</p>
|
||||||
|
</div>
|
||||||
|
<div className="probe-list">
|
||||||
|
{scenarios.map((scenario) => (
|
||||||
|
<button
|
||||||
|
className="probe-chip"
|
||||||
|
key={scenario.id}
|
||||||
|
disabled={isRunning}
|
||||||
|
onClick={() => void runTest(scenario.overrides, scenario.title)}
|
||||||
|
title={scenario.description}
|
||||||
|
>
|
||||||
|
{scenario.title}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<details className="details-panel">
|
||||||
|
<summary>Request details</summary>
|
||||||
|
<div className="details-body">
|
||||||
<div className="section-subhead">
|
<div className="section-subhead">
|
||||||
<h3>Headers</h3>
|
<h3>Headers</h3>
|
||||||
<button className="ghost" onClick={() => patchDraft({ headers: [...draft.headers, emptyHeader()] })}>
|
<button className="ghost" onClick={() => patchDraft({ headers: [...draft.headers, emptyHeader()] })}>
|
||||||
@@ -740,7 +759,7 @@ export default function App() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="control-grid body-grid">
|
<div className="body-grid">
|
||||||
<label className="field">
|
<label className="field">
|
||||||
<span>Content type</span>
|
<span>Content type</span>
|
||||||
<select value={draft.contentType} onChange={(event) => patchDraft({ contentType: event.target.value })}>
|
<select value={draft.contentType} onChange={(event) => patchDraft({ contentType: event.target.value })}>
|
||||||
@@ -762,43 +781,20 @@ export default function App() {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
<aside className="side-panel">
|
<details className="details-panel">
|
||||||
<div className="section-heading compact">
|
<summary>Domain policy notes</summary>
|
||||||
<div>
|
<div className="details-body">
|
||||||
<p className="eyebrow">Scenarios</p>
|
|
||||||
<h2>One-click probes</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="scenario-grid">
|
|
||||||
{scenarios.map((scenario) => (
|
|
||||||
<button
|
|
||||||
className="scenario-card"
|
|
||||||
key={scenario.id}
|
|
||||||
disabled={isRunning}
|
|
||||||
onClick={() => void runTest(scenario.overrides, scenario.title)}
|
|
||||||
>
|
|
||||||
<strong>{scenario.title}</strong>
|
|
||||||
<span>{scenario.description}</span>
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="policy-panel">
|
|
||||||
<div className="section-heading compact">
|
|
||||||
<div>
|
|
||||||
<p className="eyebrow">Domain policy</p>
|
|
||||||
<h2>Expected origins</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="policy-origin">
|
<div className="policy-origin">
|
||||||
<span>Actual browser origin</span>
|
<span>Actual browser origin</span>
|
||||||
<strong>{getOriginLabel()}</strong>
|
<strong>{getOriginLabel()}</strong>
|
||||||
</div>
|
</div>
|
||||||
<p className="policy-note">
|
<p className="policy-note">
|
||||||
This static page cannot spoof Origin. Use these lists to record expected allow/block rules; this run
|
Add expected allowed or blocked origins for your report. This is not a spoofed test; to verify another
|
||||||
only proves behavior for the actual browser origin above.
|
domain, run this app from that domain.
|
||||||
</p>
|
</p>
|
||||||
|
<div className="policy-grid">
|
||||||
<label className="field policy-field">
|
<label className="field policy-field">
|
||||||
<span>Expected allowed origins</span>
|
<span>Expected allowed origins</span>
|
||||||
<textarea
|
<textarea
|
||||||
@@ -820,26 +816,27 @@ export default function App() {
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
<div className="saved-panel">
|
<details className="details-panel">
|
||||||
<div className="section-subhead">
|
<summary>Saved cases</summary>
|
||||||
<h3>Saved cases</h3>
|
<div className="details-body">
|
||||||
<div className="button-pair">
|
|
||||||
<button className="ghost" disabled={!savedCases.length} onClick={exportCases}>
|
|
||||||
Export
|
|
||||||
</button>
|
|
||||||
<button className="ghost" onClick={() => fileInputRef.current?.click()}>
|
|
||||||
Import
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<input ref={fileInputRef} hidden type="file" accept="application/json" onChange={(event) => void importCases(event)} />
|
|
||||||
<div className="save-row">
|
<div className="save-row">
|
||||||
<input value={caseName} onChange={(event) => setCaseName(event.target.value)} placeholder="Case name" />
|
<input value={caseName} onChange={(event) => setCaseName(event.target.value)} placeholder="Case name" />
|
||||||
<button className="secondary" onClick={saveCase}>
|
<button className="secondary" onClick={saveCase}>
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="saved-actions">
|
||||||
|
<button className="ghost" disabled={!savedCases.length} onClick={exportCases}>
|
||||||
|
Export JSON
|
||||||
|
</button>
|
||||||
|
<button className="ghost" onClick={() => fileInputRef.current?.click()}>
|
||||||
|
Import JSON
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<input ref={fileInputRef} hidden type="file" accept="application/json" onChange={(event) => void importCases(event)} />
|
||||||
<div className="saved-list">
|
<div className="saved-list">
|
||||||
{savedCases.length === 0 ? (
|
{savedCases.length === 0 ? (
|
||||||
<p className="muted">No saved cases yet.</p>
|
<p className="muted">No saved cases yet.</p>
|
||||||
@@ -864,15 +861,14 @@ export default function App() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</details>
|
||||||
</section>
|
</div>
|
||||||
|
|
||||||
<section className="results-layout">
|
|
||||||
<div className="result-panel">
|
<div className="result-panel">
|
||||||
<div className="section-heading">
|
<div className="card-heading result-heading">
|
||||||
<div>
|
<div>
|
||||||
<p className="eyebrow">Latest result</p>
|
<h2>Result</h2>
|
||||||
<h2>{latestResult ? latestResult.label : 'No test run yet'}</h2>
|
<p>{latestResult ? latestResult.label : 'Run a test to see browser evidence and diagnostics.'}</p>
|
||||||
</div>
|
</div>
|
||||||
{latestResult ? (
|
{latestResult ? (
|
||||||
<button className="secondary" onClick={() => void copyReport(latestResult)}>
|
<button className="secondary" onClick={() => void copyReport(latestResult)}>
|
||||||
@@ -892,13 +888,16 @@ export default function App() {
|
|||||||
<span className={`big-status ${latestResult.status}`}>{latestResult.status}</span>
|
<span className={`big-status ${latestResult.status}`}>{latestResult.status}</span>
|
||||||
<div>
|
<div>
|
||||||
<strong>{latestResult.browserResult}</strong>
|
<strong>{latestResult.browserResult}</strong>
|
||||||
<span>
|
<span>{latestResult.durationMs}ms</span>
|
||||||
{latestResult.durationMs}ms
|
|
||||||
{latestResult.statusCode ? `, HTTP ${latestResult.statusCode} ${latestResult.statusText ?? ''}` : ''}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="result-meta">
|
||||||
|
<span>HTTP: {latestResult.statusCode ? `${latestResult.statusCode} ${latestResult.statusText ?? ''}` : 'not readable'}</span>
|
||||||
|
<span>Type: {latestResult.responseType ?? 'not available'}</span>
|
||||||
|
<span>Origin: {getOriginLabel()}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
{latestResult.error ? <div className="alert danger">{latestResult.error}</div> : null}
|
{latestResult.error ? <div className="alert danger">{latestResult.error}</div> : null}
|
||||||
{latestResult.warnings.map((warning) => (
|
{latestResult.warnings.map((warning) => (
|
||||||
<div className="alert warning" key={warning}>
|
<div className="alert warning" key={warning}>
|
||||||
@@ -922,15 +921,9 @@ export default function App() {
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="analysis-panel">
|
<div className="analysis-block">
|
||||||
<div className="section-heading compact">
|
<h3>Diagnostics</h3>
|
||||||
<div>
|
|
||||||
<p className="eyebrow">Analysis</p>
|
|
||||||
<h2>What it means</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{latestResult ? (
|
{latestResult ? (
|
||||||
<ul className="diagnostics-list">
|
<ul className="diagnostics-list">
|
||||||
{latestResult.diagnostics.map((diagnostic) => (
|
{latestResult.diagnostics.map((diagnostic) => (
|
||||||
@@ -940,6 +933,7 @@ export default function App() {
|
|||||||
) : (
|
) : (
|
||||||
<p className="muted">Diagnostics appear after a run.</p>
|
<p className="muted">Diagnostics appear after a run.</p>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="history">
|
<div className="history">
|
||||||
<h3>Run history</h3>
|
<h3>Run history</h3>
|
||||||
|
|||||||
796
src/styles.css
796
src/styles.css
@@ -2,8 +2,8 @@
|
|||||||
color-scheme: light;
|
color-scheme: light;
|
||||||
font-family:
|
font-family:
|
||||||
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
color: #172033;
|
color: #182230;
|
||||||
background: #eef4f8;
|
background: #f4f6f8;
|
||||||
font-synthesis: none;
|
font-synthesis: none;
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
@@ -14,12 +14,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
|
||||||
min-width: 320px;
|
min-width: 320px;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background:
|
margin: 0;
|
||||||
radial-gradient(circle at top left, rgba(37, 99, 235, 0.18), transparent 30rem),
|
background: #f4f6f8;
|
||||||
linear-gradient(135deg, #f8fbff 0%, #eef4f8 44%, #e8eef5 100%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button,
|
button,
|
||||||
@@ -38,29 +36,6 @@ button:disabled {
|
|||||||
opacity: 0.55;
|
opacity: 0.55;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-shell {
|
|
||||||
width: min(1480px, calc(100% - 32px));
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 28px 0 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: minmax(0, 1fr) minmax(280px, 420px);
|
|
||||||
gap: 24px;
|
|
||||||
align-items: end;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eyebrow {
|
|
||||||
margin: 0 0 8px;
|
|
||||||
color: #2563eb;
|
|
||||||
font-size: 0.78rem;
|
|
||||||
font-weight: 800;
|
|
||||||
letter-spacing: 0.08em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
@@ -68,142 +43,162 @@ p {
|
|||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
.app-shell {
|
||||||
margin-bottom: 12px;
|
width: min(1280px, calc(100% - 32px));
|
||||||
color: #0f172a;
|
margin: 0 auto;
|
||||||
font-size: clamp(2.8rem, 7vw, 6.5rem);
|
padding: 24px 0 48px;
|
||||||
line-height: 0.95;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
.topbar {
|
||||||
margin-bottom: 0;
|
|
||||||
color: #111827;
|
|
||||||
font-size: 1.35rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
color: #1f2937;
|
|
||||||
font-size: 0.98rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-copy {
|
|
||||||
max-width: 820px;
|
|
||||||
margin-bottom: 0;
|
|
||||||
color: #526173;
|
|
||||||
font-size: 1.08rem;
|
|
||||||
line-height: 1.65;
|
|
||||||
}
|
|
||||||
|
|
||||||
.origin-panel {
|
|
||||||
padding: 18px;
|
|
||||||
border: 1px solid rgba(37, 99, 235, 0.18);
|
|
||||||
border-radius: 8px;
|
|
||||||
background: rgba(255, 255, 255, 0.76);
|
|
||||||
box-shadow: 0 18px 50px rgba(30, 41, 59, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.origin-panel span,
|
|
||||||
.field span {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 7px;
|
|
||||||
color: #64748b;
|
|
||||||
font-size: 0.78rem;
|
|
||||||
font-weight: 800;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.origin-panel strong {
|
|
||||||
display: block;
|
|
||||||
overflow-wrap: anywhere;
|
|
||||||
color: #0f172a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard,
|
|
||||||
.results-layout {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: minmax(0, 1.45fr) minmax(360px, 0.8fr);
|
|
||||||
gap: 20px;
|
|
||||||
align-items: start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.results-layout {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.request-panel,
|
|
||||||
.side-panel,
|
|
||||||
.result-panel,
|
|
||||||
.analysis-panel {
|
|
||||||
border: 1px solid rgba(148, 163, 184, 0.35);
|
|
||||||
border-radius: 8px;
|
|
||||||
background: rgba(255, 255, 255, 0.86);
|
|
||||||
box-shadow: 0 20px 60px rgba(15, 23, 42, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.request-panel,
|
|
||||||
.result-panel,
|
|
||||||
.analysis-panel {
|
|
||||||
padding: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.side-panel {
|
|
||||||
padding: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-heading,
|
|
||||||
.section-subhead {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 16px;
|
gap: 20px;
|
||||||
align-items: center;
|
align-items: flex-end;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 18px;
|
margin-bottom: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-heading.compact {
|
.eyebrow {
|
||||||
margin-bottom: 14px;
|
margin-bottom: 6px;
|
||||||
|
color: #2563eb;
|
||||||
|
font-size: 0.74rem;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-subhead {
|
h1 {
|
||||||
margin: 18px 0 10px;
|
margin-bottom: 0;
|
||||||
|
color: #101828;
|
||||||
|
font-size: 2.25rem;
|
||||||
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-subhead h3 {
|
h2 {
|
||||||
margin: 0;
|
margin-bottom: 6px;
|
||||||
|
color: #101828;
|
||||||
|
font-size: 1.18rem;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: #182230;
|
||||||
|
font-size: 0.96rem;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
.origin-badge {
|
||||||
|
max-width: 520px;
|
||||||
|
border: 1px solid #d0d5dd;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.origin-badge span,
|
||||||
|
.field span,
|
||||||
|
.policy-origin span {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
color: #667085;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 800;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.origin-badge strong,
|
||||||
|
.policy-origin strong {
|
||||||
|
display: block;
|
||||||
|
color: #101828;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 0.95fr) minmax(420px, 1.05fr);
|
||||||
|
gap: 18px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tester-card,
|
||||||
|
.result-panel {
|
||||||
|
border: 1px solid #d0d5dd;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0 12px 30px rgba(16, 24, 40, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tester-card {
|
||||||
|
padding: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-panel {
|
||||||
|
padding: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-heading {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-heading p,
|
||||||
|
.quick-probes p,
|
||||||
|
.policy-note,
|
||||||
|
.muted {
|
||||||
|
margin-bottom: 0;
|
||||||
|
color: #667085;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-heading {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.url-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.request-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.field {
|
.field {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.field-full {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input,
|
input,
|
||||||
select,
|
select,
|
||||||
textarea {
|
textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 1px solid #d4dce7;
|
border: 1px solid #d0d5dd;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
color: #172033;
|
color: #182230;
|
||||||
outline: none;
|
outline: none;
|
||||||
transition:
|
transition:
|
||||||
border-color 160ms ease,
|
border-color 160ms ease,
|
||||||
box-shadow 160ms ease,
|
box-shadow 160ms ease;
|
||||||
background 160ms ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input,
|
input,
|
||||||
select {
|
select {
|
||||||
height: 42px;
|
height: 42px;
|
||||||
padding: 0 12px;
|
padding: 0 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
min-height: 156px;
|
min-height: 130px;
|
||||||
padding: 12px;
|
padding: 10px 11px;
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
@@ -212,22 +207,173 @@ input:focus,
|
|||||||
select:focus,
|
select:focus,
|
||||||
textarea:focus {
|
textarea:focus {
|
||||||
border-color: #2563eb;
|
border-color: #2563eb;
|
||||||
box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.14);
|
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.14);
|
||||||
}
|
}
|
||||||
|
|
||||||
.control-grid {
|
.primary,
|
||||||
|
.secondary,
|
||||||
|
.ghost,
|
||||||
|
.icon-button,
|
||||||
|
.probe-chip {
|
||||||
|
min-height: 40px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary {
|
||||||
|
border: 1px solid #1d4ed8;
|
||||||
|
background: #2563eb;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary:hover {
|
||||||
|
background: #1d4ed8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.run-button {
|
||||||
|
min-width: 132px;
|
||||||
|
padding: 0 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary {
|
||||||
|
border: 1px solid #bfdbfe;
|
||||||
|
background: #eff6ff;
|
||||||
|
color: #1d4ed8;
|
||||||
|
padding: 0 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ghost {
|
||||||
|
border: 1px solid #d0d5dd;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #344054;
|
||||||
|
padding: 0 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button {
|
||||||
|
width: 42px;
|
||||||
|
border: 1px solid #d0d5dd;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #667085;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-strip,
|
||||||
|
.probe-list,
|
||||||
|
.saved-actions,
|
||||||
|
.result-meta {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-strip {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pill {
|
||||||
|
display: inline-flex;
|
||||||
|
min-height: 28px;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid #d0d5dd;
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
background: #f9fafb;
|
||||||
|
color: #475467;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pill.ok {
|
||||||
|
border-color: #bbf7d0;
|
||||||
|
background: #f0fdf4;
|
||||||
|
color: #15803d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pill.warning {
|
||||||
|
border-color: #fed7aa;
|
||||||
|
background: #fff7ed;
|
||||||
|
color: #b45309;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-probes {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
gap: 10px;
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.body-grid {
|
|
||||||
grid-template-columns: minmax(180px, 0.32fr) minmax(0, 1fr);
|
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
|
border: 1px solid #e4e7ec;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
background: #f9fafb;
|
||||||
}
|
}
|
||||||
|
|
||||||
.field-body {
|
.quick-probes h3 {
|
||||||
grid-row: span 2;
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.probe-chip {
|
||||||
|
border: 1px solid #d0d5dd;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #344054;
|
||||||
|
padding: 0 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.probe-chip:hover,
|
||||||
|
.ghost:hover,
|
||||||
|
.saved-item:hover,
|
||||||
|
.history-row:hover {
|
||||||
|
border-color: #98a2b3;
|
||||||
|
background: #f9fafb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-panel {
|
||||||
|
margin-top: 12px;
|
||||||
|
border: 1px solid #e4e7ec;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-panel summary {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 46px;
|
||||||
|
padding: 0 13px;
|
||||||
|
color: #182230;
|
||||||
|
font-weight: 850;
|
||||||
|
list-style: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-panel summary::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-panel summary::after {
|
||||||
|
margin-left: auto;
|
||||||
|
color: #667085;
|
||||||
|
content: "+";
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-panel[open] summary {
|
||||||
|
border-bottom: 1px solid #e4e7ec;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-panel[open] summary::after {
|
||||||
|
content: "-";
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-body {
|
||||||
|
padding: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-subhead {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-subhead h3 {
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headers-list {
|
.headers-list {
|
||||||
@@ -235,184 +381,56 @@ textarea:focus {
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-row,
|
.header-row {
|
||||||
.save-row {
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 42px;
|
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 42px;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.save-row {
|
.body-grid,
|
||||||
grid-template-columns: minmax(0, 1fr) auto;
|
.policy-grid,
|
||||||
}
|
.detail-grid {
|
||||||
|
|
||||||
.status-strip {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 8px;
|
|
||||||
margin-top: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill {
|
|
||||||
display: inline-flex;
|
|
||||||
min-height: 30px;
|
|
||||||
align-items: center;
|
|
||||||
border: 1px solid #dbe3ef;
|
|
||||||
border-radius: 999px;
|
|
||||||
padding: 6px 10px;
|
|
||||||
background: #f8fafc;
|
|
||||||
color: #475569;
|
|
||||||
font-size: 0.82rem;
|
|
||||||
font-weight: 800;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill.ok {
|
|
||||||
border-color: rgba(22, 163, 74, 0.25);
|
|
||||||
background: #edfdf3;
|
|
||||||
color: #15803d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill.warning {
|
|
||||||
border-color: rgba(217, 119, 6, 0.28);
|
|
||||||
background: #fff7ed;
|
|
||||||
color: #b45309;
|
|
||||||
}
|
|
||||||
|
|
||||||
.primary,
|
|
||||||
.secondary,
|
|
||||||
.ghost,
|
|
||||||
.icon-button {
|
|
||||||
min-height: 40px;
|
|
||||||
border: 0;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 0 14px;
|
|
||||||
font-weight: 800;
|
|
||||||
}
|
|
||||||
|
|
||||||
.primary {
|
|
||||||
background: #2563eb;
|
|
||||||
color: #ffffff;
|
|
||||||
box-shadow: 0 14px 30px rgba(37, 99, 235, 0.28);
|
|
||||||
}
|
|
||||||
|
|
||||||
.secondary {
|
|
||||||
border: 1px solid #bfdbfe;
|
|
||||||
background: #eff6ff;
|
|
||||||
color: #1d4ed8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ghost {
|
|
||||||
border: 1px solid #d8e1ed;
|
|
||||||
background: #ffffff;
|
|
||||||
color: #334155;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-button {
|
|
||||||
width: 42px;
|
|
||||||
padding: 0;
|
|
||||||
border: 1px solid #d8e1ed;
|
|
||||||
background: #ffffff;
|
|
||||||
color: #64748b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-pair {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scenario-grid {
|
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 10px;
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scenario-card {
|
.body-grid {
|
||||||
width: 100%;
|
|
||||||
border: 1px solid #d9e2ee;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 14px;
|
|
||||||
background: #ffffff;
|
|
||||||
color: #172033;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scenario-card:hover,
|
|
||||||
.saved-item:hover,
|
|
||||||
.history-row:hover {
|
|
||||||
border-color: #93c5fd;
|
|
||||||
background: #f8fbff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scenario-card strong,
|
|
||||||
.scenario-card span,
|
|
||||||
.saved-item strong,
|
|
||||||
.saved-item span {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scenario-card span,
|
|
||||||
.saved-item span,
|
|
||||||
.history-row span,
|
|
||||||
.policy-note,
|
|
||||||
.muted {
|
|
||||||
color: #64748b;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.policy-panel {
|
|
||||||
margin-top: 20px;
|
|
||||||
border-top: 1px solid #e2e8f0;
|
|
||||||
padding-top: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.policy-origin {
|
|
||||||
border: 1px solid #dbeafe;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 12px;
|
|
||||||
background: #eff6ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.policy-origin span,
|
|
||||||
.policy-origin strong {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.policy-origin span {
|
|
||||||
margin-bottom: 4px;
|
|
||||||
color: #2563eb;
|
|
||||||
font-size: 0.76rem;
|
|
||||||
font-weight: 900;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.policy-origin strong {
|
|
||||||
overflow-wrap: anywhere;
|
|
||||||
color: #1e3a8a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.policy-note {
|
|
||||||
margin: 10px 0 14px;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.policy-field {
|
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.origin-textarea {
|
.origin-textarea {
|
||||||
min-height: 94px;
|
min-height: 96px;
|
||||||
font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
|
font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
|
||||||
font-size: 0.84rem;
|
font-size: 0.84rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.saved-panel {
|
.policy-origin {
|
||||||
margin-top: 20px;
|
border: 1px solid #bfdbfe;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px;
|
||||||
|
background: #eff6ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.policy-note {
|
||||||
|
margin: 10px 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.saved-actions {
|
||||||
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.saved-list {
|
.saved-list {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
max-height: 360px;
|
max-height: 280px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,10 +438,10 @@ textarea:focus {
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 1fr) 42px;
|
grid-template-columns: minmax(0, 1fr) 42px;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
border: 1px solid #d9e2ee;
|
border: 1px solid #e4e7ec;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: #ffffff;
|
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
background: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.saved-item > button:first-child {
|
.saved-item > button:first-child {
|
||||||
@@ -433,15 +451,41 @@ textarea:focus {
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.saved-item strong,
|
||||||
|
.saved-item span {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.saved-item span,
|
||||||
|
.history-row span {
|
||||||
|
color: #667085;
|
||||||
|
font-size: 0.86rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
display: grid;
|
||||||
|
min-height: 220px;
|
||||||
|
place-content: center;
|
||||||
|
border: 1px dashed #cfd4dc;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f9fafb;
|
||||||
|
color: #667085;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state strong {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
color: #182230;
|
||||||
|
}
|
||||||
|
|
||||||
.result-summary {
|
.result-summary {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 14px;
|
gap: 12px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 16px;
|
border: 1px solid #e4e7ec;
|
||||||
border: 1px solid #dbe3ef;
|
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 14px;
|
padding: 12px;
|
||||||
background: #f8fafc;
|
background: #f9fafb;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-summary strong,
|
.result-summary strong,
|
||||||
@@ -451,9 +495,9 @@ textarea:focus {
|
|||||||
|
|
||||||
.big-status {
|
.big-status {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 12px 14px;
|
padding: 10px 12px;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-size: 0.9rem;
|
font-size: 0.82rem;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
@@ -473,11 +517,26 @@ textarea:focus {
|
|||||||
background: #7c3aed;
|
background: #7c3aed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.result-meta {
|
||||||
|
margin: 10px 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-meta span {
|
||||||
|
border: 1px solid #e4e7ec;
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: 5px 9px;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #475467;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
font-weight: 750;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
.alert {
|
.alert {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 12px;
|
padding: 10px 12px;
|
||||||
font-weight: 700;
|
font-weight: 750;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert.danger {
|
.alert.danger {
|
||||||
@@ -492,19 +551,13 @@ textarea:focus {
|
|||||||
color: #b45309;
|
color: #b45309;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
||||||
gap: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
max-height: 360px;
|
max-height: 300px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
border: 1px solid #dbe3ef;
|
border: 1px solid #e4e7ec;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 12px;
|
padding: 11px;
|
||||||
background: #0f172a;
|
background: #101828;
|
||||||
color: #dbeafe;
|
color: #dbeafe;
|
||||||
font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
|
font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
|
||||||
font-size: 0.82rem;
|
font-size: 0.82rem;
|
||||||
@@ -513,36 +566,41 @@ pre {
|
|||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.analysis-block {
|
||||||
|
margin-top: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
.diagnostics-list {
|
.diagnostics-list {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 10px;
|
gap: 8px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.diagnostics-list li {
|
.diagnostics-list li {
|
||||||
border: 1px solid #dbe3ef;
|
border: 1px solid #e4e7ec;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 12px;
|
padding: 10px 12px;
|
||||||
background: #f8fafc;
|
background: #ffffff;
|
||||||
color: #334155;
|
color: #344054;
|
||||||
|
line-height: 1.45;
|
||||||
}
|
}
|
||||||
|
|
||||||
.history {
|
.history {
|
||||||
margin-top: 22px;
|
margin-top: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.history-row {
|
.history-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
grid-template-columns: 10px minmax(0, 1fr) auto;
|
grid-template-columns: 10px minmax(0, 1fr) auto;
|
||||||
gap: 10px;
|
gap: 9px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border: 1px solid #dbe3ef;
|
border: 1px solid #e4e7ec;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
padding: 10px;
|
padding: 9px 10px;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@@ -554,77 +612,65 @@ pre {
|
|||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-state {
|
@media (max-width: 980px) {
|
||||||
display: grid;
|
.topbar,
|
||||||
min-height: 220px;
|
.workspace {
|
||||||
place-content: center;
|
|
||||||
border: 1px dashed #bfcbda;
|
|
||||||
border-radius: 8px;
|
|
||||||
background: #f8fafc;
|
|
||||||
color: #64748b;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-state strong {
|
|
||||||
color: #172033;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 1120px) {
|
|
||||||
.hero,
|
|
||||||
.dashboard,
|
|
||||||
.results-layout {
|
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-panel {
|
.topbar {
|
||||||
order: 2;
|
display: grid;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace {
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.request-grid,
|
||||||
|
.detail-grid {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 760px) {
|
@media (max-width: 700px) {
|
||||||
.app-shell {
|
.app-shell {
|
||||||
width: min(100% - 20px, 1480px);
|
width: min(100% - 20px, 1280px);
|
||||||
padding-top: 18px;
|
padding-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 3.1rem;
|
font-size: 1.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.request-panel,
|
.tester-card,
|
||||||
.side-panel,
|
.result-panel {
|
||||||
.result-panel,
|
|
||||||
.analysis-panel {
|
|
||||||
padding: 14px;
|
padding: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-heading,
|
.url-row,
|
||||||
.section-subhead,
|
.request-grid,
|
||||||
.result-summary {
|
.header-row,
|
||||||
|
.body-grid,
|
||||||
|
.policy-grid,
|
||||||
|
.detail-grid,
|
||||||
|
.save-row,
|
||||||
|
.history-row {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-heading,
|
||||||
|
.section-subhead {
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.control-grid,
|
.run-button,
|
||||||
.body-grid,
|
|
||||||
.header-row,
|
|
||||||
.detail-grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-row {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-button {
|
.icon-button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.history-row {
|
|
||||||
grid-template-columns: 10px minmax(0, 1fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
.history-row span:last-child {
|
.history-row span:last-child {
|
||||||
grid-column: 2;
|
grid-column: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user