Coworking — Equipment Status
A warm industrial equipment status board for a coworking studio, showing printers, scanners, an espresso machine and AV gear as live device cards. Each card carries an online, in-use, low-supply or needs-help state, supply meters, member-avatar print queues and a report-issue flow. Refresh simulates live status drift across the floor, queues can be cleared job by job, and toast confirmations keep operators informed in a clean, accessible layout.
MCP
Code
:root {
--concrete: #efeae3;
--concrete-d: #e2dcd2;
--amber: #e8902b;
--amber-d: #cc7918;
--amber-50: #fdf1e2;
--char: #1c1b19;
--ink: #26241f;
--ink-2: #4a463e;
--muted: #7b766c;
--bg: #f6f3ee;
--surface: #ffffff;
--plant: #5f7a52;
--line: rgba(28, 27, 25, 0.1);
--line-2: rgba(28, 27, 25, 0.18);
--ok: #2f9e6f;
--warn: #d98a2b;
--danger: #d4503e;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 20px;
--sh-1: 0 1px 2px rgba(28, 27, 25, 0.06), 0 6px 18px rgba(28, 27, 25, 0.05);
--sh-2: 0 8px 28px rgba(28, 27, 25, 0.12);
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
background: var(--bg);
color: var(--ink);
font-family: "Inter", system-ui, -apple-system, sans-serif;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.app {
max-width: 1080px;
margin: 0 auto;
padding: 22px 20px 56px;
}
/* Topbar */
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
flex-wrap: wrap;
padding-bottom: 18px;
border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: center; gap: 12px; }
.brand-mark {
width: 42px;
height: 42px;
display: grid;
place-items: center;
border-radius: var(--r-md);
background: var(--char);
color: var(--amber);
font-size: 22px;
box-shadow: var(--sh-1);
}
.brand-text { display: flex; flex-direction: column; line-height: 1.25; }
.brand-text strong { font-size: 17px; font-weight: 800; letter-spacing: -0.01em; color: var(--char); }
.brand-text span { font-size: 12.5px; color: var(--muted); }
.topbar-right { display: flex; align-items: center; gap: 12px; }
.updated { font-size: 12.5px; color: var(--muted); }
.btn {
font: inherit;
font-weight: 600;
font-size: 13.5px;
border: 1px solid var(--line-2);
background: var(--surface);
color: var(--ink);
padding: 9px 14px;
border-radius: 999px;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 7px;
transition: transform 0.12s ease, background 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}
.btn:hover { background: var(--concrete); border-color: var(--char); }
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
.btn-amber { background: var(--amber); border-color: var(--amber-d); color: #2a1c08; }
.btn-amber:hover { background: var(--amber-d); border-color: var(--amber-d); color: #fff; }
.btn-ghost { background: var(--surface); }
.spin-icon { display: inline-block; font-size: 15px; }
.is-spinning .spin-icon { animation: spin 0.7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
/* Summary */
.summary {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
margin: 18px 0 22px;
}
.stat {
display: flex;
align-items: center;
gap: 11px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 13px 15px;
box-shadow: var(--sh-1);
}
.stat strong { display: block; font-size: 21px; font-weight: 800; color: var(--char); line-height: 1.1; }
.stat span { font-size: 12px; color: var(--muted); }
.stat-dot {
width: 11px; height: 11px; border-radius: 50%; flex: none;
box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.04);
}
.is-online { background: var(--ok); }
.is-busy { background: var(--amber); }
.is-low { background: var(--warn); }
.is-error { background: var(--danger); }
/* Grid */
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(310px, 1fr));
gap: 16px;
}
.card {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 16px;
box-shadow: var(--sh-1);
display: flex;
flex-direction: column;
gap: 14px;
transition: box-shadow 0.18s ease, transform 0.18s ease, border-color 0.18s ease;
}
.card:hover { box-shadow: var(--sh-2); transform: translateY(-2px); }
.card[data-status="error"] { border-color: rgba(212, 80, 62, 0.4); }
.card-top { display: flex; align-items: flex-start; gap: 13px; }
.thumb {
width: 54px; height: 54px; border-radius: var(--r-md); flex: none;
display: grid; place-items: center;
font-size: 24px;
color: #fff;
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.25);
}
.thumb.t-print { background: linear-gradient(135deg, #46403a, #6e6359); }
.thumb.t-scan { background: linear-gradient(135deg, #3c5a6e, #5f7a8c); }
.thumb.t-coffee { background: linear-gradient(135deg, #6e4a32, #a16b3f); }
.thumb.t-av { background: linear-gradient(135deg, #4a4063, #6b5f8c); }
.card-id { flex: 1; min-width: 0; }
.card-id h3 { margin: 0; font-size: 15.5px; font-weight: 700; color: var(--char); letter-spacing: -0.01em; }
.card-id p { margin: 2px 0 0; font-size: 12.5px; color: var(--muted); }
.pill {
align-self: flex-start;
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 11.5px;
font-weight: 700;
padding: 5px 10px;
border-radius: 999px;
letter-spacing: 0.01em;
white-space: nowrap;
}
.pill .led { width: 7px; height: 7px; border-radius: 50%; }
.pill .led::after { content: ""; }
.s-online { background: rgba(47, 158, 111, 0.12); color: #1d6b4b; }
.s-online .led { background: var(--ok); box-shadow: 0 0 0 3px rgba(47, 158, 111, 0.18); }
.s-busy { background: rgba(232, 144, 43, 0.14); color: #9a5e12; }
.s-busy .led { background: var(--amber); }
.s-low { background: rgba(217, 138, 43, 0.14); color: #91591a; }
.s-low .led { background: var(--warn); }
.s-error { background: rgba(212, 80, 62, 0.14); color: #a8392b; }
.s-error .led { background: var(--danger); animation: blink 1s ease-in-out infinite; }
@keyframes blink { 50% { opacity: 0.35; } }
/* Meter */
.meter-row { display: flex; flex-direction: column; gap: 6px; }
.meter-label { display: flex; justify-content: space-between; font-size: 11.5px; color: var(--ink-2); }
.meter-label strong { color: var(--char); font-weight: 700; }
.meter {
height: 8px; border-radius: 999px; background: var(--concrete-d); overflow: hidden;
}
.meter > i {
display: block; height: 100%; border-radius: 999px;
background: var(--plant);
transition: width 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
.meter.warn > i { background: var(--warn); }
.meter.bad > i { background: var(--danger); }
/* Queue */
.queue { display: flex; flex-direction: column; gap: 8px; }
.queue-head {
display: flex; justify-content: space-between; align-items: center;
font-size: 12px; font-weight: 700; color: var(--ink-2);
}
.queue-head .count {
background: var(--concrete); border-radius: 999px; padding: 2px 9px;
font-size: 11px; color: var(--ink-2);
}
.queue ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.queue li {
display: flex; align-items: center; gap: 9px;
background: var(--concrete);
border-radius: var(--r-sm);
padding: 7px 10px;
font-size: 12.5px;
}
.queue li.done { opacity: 0.5; text-decoration: line-through; }
.q-av {
width: 22px; height: 22px; border-radius: 50%; flex: none;
display: grid; place-items: center; color: #fff; font-size: 10px; font-weight: 700;
}
.q-name { flex: 1; color: var(--ink); font-weight: 600; }
.q-name small { display: block; font-weight: 500; color: var(--muted); font-size: 10.5px; }
.q-pages { color: var(--muted); font-size: 11.5px; white-space: nowrap; }
.queue .empty { color: var(--muted); font-size: 12.5px; padding: 4px 2px; }
.note-low {
font-size: 12px; color: #91591a; background: var(--amber-50);
border: 1px solid rgba(217, 138, 43, 0.25);
border-radius: var(--r-sm); padding: 8px 10px;
}
/* Card actions */
.card-actions { display: flex; gap: 8px; margin-top: auto; }
.card-actions .btn { flex: 1; justify-content: center; padding: 8px 10px; font-size: 12.5px; }
/* Modal */
.modal { position: fixed; inset: 0; z-index: 40; display: grid; place-items: center; padding: 18px; }
.modal[hidden] { display: none; }
.modal-backdrop { position: absolute; inset: 0; background: rgba(28, 27, 25, 0.42); backdrop-filter: blur(2px); }
.modal-card {
position: relative;
width: min(440px, 100%);
background: var(--surface);
border-radius: var(--r-lg);
padding: 20px;
box-shadow: var(--sh-2);
border: 1px solid var(--line);
animation: pop 0.18s ease;
}
@keyframes pop { from { transform: translateY(8px) scale(0.98); opacity: 0; } }
.modal-head { display: flex; justify-content: space-between; align-items: center; }
.modal-head h2 { margin: 0; font-size: 17px; font-weight: 800; color: var(--char); }
.icon-btn {
border: none; background: var(--concrete); color: var(--ink-2);
width: 30px; height: 30px; border-radius: 50%; cursor: pointer; font-size: 13px;
}
.icon-btn:hover { background: var(--concrete-d); }
.modal-device { margin: 6px 0 14px; font-size: 13px; color: var(--muted); }
.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 13px; }
.field > span { font-size: 12.5px; font-weight: 600; color: var(--ink-2); }
.field em { color: var(--muted); font-weight: 400; font-style: normal; }
.field select, .field textarea {
font: inherit; font-size: 13.5px;
padding: 9px 11px;
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
background: var(--bg);
color: var(--ink);
resize: vertical;
}
.field select:focus, .field textarea:focus { outline: 2px solid var(--amber); outline-offset: 1px; border-color: var(--amber); }
.modal-actions { display: flex; justify-content: flex-end; gap: 9px; margin-top: 4px; }
/* Toast */
.toast-wrap {
position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%);
z-index: 60; display: flex; flex-direction: column; gap: 8px; align-items: center;
pointer-events: none;
}
.toast {
background: var(--char); color: #fff;
padding: 11px 16px; border-radius: 999px;
font-size: 13px; font-weight: 600;
box-shadow: var(--sh-2);
display: flex; align-items: center; gap: 9px;
animation: toastin 0.22s ease;
}
.toast .tled { width: 8px; height: 8px; border-radius: 50%; background: var(--ok); }
.toast.warn .tled { background: var(--warn); }
.toast.bad .tled { background: var(--danger); }
@keyframes toastin { from { transform: translateY(10px); opacity: 0; } }
.toast.out { animation: toastout 0.25s ease forwards; }
@keyframes toastout { to { transform: translateY(8px); opacity: 0; } }
.foot {
margin-top: 28px; text-align: center; font-size: 12px; color: var(--muted);
display: flex; align-items: center; justify-content: center; gap: 7px;
}
.foot .plant { color: var(--plant); font-size: 15px; }
@media (max-width: 520px) {
.app { padding: 16px 14px 44px; }
.summary { grid-template-columns: repeat(2, 1fr); }
.grid { grid-template-columns: 1fr; }
.topbar-right { width: 100%; justify-content: space-between; }
.brand-text span { font-size: 12px; }
}(function () {
"use strict";
var AVATAR_COLORS = ["#5f7a52", "#e8902b", "#3c5a6e", "#a16b3f", "#6b5f8c", "#a8392b"];
function initials(name) {
return name.split(" ").map(function (w) { return w[0]; }).join("").slice(0, 2).toUpperCase();
}
function avColor(name) {
var s = 0;
for (var i = 0; i < name.length; i++) s += name.charCodeAt(i);
return AVATAR_COLORS[s % AVATAR_COLORS.length];
}
// --- Seed data: fictional Loomyard studio equipment ---
var devices = [
{
id: "px-01", name: "RICOH IM C3010", type: "print", kind: "Print · Scan · Copy",
icon: "🖨", status: "online",
supplies: [{ label: "Toner (black)", pct: 74 }, { label: "Paper · Tray 1", pct: 88 }],
queue: [
{ name: "Mara Velez", doc: "Q3-pitch-deck.pdf", pages: 12, done: false },
{ name: "Theo Nakamura", doc: "lease-addendum.docx", pages: 3, done: false }
]
},
{
id: "px-02", name: "Brother HL-L8360", type: "print", kind: "Color laser",
icon: "🖨", status: "low",
supplies: [{ label: "Toner (cyan)", pct: 9 }, { label: "Paper · Tray 2", pct: 41 }],
queue: [{ name: "Priya Anand", doc: "moodboard-A3.pdf", pages: 6, done: false }]
},
{
id: "sc-01", name: "Fujitsu fi-8170", type: "scan", kind: "Document scanner",
icon: "🗂", status: "busy",
supplies: [{ label: "Roller life", pct: 63 }],
queue: [{ name: "Devon Hart", doc: "receipts-batch", pages: 40, done: false }]
},
{
id: "cf-01", name: "La Marzocco Linea", type: "coffee", kind: "Espresso · Studio bar",
icon: "☕", status: "online",
supplies: [{ label: "Bean hopper", pct: 56 }, { label: "Water tank", pct: 82 }],
queue: []
},
{
id: "av-01", name: "Epson PowerLite — Loft", type: "av", kind: "Projector · Room Loft",
icon: "📽", status: "error",
supplies: [{ label: "Lamp hours", pct: 18 }],
queue: [],
issue: "No signal on HDMI 1"
},
{
id: "av-02", name: "Poly Studio X52 — Atrium", type: "av", kind: "Conf cam · Room Atrium",
icon: "🎥", status: "busy",
supplies: [{ label: "Firmware", pct: 100 }],
queue: []
}
];
var STATUS_META = {
online: { cls: "s-online", label: "Online" },
busy: { cls: "s-busy", label: "In use" },
low: { cls: "s-low", label: "Low supply" },
error: { cls: "s-error", label: "Needs help" }
};
var grid = document.getElementById("deviceGrid");
var modal = document.getElementById("modal");
var activeDeviceId = null;
function meterClass(pct) { return pct <= 15 ? "bad" : pct <= 40 ? "warn" : ""; }
function render() {
grid.innerHTML = "";
devices.forEach(function (d) {
grid.appendChild(buildCard(d));
});
updateSummary();
}
function buildCard(d) {
var meta = STATUS_META[d.status];
var card = document.createElement("article");
card.className = "card";
card.dataset.status = d.status;
card.dataset.id = d.id;
var queueCount = d.queue.filter(function (q) { return !q.done; }).length;
var suppliesHtml = d.supplies.map(function (s) {
return '<div class="meter-row">' +
'<div class="meter-label"><span>' + s.label + '</span><strong>' + s.pct + '%</strong></div>' +
'<div class="meter ' + meterClass(s.pct) + '"><i style="width:' + s.pct + '%"></i></div>' +
'</div>';
}).join("");
var queueHtml;
if (d.type === "print" || d.type === "scan") {
var items = d.queue.length
? '<ul>' + d.queue.map(function (q) {
return '<li class="' + (q.done ? "done" : "") + '">' +
'<span class="q-av" style="background:' + avColor(q.name) + '">' + initials(q.name) + '</span>' +
'<span class="q-name">' + q.name + '<small>' + q.doc + '</small></span>' +
'<span class="q-pages">' + q.pages + 'p</span></li>';
}).join("") + '</ul>'
: '<div class="empty">Queue is clear ✓</div>';
queueHtml = '<div class="queue">' +
'<div class="queue-head"><span>Print queue</span>' +
'<span class="count">' + queueCount + ' waiting</span></div>' + items + '</div>';
} else if (d.issue) {
queueHtml = '<div class="note-low">⚠ ' + d.issue + '</div>';
} else if (d.status === "low") {
queueHtml = '<div class="note-low">Refill needed soon — flagged to ops.</div>';
} else {
queueHtml = "";
}
var clearBtn = (d.type === "print" || d.type === "scan") && queueCount > 0
? '<button class="btn btn-ghost" data-act="next">Mark next done</button>'
: '';
card.innerHTML =
'<div class="card-top">' +
'<div class="thumb t-' + d.type + '">' + d.icon + '</div>' +
'<div class="card-id"><h3>' + d.name + '</h3><p>' + d.kind + ' · ' + d.id.toUpperCase() + '</p></div>' +
'</div>' +
'<span class="pill ' + meta.cls + '"><span class="led"></span>' + meta.label + '</span>' +
suppliesHtml +
queueHtml +
'<div class="card-actions">' +
clearBtn +
'<button class="btn btn-ghost" data-act="report">Report issue</button>' +
'</div>';
card.querySelectorAll("[data-act]").forEach(function (btn) {
btn.addEventListener("click", function () {
var act = btn.dataset.act;
if (act === "report") openModal(d);
else if (act === "next") markNext(d);
});
});
return card;
}
function updateSummary() {
var c = { online: 0, busy: 0, low: 0, error: 0 };
devices.forEach(function (d) { c[d.status]++; });
document.getElementById("cOnline").textContent = c.online;
document.getElementById("cBusy").textContent = c.busy;
document.getElementById("cLow").textContent = c.low;
document.getElementById("cError").textContent = c.error;
}
function markNext(d) {
var next = d.queue.find(function (q) { return !q.done; });
if (!next) return;
next.done = true;
var remaining = d.queue.filter(function (q) { return !q.done; }).length;
if (remaining === 0 && d.status === "busy") d.status = "online";
render();
toast(next.doc + " finished on " + d.name, "ok");
}
// --- Modal ---
function openModal(d) {
activeDeviceId = d.id;
document.getElementById("modalDevice").textContent = d.name + " · " + d.id.toUpperCase();
document.getElementById("issueNotes").value = "";
document.getElementById("issueType").selectedIndex = 0;
modal.hidden = false;
document.getElementById("issueType").focus();
document.addEventListener("keydown", onEsc);
}
function closeModal() {
modal.hidden = true;
activeDeviceId = null;
document.removeEventListener("keydown", onEsc);
}
function onEsc(e) { if (e.key === "Escape") closeModal(); }
modal.querySelectorAll("[data-close]").forEach(function (el) {
el.addEventListener("click", closeModal);
});
document.getElementById("submitIssue").addEventListener("click", function () {
var d = devices.find(function (x) { return x.id === activeDeviceId; });
var type = document.getElementById("issueType").value;
if (d && d.status !== "error") {
d.status = "error";
d.issue = type;
}
closeModal();
render();
toast("Issue reported — ops notified", "warn");
});
// --- Refresh: simulate live status drift ---
var refreshBtn = document.getElementById("refreshBtn");
refreshBtn.addEventListener("click", function () {
if (refreshBtn.classList.contains("is-spinning")) return;
refreshBtn.classList.add("is-spinning");
setTimeout(function () {
devices.forEach(function (d) {
// supplies drift slightly down
d.supplies.forEach(function (s) {
if (s.label.indexOf("Firmware") === -1) {
s.pct = Math.max(0, Math.min(100, s.pct + (Math.random() < 0.6 ? -1 : 1) * Math.floor(Math.random() * 5)));
}
});
// a busy scanner/printer may free up
if (d.status === "busy" && Math.random() < 0.4) {
d.queue.forEach(function (q) { q.done = true; });
d.status = "online";
}
// low supply auto-flag from meters
if (d.status !== "error") {
var critical = d.supplies.some(function (s) { return s.pct <= 12; });
if (critical && d.status !== "low") d.status = "low";
}
});
render();
stamp();
refreshBtn.classList.remove("is-spinning");
toast("Status synced across the floor", "ok");
}, 650);
});
function stamp() {
var now = new Date();
var hh = String(now.getHours()).padStart(2, "0");
var mm = String(now.getMinutes()).padStart(2, "0");
document.getElementById("updated").textContent = "Updated " + hh + ":" + mm;
}
// --- Toast helper ---
var toastWrap = document.getElementById("toastWrap");
function toast(msg, kind) {
var t = document.createElement("div");
t.className = "toast" + (kind === "warn" ? " warn" : kind === "bad" ? " bad" : "");
t.innerHTML = '<span class="tled"></span>' + msg;
toastWrap.appendChild(t);
setTimeout(function () {
t.classList.add("out");
setTimeout(function () { t.remove(); }, 250);
}, 2600);
}
render();
stamp();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Loomyard — Equipment Status</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;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="app">
<header class="topbar">
<div class="brand">
<span class="brand-mark" aria-hidden="true">◳</span>
<div class="brand-text">
<strong>Loomyard</strong>
<span>Equipment Status · 3rd Floor Studio</span>
</div>
</div>
<div class="topbar-right">
<span class="updated" id="updated">Updated just now</span>
<button class="btn btn-ghost" id="refreshBtn" type="button">
<span class="spin-icon" aria-hidden="true">⟳</span> Refresh
</button>
</div>
</header>
<section class="summary" aria-label="Fleet summary">
<div class="stat">
<span class="stat-dot is-online" aria-hidden="true"></span>
<div><strong id="cOnline">0</strong><span>Online</span></div>
</div>
<div class="stat">
<span class="stat-dot is-busy" aria-hidden="true"></span>
<div><strong id="cBusy">0</strong><span>Busy</span></div>
</div>
<div class="stat">
<span class="stat-dot is-low" aria-hidden="true"></span>
<div><strong id="cLow">0</strong><span>Low supply</span></div>
</div>
<div class="stat">
<span class="stat-dot is-error" aria-hidden="true"></span>
<div><strong id="cError">0</strong><span>Needs help</span></div>
</div>
</section>
<main class="grid" id="deviceGrid" aria-label="Equipment cards"></main>
<footer class="foot">
<span class="plant" aria-hidden="true">❧</span>
Illustrative status board · fictional space — not a real booking system.
</footer>
</div>
<!-- Report issue modal -->
<div class="modal" id="modal" hidden>
<div class="modal-backdrop" data-close></div>
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="modalTitle">
<header class="modal-head">
<h2 id="modalTitle">Report an issue</h2>
<button class="icon-btn" id="modalClose" data-close type="button" aria-label="Close">✕</button>
</header>
<p class="modal-device" id="modalDevice">Device</p>
<label class="field">
<span>What's wrong?</span>
<select id="issueType">
<option value="Paper jam">Paper jam</option>
<option value="Out of toner / ink">Out of toner / ink</option>
<option value="Won't power on">Won't power on</option>
<option value="Bad output quality">Bad output quality</option>
<option value="Connectivity / offline">Connectivity / offline</option>
<option value="Other">Other</option>
</select>
</label>
<label class="field">
<span>Notes <em>(optional)</em></span>
<textarea id="issueNotes" rows="3" placeholder="Tray 2 keeps jamming on A4…"></textarea>
</label>
<div class="modal-actions">
<button class="btn btn-ghost" data-close type="button">Cancel</button>
<button class="btn btn-amber" id="submitIssue" type="button">Send report</button>
</div>
</div>
</div>
<div class="toast-wrap" id="toastWrap" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Equipment Status
A floor-level status board for the Loomyard studio’s shared equipment. A summary strip counts how many devices are online, busy, low on supply or asking for help, then a responsive grid of device cards lays out each printer, scanner, espresso machine and AV unit. Cards use the warm-concrete and amber palette with color-coded status pills — a steady green LED for online, amber for in-use, and a blinking red for anything that needs attention.
Every card surfaces what an operator actually needs: supply meters that turn warn-amber or danger-red as toner, paper, lamp hours or water run down, plus a live print queue with member avatars, document names and page counts. Printers and scanners let you mark the next job done, which clears it from the queue and flips a busy device back to online.
The Report issue button opens an accessible modal (focus moves in, Escape closes) where you pick a fault and add notes; submitting flags the device as needs-help and notifies ops. Refresh simulates live drift — supplies tick down, busy machines free up, and critically low devices auto-flag — with toast confirmations after each action. Everything is vanilla JS, keyboard-usable and responsive down to ~360px.
Illustrative UI only — fictional coworking space, not a real booking system.