lxc-keyreg/public/styles.css
2026-08-24 15:21:24 +01:00

515 lines
28 KiB
CSS

:root{
--ink:#131E1B; --ink-2:#20302B; --ink-3:#2C3F39;
--slate:#5C6B65; --slate-2:#8A968F; --line:#DCDFD9;
--paper:#F4F5F2; --card:#FFFFFF;
--verdigris:#2E6E60; --verdigris-soft:#E4EFEB;
--brass:#A87F3C; --brass-soft:#F6EEDF;
--violet:#5F55A0; --steel:#54646D; --steel-soft:#E9EDEF;
--late:#AE4034; --late-soft:#F8E9E7; --r:10px;
}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;padding:0}
body{background:var(--paper);color:var(--ink);font-family:"IBM Plex Sans",system-ui,sans-serif;
font-size:15px;line-height:1.45;-webkit-font-smoothing:antialiased}
button{font:inherit;color:inherit;border:0;background:none;cursor:pointer}
textarea,input,select{font:inherit}
:focus-visible{outline:2px solid var(--verdigris);outline-offset:2px;border-radius:4px}
.app{max-width:560px;margin:0 auto;min-height:100vh;display:flex;flex-direction:column}
header{background:var(--ink);color:#fff;padding:16px 18px 14px;position:relative}
.eyebrow{font-family:"IBM Plex Mono",monospace;font-size:10px;letter-spacing:.18em;
text-transform:uppercase;color:var(--slate-2);margin:0 0 3px}
h1{font-family:"IBM Plex Sans Condensed",sans-serif;font-weight:700;font-size:23px;margin:0;text-transform:uppercase}
/* One flex row of equal boxes. Positioning each icon with its own `right`
offset gave uneven gaps, because the glyphs are not the same width — and a
mix of emoji and text glyphs also renders at different sizes and colours.
Matched SVGs in fixed boxes solve both. */
.tools{position:absolute;top:12px;right:12px;display:flex;gap:2px}
.tool{
width:36px;height:36px;display:flex;align-items:center;justify-content:center;
border-radius:8px;color:#7A8A83;transition:color .12s,background .12s
}
.tool svg{
width:19px;height:19px;fill:none;stroke:currentColor;stroke-width:1.6;
stroke-linecap:round;stroke-linejoin:round
}
.tool:hover{color:#DDE4E0;background:rgba(255,255,255,.07)}
.tool:active{background:rgba(255,255,255,.12)}
/* the admin and report pages still use a single back/settings link */
.gear{position:absolute;top:14px;right:14px;color:#7A8A83;font-size:19px;padding:6px}
.counts{display:flex;gap:7px;margin-top:13px;flex-wrap:wrap}
.count{font-family:"IBM Plex Mono",monospace;font-size:11px;padding:5px 9px;border-radius:100px;
background:var(--ink-3);color:#D7DEDA;display:flex;align-items:center;gap:6px}
.count b{font-weight:600;color:#fff}
.count.warn{background:#43221D;color:#F0BDB6} .count.warn b{color:#fff}
.swatch{width:8px;height:8px;border-radius:50%}
.swatch.out{background:var(--brass)} .swatch.in{background:var(--verdigris)} .swatch.res{background:var(--steel)}
.scroll{flex:1;overflow-y:auto;padding-bottom:80px}
.board{background:var(--ink-2);padding:14px 14px 20px;min-height:60vh}
/* Cabinets wrap onto as many rows as they need. A horizontal scroller hides
cabinets behind the edge, on the one screen you look at most. */
.cabs{
display:grid;grid-template-columns:repeat(auto-fit,minmax(96px,1fr));
gap:6px;margin-bottom:11px
}
.cab{
padding:8px 10px;border-radius:8px;background:var(--ink);border:1px solid var(--ink-3);
text-align:left;min-width:0
}
.cab[aria-pressed="true"]{background:#fff;border-color:#fff}
.cab .cn{
display:block;font-family:"IBM Plex Sans Condensed",sans-serif;font-weight:700;
font-size:12.5px;text-transform:uppercase;color:#CBD5D0;letter-spacing:.01em;
white-space:nowrap;overflow:hidden;text-overflow:ellipsis
}
.cab[aria-pressed="true"] .cn{color:var(--ink)}
.cab .cs{
display:block;font-family:"IBM Plex Mono",monospace;font-size:9.5px;color:#7A8A83;
margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis
}
.cab[aria-pressed="true"] .cs{color:var(--slate)}
.cab .cs .o{color:var(--brass)}
/* wider screens fit more per row without the tiles becoming huge */
@media (min-width:460px){
.cabs{grid-template-columns:repeat(auto-fit,minmax(112px,1fr))}
.cab{padding:9px 12px}
.cab .cn{font-size:13px}
.cab .cs{font-size:10px}
}
.where{font-size:11.5px;color:#8A968F;margin:0 0 12px;padding-left:2px;line-height:1.5}
.filters{display:flex;gap:6px;margin-bottom:14px;flex-wrap:wrap}
.chip{font-size:12px;padding:6px 11px;border-radius:100px;background:transparent;color:#9FADA7;border:1px solid var(--ink-3)}
.chip[aria-pressed="true"]{background:#fff;color:var(--ink);border-color:#fff;font-weight:500}
.rail{margin-bottom:15px}
.rail-label{font-family:"IBM Plex Mono",monospace;font-size:10px;letter-spacing:.14em;text-transform:uppercase;
color:var(--slate-2);padding-bottom:6px;margin-bottom:9px;border-bottom:1px solid var(--ink-3);
display:flex;justify-content:space-between;align-items:baseline;gap:10px}
.rail-label span{color:#6E7D77;flex:none}
.pegs{display:grid;grid-template-columns:repeat(auto-fill,minmax(46px,1fr));gap:7px}
.peg{aspect-ratio:1;display:flex;flex-direction:column;align-items:center;justify-content:center;
font-family:"IBM Plex Mono",monospace;font-size:11.5px;font-weight:500;border:1.5px solid transparent;
position:relative;transition:transform .1s}
.peg:active{transform:scale(.93)}
/* shape = what it is, fill = where it is. Both now come from the type record
rather than being hardcoded for two kinds. */
.peg.shape-circle{border-radius:50%}
.peg.shape-rounded{border-radius:5px}
.peg.shape-square{border-radius:0}
.peg.st-in{background:var(--k,var(--verdigris));color:#fff;border-color:var(--k,var(--verdigris))}
.peg.st-reserve{background:#48585F;color:#C6D2D7;border-color:#5E7079}
.peg.st-out{background:transparent;color:var(--brass);border-color:var(--brass)}
.peg.st-lost,.peg.st-retired{background:transparent;color:#6E7D77;border-color:#4A5A54;border-style:dashed}
.peg.late{color:#E8907F;border-color:var(--late);border-width:2px}
.peg .flag{position:absolute;top:-2px;right:-2px;width:9px;height:9px;border-radius:50%;
background:var(--late);border:1.5px solid var(--ink-2)}
.peg .memo{position:absolute;bottom:-1px;right:2px;font-size:8px;opacity:.85}
.peg small{font-size:8px;opacity:.75;margin-top:1px}
.legend{display:flex;gap:13px;flex-wrap:wrap;margin-top:4px;font-family:"IBM Plex Mono",monospace;
font-size:10px;color:#8A968F}
.legend i{display:inline-block;width:11px;height:11px;margin-right:5px;vertical-align:-2px;border:1.5px solid}
.lg-in{background:var(--verdigris);border-color:var(--verdigris);border-radius:50%}
.lg-out{border-color:var(--brass);border-radius:50%}
.lg-res{background:#48585F;border-color:#5E7079;border-radius:50%}
.lg-late{border-color:var(--late);border-radius:50%;border-width:2px}
.lg-kind{border-radius:3px}
.pane{padding:14px}
.search,.field{width:100%;padding:11px 13px;border:1px solid var(--line);border-radius:var(--r);
background:var(--card);font-size:15px;margin-bottom:12px}
.search:focus,.field:focus{outline:none;border-color:var(--verdigris);box-shadow:0 0 0 3px var(--verdigris-soft)}
.row{display:flex;align-items:center;gap:12px;width:100%;text-align:left;background:var(--card);
border:1px solid var(--line);border-radius:var(--r);padding:11px 13px;margin-bottom:7px}
.row:active{background:#FAFBF9}
.row.late{border-color:#E4BDB6;background:#FEFAF9}
.badge{flex:none;width:42px;height:42px;display:flex;align-items:center;justify-content:center;
font-family:"IBM Plex Mono",monospace;font-size:11px;font-weight:600;border:1.5px solid}
.badge.shape-circle{border-radius:50%}
.badge.shape-rounded{border-radius:6px}
.badge.shape-square{border-radius:0}
.badge.st-in{background:var(--k,var(--verdigris));border-color:var(--k,var(--verdigris));color:#fff}
.badge.st-reserve{background:var(--steel);border-color:var(--steel);color:#fff}
.badge.st-out{border-color:var(--brass);color:var(--brass)}
.badge.st-lost,.badge.st-retired{border-color:#B9BEB8;color:#909790;border-style:dashed}
.badge.person{background:var(--paper);border-color:var(--line);color:var(--slate);border-radius:50%;
font-family:"IBM Plex Sans Condensed",sans-serif;font-size:14px}
.badge.door{background:var(--ink);border-color:var(--ink);color:#fff;border-radius:8px;
font-family:"IBM Plex Sans Condensed",sans-serif;font-size:15px}
.row-main{flex:1;min-width:0}
.row-t{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.row-s{font-size:12.5px;color:var(--slate);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.row-s .held{color:var(--brass);font-weight:500} .row-s .od{color:var(--late);font-weight:500}
.row-n{flex:none;font-family:"IBM Plex Mono",monospace;font-size:11px;color:var(--slate-2);
text-align:right;line-height:1.3}
.dots{display:flex;gap:3px;justify-content:flex-end;margin-top:3px;flex-wrap:wrap;max-width:70px}
.dot{width:9px;height:9px;border-radius:50%;border:1.5px solid}
.dot.in{background:var(--k,var(--verdigris));border-color:var(--k,var(--verdigris))}
.dot.out{border-color:var(--brass)}
.dot.reserve{background:var(--steel);border-color:var(--steel)}
.dot.lost,.dot.retired{border-color:#C3C8C2;border-style:dashed}
.sec{font-family:"IBM Plex Mono",monospace;font-size:10px;letter-spacing:.14em;text-transform:uppercase;
color:var(--slate);margin:18px 0 8px;padding-bottom:5px;border-bottom:1px solid var(--line)}
.sec:first-child{margin-top:0}
.empty{text-align:center;padding:32px 22px;color:var(--slate);font-size:14px;border:1px dashed var(--line);
border-radius:var(--r);background:var(--card)}
.log{background:var(--card);border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.log-row{display:flex;gap:11px;padding:11px 13px;border-bottom:1px solid var(--line);align-items:baseline}
.log-row:last-child{border-bottom:0}
.log-d{font-family:"IBM Plex Mono",monospace;font-size:10.5px;color:var(--slate-2);flex:none;width:52px}
.log-b{flex:1;font-size:13.5px;min-width:0}
.log-tag{font-family:"IBM Plex Mono",monospace;font-weight:500}
.memo-line{color:var(--slate);font-size:12px;font-style:italic;margin-top:2px}
.dir{font-family:"IBM Plex Mono",monospace;font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;
padding:2px 6px;border-radius:3px;flex:none}
.dir.out{background:var(--brass-soft);color:var(--brass)}
.dir.in{background:var(--verdigris-soft);color:var(--verdigris)}
.dir.late{background:var(--late-soft);color:var(--late)}
.scrim{position:fixed;inset:0;background:rgba(10,18,16,.5);z-index:40;opacity:0;pointer-events:none;transition:opacity .2s}
.scrim.on{opacity:1;pointer-events:auto}
.sheet{position:fixed;left:50%;bottom:0;transform:translate(-50%,100%);width:100%;max-width:560px;
max-height:90vh;overflow-y:auto;z-index:50;background:var(--paper);border-radius:16px 16px 0 0;
transition:transform .24s cubic-bezier(.32,.72,0,1);padding:0 16px calc(20px + env(safe-area-inset-bottom))}
.sheet.on{transform:translate(-50%,0)}
.grab{width:34px;height:4px;background:#C8CEC8;border-radius:3px;margin:9px auto 4px}
.sheet h2{font-family:"IBM Plex Sans Condensed",sans-serif;font-weight:700;font-size:24px;margin:8px 0 2px;text-transform:uppercase}
.sheet .sub{color:var(--slate);font-size:13.5px;margin:0 0 14px}
.status-line{padding:11px 13px;border-radius:var(--r);margin-bottom:14px;font-size:13.5px}
.status-line.out{background:var(--brass-soft);color:#6E5223}
.status-line.in{background:var(--verdigris-soft);color:#1D4A40}
.status-line.reserve{background:var(--steel-soft);color:#33454D}
.status-line.late{background:var(--late-soft);color:#7A2C23}
.status-line.lost,.status-line.retired{background:#F0F0EE;color:var(--slate)}
.status-line b{font-weight:600}
.facts{background:var(--card);border:1px solid var(--line);border-radius:var(--r);margin-bottom:14px}
.fact{display:flex;justify-content:space-between;gap:14px;padding:9px 13px;border-bottom:1px solid var(--line);font-size:13.5px}
.fact:last-child{border-bottom:0}
.fact dt{color:var(--slate);margin:0;flex:none} .fact dd{margin:0;text-align:right;font-weight:500;min-width:0}
.mono{font-family:"IBM Plex Mono",monospace;font-size:12.5px}
.tiny{display:block;font-weight:400;font-size:11.5px;color:var(--slate-2)}
.actions{display:flex;gap:8px;margin-bottom:16px}
.btn{flex:1;padding:13px;border-radius:var(--r);font-weight:600;font-size:15px;text-align:center}
.btn:active{filter:brightness(.92)} .btn:disabled{opacity:.45}
.btn-p{background:var(--verdigris);color:#fff}
.btn-o{background:var(--brass);color:#fff}
.btn-d{background:var(--late);color:#fff}
.btn-g{background:var(--card);color:var(--ink);border:1px solid var(--line)}
.note-box{background:var(--card);border:1px solid var(--line);border-radius:var(--r);padding:11px 13px;margin-bottom:14px}
.note-box textarea{width:100%;border:0;resize:vertical;min-height:52px;font-size:14px;background:transparent}
.note-box textarea:focus{outline:none}
.note-box .cap{font-family:"IBM Plex Mono",monospace;font-size:9.5px;letter-spacing:.12em;
text-transform:uppercase;color:var(--slate-2);margin-bottom:6px;display:block}
.mini{padding:8px 14px;border-radius:8px;font-size:13px;font-weight:600;background:var(--ink);color:#fff}
.sigpad{background:#fff;border:1px solid var(--line);border-radius:var(--r);margin-bottom:8px;position:relative;overflow:hidden}
.sigpad canvas{display:block;width:100%;height:150px;touch-action:none}
.sigpad .hint{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:#B7BEB8;font-size:13px;pointer-events:none}
.sigline{position:absolute;left:16px;right:16px;bottom:30px;border-bottom:1px dashed #D4D9D3;pointer-events:none}
.sigshot{background:#fff;border:1px solid var(--line);border-radius:8px;padding:5px;margin-top:6px}
.sigshot img{display:block;width:100%;max-width:220px}
.due-opts{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:14px}
.due{padding:8px 12px;border-radius:100px;border:1px solid var(--line);background:var(--card);font-size:13px;color:var(--slate)}
.due[aria-pressed="true"]{background:var(--ink);color:#fff;border-color:var(--ink);font-weight:500}
.tabs{position:fixed;bottom:0;left:50%;transform:translateX(-50%);width:100%;max-width:560px;
background:rgba(255,255,255,.94);backdrop-filter:blur(12px);border-top:1px solid var(--line);
display:flex;z-index:30;padding-bottom:env(safe-area-inset-bottom)}
.tab{flex:1;padding:9px 2px 11px;text-align:center;color:var(--slate-2);font-size:10px;font-weight:500}
.tab .ic{display:flex;align-items:center;justify-content:center;margin-bottom:3px}
.tab .ic svg{
width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.6;
stroke-linecap:round;stroke-linejoin:round
}
.tab[aria-selected="true"]{color:var(--verdigris)}
.note{font-size:11.5px;color:var(--slate);text-align:center;padding:14px 20px 4px;line-height:1.5}
.toast{position:fixed;left:50%;bottom:78px;transform:translate(-50%,20px);background:var(--ink);color:#fff;
padding:10px 18px;border-radius:100px;font-size:13.5px;z-index:60;opacity:0;pointer-events:none;
transition:opacity .2s,transform .2s;max-width:90vw;text-align:center}
.toast.on{opacity:1;transform:translate(-50%,0)}
.toast.err{background:var(--late)}
/* ---- admin ---- */
.admin{max-width:760px;margin:0 auto;padding-bottom:40px}
.adm-nav{display:flex;gap:6px;flex-wrap:wrap;padding:12px 14px 0}
.adm-nav button{padding:8px 13px;border-radius:100px;border:1px solid var(--line);
background:var(--card);font-size:13px;color:var(--slate)}
.adm-nav button[aria-pressed="true"]{background:var(--ink);color:#fff;border-color:var(--ink);font-weight:500}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.lab{display:block;font-family:"IBM Plex Mono",monospace;font-size:9.5px;letter-spacing:.12em;
text-transform:uppercase;color:var(--slate-2);margin-bottom:4px}
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r);padding:14px;margin-bottom:12px}
.card h3{font-family:"IBM Plex Sans Condensed",sans-serif;text-transform:uppercase;font-size:16px;margin:0 0 10px}
table{width:100%;border-collapse:collapse;font-size:13.5px}
th{text-align:left;font-family:"IBM Plex Mono",monospace;font-size:9.5px;letter-spacing:.1em;
text-transform:uppercase;color:var(--slate-2);padding:6px 8px;border-bottom:1px solid var(--line)}
td{padding:7px 8px;border-bottom:1px solid var(--line)}
tr:last-child td{border-bottom:0}
.warnbox{background:var(--late-soft);border:1px solid #E4BDB6;color:#7A2C23;
border-radius:var(--r);padding:12px 14px;margin-bottom:12px;font-size:13.5px}
@media (prefers-reduced-motion:reduce){*{transition:none!important}}
/* ---- login ---------------------------------------------------------------
Full-bleed dark, with the peg board itself as the backdrop — the same
motif the app opens onto, so the first screen already looks like the thing
it protects. */
.login{
min-height:100vh;min-height:100dvh;display:flex;align-items:center;justify-content:center;
padding:24px;background:var(--ink);position:relative;overflow:hidden
}
#pegfield{position:absolute;inset:0;width:100%;height:100%;opacity:.28;pointer-events:none}
.login-card{
position:relative;z-index:1;width:100%;max-width:330px;text-align:center
}
.login-mark{
width:54px;height:54px;margin:0 auto 18px;border-radius:50%;
border:2px solid var(--verdigris);display:flex;align-items:center;justify-content:center;
background:rgba(46,110,96,.14)
}
.login-mark svg{width:26px;height:26px;stroke:var(--verdigris);fill:none;stroke-width:1.9;
stroke-linecap:round;stroke-linejoin:round}
.login-card .eyebrow{margin-bottom:5px}
.login-card h1{color:#fff;font-size:26px;margin-bottom:8px}
.login-sub{color:#8A968F;font-size:13.5px;margin:0 0 22px}
.pw-wrap{position:relative;margin-bottom:10px}
.pw{
width:100%;padding:14px 46px 14px 16px;border-radius:var(--r);
background:var(--ink-2);border:1px solid var(--ink-3);color:#fff;font-size:16px;
font-family:"IBM Plex Mono",monospace;letter-spacing:.08em;text-align:center;
transition:border-color .15s,box-shadow .15s
}
.pw::placeholder{color:#5E6E68;letter-spacing:0;font-family:"IBM Plex Sans",sans-serif}
.pw:focus{outline:none;border-color:var(--verdigris);box-shadow:0 0 0 3px rgba(46,110,96,.25)}
.pw-toggle{
position:absolute;right:6px;top:50%;transform:translateY(-50%);
width:36px;height:36px;border-radius:8px;color:#7A8A83;font-size:15px;
display:flex;align-items:center;justify-content:center
}
.pw-toggle:hover{color:#B9C4BF}
.login .btn-p{width:100%;padding:14px}
.login-err{
color:#E8907F;font-size:13px;min-height:19px;margin:12px 0 0;
opacity:0;transition:opacity .15s
}
.login-err.on{opacity:1}
.shake{animation:shake .32s cubic-bezier(.36,.07,.19,.97)}
@keyframes shake{
10%,90%{transform:translateX(-2px)} 20%,80%{transform:translateX(4px)}
30%,50%,70%{transform:translateX(-7px)} 40%,60%{transform:translateX(7px)}
}
.login-foot{
position:absolute;bottom:22px;left:0;right:0;text-align:center;z-index:1;
color:#4E5E58;font-size:11px;font-family:"IBM Plex Mono",monospace;letter-spacing:.05em;
padding:0 24px
}
@media (prefers-reduced-motion:reduce){.shake{animation:none}}
/* ---- reports --------------------------------------------------------------
Screen styling stays close to the app; print styling drops to plain black
on white, because these end up in a folder or taped inside a cabinet. */
.report{max-width:900px;margin:0 auto;padding:0 18px 60px}
.rep-bar{
display:flex;gap:8px;flex-wrap:wrap;align-items:center;
padding:14px 0;position:sticky;top:0;background:var(--paper);z-index:5;
border-bottom:1px solid var(--line);margin-bottom:20px
}
.rep-bar select,.rep-bar .mini{margin:0}
.rep-bar select{
padding:9px 12px;border:1px solid var(--line);border-radius:8px;
background:var(--card);font-size:14px
}
.rep-head{margin-bottom:22px}
.rep-head h1{font-size:26px;margin:0 0 4px}
.rep-meta{
font-family:"IBM Plex Mono",monospace;font-size:11px;color:var(--slate);
letter-spacing:.03em
}
.rep-stats{display:flex;gap:20px;flex-wrap:wrap;margin:14px 0 0;padding:12px 0;
border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.rep-stat b{display:block;font-family:"IBM Plex Sans Condensed",sans-serif;
font-size:22px;line-height:1.1}
.rep-stat span{font-size:11px;color:var(--slate);text-transform:uppercase;letter-spacing:.08em;
font-family:"IBM Plex Mono",monospace}
.rep-group{margin-bottom:26px;break-inside:avoid}
.rep-group h2{
font-family:"IBM Plex Sans Condensed",sans-serif;font-size:17px;text-transform:uppercase;
margin:0 0 2px;padding-bottom:5px;border-bottom:2px solid var(--ink)
}
.rep-group .who{font-size:12.5px;color:var(--slate);margin:0 0 9px}
table.rep{width:100%;border-collapse:collapse;font-size:13px}
table.rep th{
text-align:left;font-family:"IBM Plex Mono",monospace;font-size:9.5px;letter-spacing:.1em;
text-transform:uppercase;color:var(--slate);padding:5px 7px;border-bottom:1px solid var(--ink)
}
table.rep td{padding:6px 7px;border-bottom:1px solid var(--line);vertical-align:top}
table.rep tr{break-inside:avoid}
table.rep .num{font-family:"IBM Plex Mono",monospace;font-weight:600;white-space:nowrap}
table.rep .od{color:var(--late);font-weight:600}
.tick{display:inline-block;width:13px;height:13px;border:1.4px solid #556;border-radius:3px}
.sig-block{
break-inside:avoid;border:1px solid var(--line);border-radius:8px;padding:10px;
margin-bottom:10px;background:var(--card)
}
.sig-block img{display:block;max-width:230px;max-height:80px}
.sig-cap{font-family:"IBM Plex Mono",monospace;font-size:10px;color:var(--slate);margin-bottom:6px}
.sign-off{margin-top:34px;display:flex;gap:40px;flex-wrap:wrap;break-inside:avoid}
.sign-off div{flex:1;min-width:200px}
.sign-line{border-bottom:1px solid var(--ink);height:38px;margin-bottom:5px}
.sign-lab{font-size:11px;color:var(--slate);font-family:"IBM Plex Mono",monospace}
@media print{
@page{size:A4;margin:14mm 12mm}
html,body{background:#fff!important;color:#000!important}
.rep-bar,.no-print{display:none!important}
.report{max-width:none;padding:0}
.rep-head h1{font-size:20px}
table.rep{font-size:10.5pt}
table.rep thead{display:table-header-group} /* repeat headers across pages */
.rep-group{page-break-inside:avoid}
a{text-decoration:none;color:#000}
.rep-stat b{font-size:16pt}
}
/* ---- access request form -------------------------------------------------
The only page an outsider may ever see, so it is calmer and plainer than
the rest of the app: no peg board, no counts, nothing about the building. */
.req-page{min-height:100vh;background:var(--paper)}
.req-top{background:var(--ink);color:#fff;padding:26px 20px 24px}
.req-top .inner{max-width:520px;margin:0 auto}
.req-top h1{color:#fff;font-size:24px;margin:2px 0 10px}
.req-top p{color:#9FADA7;font-size:13.5px;line-height:1.55;margin:0;max-width:46ch}
.req-wrap{max-width:520px;margin:0 auto;padding:20px 16px 60px}
.req-field{margin-bottom:16px}
.req-field .lab{margin-bottom:6px}
.req-field input[type=text],.req-field textarea{
width:100%;padding:12px 13px;border:1px solid var(--line);border-radius:var(--r);
background:var(--card);font-size:16px
}
.req-field textarea{min-height:86px;resize:vertical}
.req-field input:focus,.req-field textarea:focus{
outline:none;border-color:var(--verdigris);box-shadow:0 0 0 3px var(--verdigris-soft)
}
.req-hint{font-size:12px;color:var(--slate);margin:5px 0 0}
.areas{display:flex;flex-direction:column;gap:7px}
.area{
display:flex;align-items:center;gap:11px;padding:12px 13px;background:var(--card);
border:1px solid var(--line);border-radius:var(--r);cursor:pointer;font-size:15px
}
.area:has(input:checked){border-color:var(--verdigris);background:var(--verdigris-soft)}
.area input{width:19px;height:19px;accent-color:var(--verdigris);flex:none}
.pin-row{display:flex;gap:10px;align-items:flex-start}
.pin-row .req-field{flex:1;margin-bottom:0}
.req-msg{
padding:12px 14px;border-radius:var(--r);font-size:13.5px;margin-bottom:14px;display:none
}
.req-msg.on{display:block}
.req-msg.bad{background:var(--late-soft);color:#7A2C23}
.req-done{
text-align:center;padding:52px 20px;max-width:400px;margin:0 auto
}
.req-done .tickmark{
width:60px;height:60px;border-radius:50%;background:var(--verdigris-soft);
color:var(--verdigris);display:flex;align-items:center;justify-content:center;
font-size:30px;margin:0 auto 18px
}
.req-done h2{font-family:"IBM Plex Sans Condensed",sans-serif;text-transform:uppercase;
font-size:22px;margin:0 0 8px}
.req-done p{color:var(--slate);font-size:14px;line-height:1.6;margin:0 0 22px}
.req-foot{
text-align:center;font-size:11.5px;color:var(--slate-2);margin-top:26px;
font-family:"IBM Plex Mono",monospace
}
.hp{position:absolute;left:-9999px;width:1px;height:1px;opacity:0}
/* request inbox inside the app */
.req-card{
background:var(--card);border:1px solid var(--line);border-left:3px solid var(--brass);
border-radius:var(--r);padding:12px 13px;margin-bottom:8px
}
.req-card.done{border-left-color:var(--line);opacity:.62}
.req-card h4{margin:0 0 2px;font-size:15px;font-weight:600}
.req-card .meta{font-family:"IBM Plex Mono",monospace;font-size:10.5px;color:var(--slate-2)}
.req-card .areas-line{font-size:13px;color:var(--brass);font-weight:500;margin:6px 0 0}
.req-card .detail{font-size:13.5px;color:var(--slate);margin:6px 0 0;line-height:1.5}
.badge-count{
background:var(--brass);color:#fff;border-radius:100px;padding:1px 7px;
font-size:11px;font-weight:600;margin-left:7px;font-family:"IBM Plex Sans",sans-serif
}
/* ---- board fills its column, so the dark panel never stops mid-screen ---- */
.scroll{display:flex;flex-direction:column}
.board{flex:1 0 auto}
.board .note{color:#77857F}
/* ---------------------------------------------------------------------------
Desktop. Same markup, different shape: the bottom tab bar becomes a side
rail and the content gets the width it was already capable of using.
--------------------------------------------------------------------------- */
@media (min-width:900px){
body{background:var(--paper)}
.app{
max-width:1120px;min-height:100vh;
display:grid;
grid-template-columns:212px minmax(0,1fr);
grid-template-rows:auto minmax(0,1fr);
border-left:1px solid var(--line);border-right:1px solid var(--line)
}
header{grid-column:1 / -1;grid-row:1;padding:20px 26px 18px}
header h1{font-size:26px}
.tools{top:18px;right:18px}
.gear{top:20px}
/* side rail */
.tabs{
position:static;transform:none;grid-column:1;grid-row:2;
max-width:none;width:auto;flex-direction:column;align-items:stretch;
background:transparent;backdrop-filter:none;
border-top:0;border-right:1px solid var(--line);
padding:16px 12px 24px;gap:2px;
justify-content:flex-start;align-content:flex-start
}
.tab{
/* the mobile bar sets flex:1 so five tabs share the width; in a vertical
rail that makes each item stretch to fill the whole column height */
flex:0 0 auto;
display:flex;align-items:center;gap:11px;
padding:11px 13px;border-radius:8px;text-align:left;font-size:14px;
transition:background .12s
}
.tab:hover{background:#EDEFEA}
.tab .ic{margin:0;width:20px;flex:none}
.tab[aria-selected="true"]{background:var(--verdigris-soft);color:var(--verdigris);font-weight:600}
.scroll{grid-column:2;grid-row:2;padding-bottom:0;overflow-y:auto}
.board{padding:20px 22px 26px}
.pane{padding:20px 22px 40px;max-width:720px}
/* more pegs per row when there is room for them */
.pegs{grid-template-columns:repeat(auto-fill,minmax(52px,1fr));gap:8px}
.cabs{grid-template-columns:repeat(auto-fit,minmax(132px,1fr));gap:8px}
/* the bottom sheet becomes a centred dialog */
.sheet{
top:50%;bottom:auto;left:50%;
transform:translate(-50%,-46%) scale(.97);
width:min(560px,92vw);max-height:86vh;border-radius:14px;
box-shadow:0 24px 70px rgba(10,18,16,.32);
padding:0 22px 24px;opacity:0;transition:opacity .16s,transform .16s
}
.sheet.on{transform:translate(-50%,-50%) scale(1);opacity:1}
.grab{display:none}
.sheet h2{margin-top:20px}
.toast{bottom:26px}
/* wider forms and tables have room to breathe */
.admin{max-width:1000px}
.grid2{gap:14px}
}
@media (min-width:900px) and (prefers-reduced-motion:reduce){
.sheet{transition:none}
}
/* type swatch in the admin list */
.kind-chip{
display:inline-flex;align-items:center;gap:8px;padding:5px 10px;border:1px solid var(--line);
background:var(--card);font-size:13px;margin:0 6px 6px 0
}
.kind-dot{width:15px;height:15px;border:1.5px solid;flex:none}