360 lines
14 KiB
HTML
360 lines
14 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=1440" />
|
|
<title>Powder Coating Logix — Design Review</title>
|
|
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&family=Fraunces:opsz,wght@9..144,500;9..144,600&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css">
|
|
|
|
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
|
|
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
|
|
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
|
|
|
|
<style>
|
|
:root{
|
|
--ink:#0F0F10;
|
|
--graphite:#1A1A1C;
|
|
--slate:#3A3A3E;
|
|
--steel:#6B6B70;
|
|
--mute:#9A9A9F;
|
|
--rule:#E4E2DC;
|
|
--rule-soft:#EFEDE7;
|
|
--paper:#FAFAF7;
|
|
--paper-2:#F3F1EB;
|
|
--card:#FFFFFF;
|
|
--ember:oklch(0.68 0.17 50);
|
|
--ember-tint:oklch(0.95 0.04 55);
|
|
--ember-ink:oklch(0.42 0.15 40);
|
|
--ok:oklch(0.62 0.11 155);
|
|
--ok-tint:oklch(0.95 0.04 155);
|
|
--warn:oklch(0.76 0.13 80);
|
|
--warn-tint:oklch(0.96 0.05 85);
|
|
--bad:oklch(0.60 0.19 25);
|
|
--bad-tint:oklch(0.95 0.04 25);
|
|
--cool:oklch(0.58 0.09 240);
|
|
--cool-tint:oklch(0.95 0.03 240);
|
|
}
|
|
|
|
*{box-sizing:border-box}
|
|
html,body{margin:0;padding:0}
|
|
body{
|
|
font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
|
|
background:var(--paper);
|
|
color:var(--ink);
|
|
font-size:14px;
|
|
line-height:1.5;
|
|
-webkit-font-smoothing:antialiased;
|
|
font-feature-settings:"ss01","cv11";
|
|
}
|
|
.mono{font-family:'IBM Plex Mono',ui-monospace,monospace;font-feature-settings:"zero"}
|
|
.serif{font-family:'Fraunces',Georgia,serif;font-variation-settings:"opsz" 144}
|
|
|
|
/* ────────── Document shell ────────── */
|
|
.doc{max-width:1320px;margin:0 auto;padding:56px 48px 120px}
|
|
|
|
.doc-head{
|
|
display:grid;grid-template-columns:1fr auto;gap:32px;align-items:end;
|
|
padding-bottom:28px;border-bottom:1px solid var(--rule);margin-bottom:40px;
|
|
}
|
|
.kicker{
|
|
font-family:'IBM Plex Mono',monospace;
|
|
font-size:11px;letter-spacing:0.14em;text-transform:uppercase;
|
|
color:var(--steel);display:flex;align-items:center;gap:10px;
|
|
}
|
|
.kicker::before{content:"";width:18px;height:1px;background:var(--ember)}
|
|
h1.doc-title{
|
|
font-family:'Fraunces',Georgia,serif;
|
|
font-weight:500;
|
|
font-size:64px;
|
|
line-height:1.02;
|
|
letter-spacing:-0.02em;
|
|
margin:14px 0 10px;
|
|
color:var(--ink);
|
|
}
|
|
h1.doc-title em{font-style:italic;color:var(--ember-ink)}
|
|
.doc-sub{max-width:640px;color:var(--slate);font-size:16px;line-height:1.55}
|
|
|
|
.doc-meta{
|
|
text-align:right;font-size:12px;color:var(--steel);
|
|
display:flex;flex-direction:column;gap:4px;
|
|
}
|
|
.doc-meta b{color:var(--ink);font-weight:600}
|
|
|
|
/* ────────── Section ────────── */
|
|
section.chapter{margin:88px 0 0}
|
|
.chap-head{
|
|
display:grid;grid-template-columns:120px 1fr;gap:32px;
|
|
padding-bottom:20px;margin-bottom:28px;
|
|
border-bottom:1px solid var(--rule);
|
|
}
|
|
.chap-num{
|
|
font-family:'IBM Plex Mono',monospace;font-size:12px;letter-spacing:0.1em;
|
|
color:var(--ember-ink);padding-top:6px;
|
|
}
|
|
.chap-title{
|
|
font-family:'Fraunces',Georgia,serif;
|
|
font-weight:500;font-size:32px;line-height:1.15;letter-spacing:-0.01em;
|
|
margin:0 0 8px;color:var(--ink);
|
|
}
|
|
.chap-lede{color:var(--slate);max-width:720px;font-size:15px}
|
|
|
|
/* ────────── Critique list ────────── */
|
|
.findings{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--rule);border:1px solid var(--rule);border-radius:2px}
|
|
.finding{background:var(--card);padding:22px 22px 24px}
|
|
.finding-tag{
|
|
font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:0.12em;
|
|
color:var(--steel);text-transform:uppercase;display:flex;align-items:center;gap:8px;
|
|
}
|
|
.finding-tag .sev{
|
|
width:8px;height:8px;border-radius:50%;
|
|
}
|
|
.sev.hi{background:var(--bad)}
|
|
.sev.md{background:var(--warn)}
|
|
.sev.lo{background:var(--cool)}
|
|
.finding h4{margin:10px 0 6px;font-size:15px;font-weight:600;letter-spacing:-0.01em}
|
|
.finding p{margin:0;color:var(--slate);font-size:13px;line-height:1.55}
|
|
|
|
/* ────────── Before/After stage ────────── */
|
|
.stage{
|
|
display:grid;grid-template-columns:1fr 1fr;gap:24px;margin-top:24px;
|
|
}
|
|
.stage.single{grid-template-columns:1fr}
|
|
.mock{
|
|
background:var(--card);
|
|
border:1px solid var(--rule);
|
|
border-radius:8px;
|
|
overflow:hidden;
|
|
position:relative;
|
|
}
|
|
.mock-head{
|
|
display:flex;align-items:center;justify-content:space-between;
|
|
padding:10px 14px;border-bottom:1px solid var(--rule);
|
|
background:var(--paper-2);
|
|
}
|
|
.mock-label{
|
|
font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:0.14em;
|
|
text-transform:uppercase;color:var(--steel);
|
|
}
|
|
.mock-label b{color:var(--ink);font-weight:600}
|
|
.mock-label.after b{color:var(--ember-ink)}
|
|
.mock-tag{
|
|
font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:0.1em;
|
|
text-transform:uppercase;color:var(--steel);padding:3px 7px;
|
|
border:1px solid var(--rule);border-radius:99px;background:var(--card);
|
|
}
|
|
.mock-body{
|
|
position:relative;
|
|
overflow:hidden;
|
|
/* content is a fake app at ~1280px wide, scaled to fit column */
|
|
}
|
|
.scaler{
|
|
width:1280px;
|
|
transform-origin:top left;
|
|
/* transform scale is applied by component based on container width */
|
|
}
|
|
|
|
/* small callout strip under before/after */
|
|
.delta{
|
|
display:grid;grid-template-columns:1fr 1fr;gap:24px;margin-top:14px;
|
|
}
|
|
.delta-col{
|
|
font-size:12.5px;color:var(--slate);line-height:1.6;
|
|
}
|
|
.delta-col h5{
|
|
font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:0.14em;
|
|
text-transform:uppercase;color:var(--steel);margin:0 0 6px;font-weight:500;
|
|
}
|
|
.delta-col ul{margin:0;padding-left:16px}
|
|
.delta-col li{margin:3px 0}
|
|
.delta-col li::marker{color:var(--ember)}
|
|
|
|
/* ────────── System tokens strip ────────── */
|
|
.sys{
|
|
display:grid;grid-template-columns:repeat(4,1fr);gap:1px;
|
|
background:var(--rule);border:1px solid var(--rule);border-radius:2px;
|
|
margin-top:28px;
|
|
}
|
|
.sys-cell{background:var(--card);padding:22px}
|
|
.sys-cell h5{
|
|
font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:0.14em;
|
|
text-transform:uppercase;color:var(--steel);margin:0 0 14px;font-weight:500;
|
|
}
|
|
|
|
.swatch-row{display:flex;flex-direction:column;gap:8px}
|
|
.swatch{display:flex;align-items:center;gap:10px;font-size:12px}
|
|
.swatch .chip{width:24px;height:24px;border-radius:4px;border:1px solid rgba(0,0,0,0.08);flex-shrink:0}
|
|
.swatch .lbl{color:var(--ink);font-weight:500}
|
|
.swatch .hex{margin-left:auto;font-family:'IBM Plex Mono',monospace;color:var(--steel);font-size:11px}
|
|
|
|
.type-sample{margin-bottom:12px}
|
|
.type-sample:last-child{margin-bottom:0}
|
|
.type-meta{font-family:'IBM Plex Mono',monospace;font-size:10px;color:var(--steel);letter-spacing:0.1em;text-transform:uppercase;margin-bottom:2px}
|
|
.type-val{color:var(--ink);line-height:1.1}
|
|
|
|
/* ────────── Tweaks panel ────────── */
|
|
.tweaks{
|
|
position:fixed;right:20px;bottom:20px;width:280px;
|
|
background:var(--card);border:1px solid var(--rule);border-radius:10px;
|
|
box-shadow:0 20px 40px -20px rgba(0,0,0,0.25), 0 2px 4px rgba(0,0,0,0.04);
|
|
padding:14px 14px 10px;z-index:9999;font-size:12px;
|
|
display:none;
|
|
}
|
|
.tweaks.on{display:block}
|
|
.tweaks h5{
|
|
font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:0.14em;
|
|
text-transform:uppercase;color:var(--steel);margin:0 0 10px;
|
|
display:flex;align-items:center;justify-content:space-between;
|
|
}
|
|
.tweak-row{margin-bottom:10px}
|
|
.tweak-row label{display:block;font-size:11px;color:var(--steel);margin-bottom:4px;text-transform:uppercase;letter-spacing:0.06em}
|
|
.tweak-opts{display:flex;gap:4px;flex-wrap:wrap}
|
|
.tweak-opt{
|
|
flex:1;min-width:0;padding:6px 8px;border:1px solid var(--rule);
|
|
background:var(--card);border-radius:5px;cursor:pointer;font-size:12px;
|
|
color:var(--ink);font-weight:500;transition:all .15s;
|
|
}
|
|
.tweak-opt:hover{border-color:var(--slate)}
|
|
.tweak-opt.on{background:var(--ink);color:var(--paper);border-color:var(--ink)}
|
|
.tweak-dot{display:flex;gap:6px}
|
|
.tweak-dot button{
|
|
width:22px;height:22px;border-radius:50%;border:2px solid transparent;
|
|
cursor:pointer;padding:0;
|
|
}
|
|
.tweak-dot button.on{border-color:var(--ink)}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="doc">
|
|
|
|
<header class="doc-head">
|
|
<div>
|
|
<div class="kicker">Design critique · April 2026</div>
|
|
<h1 class="doc-title">Powder Coating Logix <em>wants to grow up.</em></h1>
|
|
<p class="doc-sub">
|
|
The app is functionally dense — billing, jobs, inventory, shop floor, accounting. Visually
|
|
it reads as a generic Bootstrap admin: rainbow KPIs, decorative icon bubbles, gradient
|
|
welcome banners, duplicated stat strips. Here's what's holding the look back, and a
|
|
direction that feels like a tool, not a template.
|
|
</p>
|
|
</div>
|
|
<div class="doc-meta">
|
|
<span>File · <b>Design Review.html</b></span>
|
|
<span>Screens analyzed · <b>Dashboard · Jobs · Board</b></span>
|
|
<span>Source · <b>PowderCoating.Web</b></span>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Mount points -->
|
|
<div id="critique-root"></div>
|
|
<div id="system-root"></div>
|
|
<div id="screen1-root"></div>
|
|
<div id="screen2-root"></div>
|
|
<div id="screen3-root"></div>
|
|
<div id="wrap-root"></div>
|
|
|
|
<div class="tweaks" id="tweaksPanel">
|
|
<h5>Tweaks <span class="mono" style="color:var(--mute)">· v1</span></h5>
|
|
<div class="tweak-row">
|
|
<label>Accent hue</label>
|
|
<div class="tweak-dot" id="tw-accent"></div>
|
|
</div>
|
|
<div class="tweak-row">
|
|
<label>Density</label>
|
|
<div class="tweak-opts" id="tw-density">
|
|
<button class="tweak-opt" data-v="comfortable">Comfortable</button>
|
|
<button class="tweak-opt on" data-v="compact">Compact</button>
|
|
</div>
|
|
</div>
|
|
<div class="tweak-row">
|
|
<label>Surface</label>
|
|
<div class="tweak-opts" id="tw-surface">
|
|
<button class="tweak-opt on" data-v="paper">Paper</button>
|
|
<button class="tweak-opt" data-v="white">White</button>
|
|
<button class="tweak-opt" data-v="ink">Ink</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script type="application/json" id="tweak-defaults">/*EDITMODE-BEGIN*/{
|
|
"accent": "ember",
|
|
"density": "comfortable",
|
|
"surface": "ink"
|
|
}/*EDITMODE-END*/</script>
|
|
|
|
<script type="text/babel" src="mock_components.jsx"></script>
|
|
<script type="text/babel" src="screens_before.jsx"></script>
|
|
<script type="text/babel" src="screens_after.jsx"></script>
|
|
<script type="text/babel" src="review_app.jsx"></script>
|
|
|
|
<script>
|
|
// Tweaks wiring — accent hue swatches
|
|
const ACCENTS = {
|
|
ember: {h:50, c:0.17, label:'Ember'},
|
|
signal: {h:25, c:0.19, label:'Signal'},
|
|
cobalt: {h:245, c:0.15, label:'Cobalt'},
|
|
moss: {h:145, c:0.11, label:'Moss'},
|
|
ink: {h:280, c:0.04, label:'Graphite'},
|
|
};
|
|
const accentWrap = document.getElementById('tw-accent');
|
|
Object.entries(ACCENTS).forEach(([k,v])=>{
|
|
const b = document.createElement('button');
|
|
b.style.background = `oklch(0.68 ${v.c} ${v.h})`;
|
|
b.title = v.label;
|
|
b.dataset.v = k;
|
|
b.addEventListener('click',()=>applyAccent(k));
|
|
accentWrap.appendChild(b);
|
|
});
|
|
function applyAccent(k){
|
|
const v = ACCENTS[k];
|
|
document.documentElement.style.setProperty('--ember', `oklch(0.68 ${v.c} ${v.h})`);
|
|
document.documentElement.style.setProperty('--ember-tint', `oklch(0.95 ${v.c*0.3} ${v.h})`);
|
|
document.documentElement.style.setProperty('--ember-ink', `oklch(0.42 ${v.c*0.9} ${v.h-10})`);
|
|
[...accentWrap.children].forEach(b=>b.classList.toggle('on',b.dataset.v===k));
|
|
post({accent:k});
|
|
}
|
|
applyAccent('ember');
|
|
|
|
document.querySelectorAll('#tw-density .tweak-opt').forEach(b=>{
|
|
b.addEventListener('click',()=>{
|
|
document.querySelectorAll('#tw-density .tweak-opt').forEach(x=>x.classList.remove('on'));
|
|
b.classList.add('on');
|
|
document.documentElement.dataset.density = b.dataset.v;
|
|
post({density:b.dataset.v});
|
|
});
|
|
});
|
|
document.querySelectorAll('#tw-surface .tweak-opt').forEach(b=>{
|
|
b.addEventListener('click',()=>{
|
|
document.querySelectorAll('#tw-surface .tweak-opt').forEach(x=>x.classList.remove('on'));
|
|
b.classList.add('on');
|
|
applySurface(b.dataset.v);
|
|
post({surface:b.dataset.v});
|
|
});
|
|
});
|
|
function applySurface(v){
|
|
const s = document.documentElement.style;
|
|
if(v==='paper'){ s.setProperty('--paper','#FAFAF7'); s.setProperty('--paper-2','#F3F1EB'); s.setProperty('--card','#FFFFFF'); s.setProperty('--ink','#0F0F10'); }
|
|
if(v==='white'){ s.setProperty('--paper','#FFFFFF'); s.setProperty('--paper-2','#F6F6F6'); s.setProperty('--card','#FFFFFF'); s.setProperty('--ink','#0F0F10'); }
|
|
if(v==='ink'){ s.setProperty('--paper','#0E0E10'); s.setProperty('--paper-2','#17171A'); s.setProperty('--card','#1C1C20'); s.setProperty('--ink','#F4F2EC'); s.setProperty('--slate','#C2C0BA'); s.setProperty('--steel','#8A8883'); s.setProperty('--rule','#2B2B2F'); s.setProperty('--rule-soft','#232327'); }
|
|
}
|
|
|
|
// Edit mode protocol
|
|
window.addEventListener('message',(e)=>{
|
|
if(e.data?.type==='__activate_edit_mode') document.getElementById('tweaksPanel').classList.add('on');
|
|
if(e.data?.type==='__deactivate_edit_mode') document.getElementById('tweaksPanel').classList.remove('on');
|
|
});
|
|
function post(edits){
|
|
try{ window.parent.postMessage({type:'__edit_mode_set_keys',edits},'*'); }catch(e){}
|
|
}
|
|
try{ window.parent.postMessage({type:'__edit_mode_available'},'*'); }catch(e){}
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|