/* Direction A, the look Simon chose. Tokens first, everything else derives from them. */
:root{color-scheme:light;--bar:69px;--danger-strong:#DC2626;--on-accent:#FFFFFF;--gap:24px;--narrow:680px;--ground:#F5F6F8;--surface:#FFFFFF;--line:#E4E7EC;--text:#15171C;--muted:#6C717C;--accent:#155E75;--accent-soft:#D7EEF6;--online:#17A34A;--online-soft:#E6F6EC;--warn:#C2410C;--warn-soft:#FFF1E8;--danger:#DC2626;--danger-soft:#FEE7E7;--shadow:0 1px 2px rgba(21,23,28,.05),0 8px 24px rgba(21,23,28,.06);--radius:14px;--font:"Instrument Sans",system-ui,-apple-system,"Segoe UI",sans-serif;--mono:"JetBrains Mono",ui-monospace,Menlo,Consolas,monospace}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){color-scheme:dark;--on-accent:#0F1115;--ground:#0F1115;--surface:#171A20;--line:#262A33;--text:#EDEFF3;--muted:#9AA0AB;--accent:#38BDF8;--accent-soft:#102A36;--online:#3CCB6E;--online-soft:#123321;--warn:#F59E5B;--warn-soft:#3A2214;--danger:#F87171;--danger-soft:#3B1A1A;--shadow:0 1px 2px rgba(0,0,0,.4),0 8px 24px rgba(0,0,0,.35)}}
:root[data-theme="dark"]{color-scheme:dark;--on-accent:#0F1115;--ground:#0F1115;--surface:#171A20;--line:#262A33;--text:#EDEFF3;--muted:#9AA0AB;--accent:#38BDF8;--accent-soft:#102A36;--online:#3CCB6E;--online-soft:#123321;--warn:#F59E5B;--warn-soft:#3A2214;--danger:#F87171;--danger-soft:#3B1A1A;--shadow:0 1px 2px rgba(0,0,0,.4),0 8px 24px rgba(0,0,0,.35)}
@font-face{font-family:"Instrument Sans";font-style:normal;font-weight:400 700;font-display:swap;src:url("fonts/instrument-sans.fa41c43a3d8e.woff2") format("woff2")}
@font-face{font-family:"JetBrains Mono";font-style:normal;font-weight:400 500;font-display:swap;src:url("fonts/jetbrains-mono.b636a65da4f0.woff2") format("woff2")}
*{box-sizing:border-box}html,body{margin:0}
body{background:var(--ground);color:var(--text);font:15px/1.5 var(--font);-webkit-font-smoothing:antialiased;min-height:100dvh}
/* What people type may be Arabic, inside our English sentences. A value printed in its own element keeps to itself,
   so a number, a date or a bracket next to a right to left name never jumps to its other side; a value that sits
   bare in a sentence is wrapped in <bdi> in the template. Nothing changes for left to right text. */
b,strong,a,span,td,th,dt,dd,label,option,summary,bdi{unicode-bidi:isolate}
a{color:inherit;text-decoration:none}button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
input,select,textarea{font:inherit;color:var(--text);background:var(--surface);border:1px solid var(--line);border-radius:10px;height:44px;padding:0 12px;font-size:16px;width:100%}textarea{height:auto;padding:10px 12px;min-height:88px}
input[type=checkbox],input[type=radio]{width:18px;height:18px;padding:0;flex:none;accent-color:var(--accent)}input[type=radio]{border-radius:50%}
input[type=search]{-webkit-appearance:none;appearance:none}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.app{display:grid;grid-template-columns:248px 1fr;min-height:100dvh}
.rail{background:var(--surface);border-right:1px solid var(--line);padding:20px 16px;display:flex;flex-direction:column;gap:20px;position:sticky;top:0;height:100dvh;overflow:hidden}.rail>*{flex:none}
.brand{display:flex;align-items:center;gap:10px;padding:4px 8px;font-weight:700;letter-spacing:.02em}.brand small{font-weight:600;color:var(--accent);font-size:11px;letter-spacing:.08em;text-transform:uppercase}
.brand .mark{width:28px;height:28px;display:block;flex:none}
.switcher{border:1px solid var(--line);border-radius:12px;padding:10px 12px;display:flex;align-items:center;justify-content:space-between;gap:8px;background:var(--ground);width:100%;text-align:left}
.switcher b{display:block;font-size:14px}.switcher span{display:block;color:var(--muted);font-size:12px}
/* The owner inside a company: the company's name over the way back. Names of its own, since `.stack` is the page's column of cards and `.pick` a row's tick box, and both once bent it (found 2026-10-01: the name squeezed to 24 px and cut). */
.switcher.two-rows{padding:0;flex-direction:column;align-items:stretch;gap:0;overflow:hidden}.switcher-pick{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 12px;min-height:44px}.switcher-pick:hover{background:var(--surface)}.switcher .leave{display:block;width:100%;text-align:left;padding:0 12px;min-height:38px;border-top:1px solid var(--line);color:var(--accent);font-size:12px;font-weight:600;background:var(--surface)}.switcher .leave:hover{background:var(--accent-soft)}
.nav{display:flex;flex-direction:column;gap:2px}.nav h3{margin:10px 12px 4px;font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}.nav h3:first-child{margin-top:0}.nav hr{border:0;border-top:1px solid var(--line);margin:8px 0 2px}.nav a{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:10px;color:var(--muted);font-weight:500;min-height:44px}.nav a.on{background:var(--accent-soft);color:var(--accent)}.nav a:active{background:var(--ground)}
.nav svg{width:18px;height:18px;flex:none}
/* THE PERSON AT THE FOOT OF THE RAIL (Simon 2026-10-09, his name and his company stood on three lines): the practice of the dashboards, the avatar, the name and the role on one row each, never wrapping, the way out beside them; under a hairline the copyright and the build. The nav scrolls in the middle while the top and the foot stay where they are, on the phone's drawer too. */
.rail .nav{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}
.rail .me{margin-top:auto;display:flex;align-items:center;gap:8px;padding:8px 0 8px 12px;flex:none}
.me .lines{display:grid;gap:1px;min-width:0;flex:1}
.rail-foot{flex:none;margin:-8px -16px -6px;padding:14px 16px 0;border-top:1px solid var(--line);color:var(--muted);font-size:11px;line-height:16px;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.avatar{width:32px;height:32px;border-radius:50%;background:var(--accent-soft);color:var(--accent);display:grid;place-items:center;font-weight:700;font-size:12px;flex:none}
.me b{display:block;font-size:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.me .lines>span{color:var(--muted);font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.me form,.me>.icon-btn{margin-left:auto;flex:none}
.main{padding:20px 28px 40px;min-width:0}
html{scroll-padding-top:calc(var(--bar) + 16px)}
/* The top bar stays in reach: frosted like the header of Simon's store (the ground at 90 percent over an 8 px blur), under a tenth of the screen, no animation, its edge drawn only once the page has moved under it. It bleeds over the padding of .main, so the two must change together. */
.top{display:flex;align-items:center;gap:12px;position:sticky;top:0;z-index:8;margin:-20px -28px 18px;padding:12px 28px;background:color-mix(in srgb,var(--ground) 95%,transparent);border-bottom:1px solid transparent}@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){.top{background:color-mix(in srgb,var(--ground) 90%,transparent);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}}.top.stuck{border-bottom-color:var(--line);box-shadow:0 6px 16px -12px rgba(21,23,28,.25)}
.menu{display:none;width:44px;height:44px;border-radius:10px;border:1px solid var(--line);background:var(--surface);place-items:center}
.search{flex:1;max-width:520px;display:flex;align-items:center;gap:10px;background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:0 14px;height:44px;color:var(--muted)}.search input{border:0;background:none;outline:0;flex:1;height:auto;padding:0;min-width:0}
/* The clear cross inside every search box (app.js dressSearch, 2026-10-15): the browser's own is hidden, since Safari and Firefox draw none; ours shows once text is typed and sits at the text's end, the left for Arabic (the box mirrors below). */
input[type=search]::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none;display:none}
.search .x{flex:none;width:36px;height:36px;margin-inline-end:-10px;border-radius:50%;display:grid;place-items:center;color:var(--muted);position:relative}.search .x:hover{background:var(--ground);color:var(--text)}.search:has(input:dir(rtl)) .x{margin-inline-end:0;margin-inline-start:-10px}
/* NOTHING MAKES A PAGE SCROLL SIDEWAYS (measured 2026-09-21 on 18 pages at six widths): the search box of the top bar may shrink (a flex item keeps the width of what it holds
   unless told otherwise, and an input holds about twenty characters), and a line of words may break a word that is longer than its place. */
.search{min-width:0}.what{min-width:0;overflow-wrap:anywhere}
/* On a phone the top bar cannot hold a search box next to the menu, the license, the theme and the bell: the search takes a line of its own under them. */
@media (max-width:520px){.top:has(.search){flex-wrap:wrap}.top .search{order:9;flex:1 1 100%}}
.top .spacer{flex:1}.icon-btn{width:44px;height:44px;border-radius:50%;border:1px solid var(--line);background:var(--surface);display:grid;place-items:center;color:var(--muted);flex:none}
.chips-input{display:flex;flex-wrap:wrap;align-items:center;gap:6px;min-height:44px;padding:6px 8px;border:1px solid var(--line);border-radius:10px;background:var(--surface);cursor:text}.chips-input:focus-within{outline:2px solid var(--accent);outline-offset:2px}.chips-input .chip{height:30px;padding:0 6px 0 12px;gap:4px;background:var(--accent-soft);border-color:transparent;color:var(--accent);max-width:100%}.chips-input .chip span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chips-input .chip.bad{background:var(--danger-soft);color:var(--danger)}.chips-input .chip button{width:22px;height:22px;border-radius:50%;display:grid;place-items:center;color:inherit;font-size:15px;line-height:1}.chips-input .chip button:hover{background:rgba(0,0,0,.08)}.chips-input input{flex:1;min-width:170px;height:30px;border:0;padding:0 4px;background:none;font-size:16px}.chips-input input:focus{outline:0}
.field .label{display:block;font-size:13px;font-weight:600;margin-bottom:8px}.field .label [data-saved]{margin-left:8px}.choices,.rows{display:grid;border:1px solid var(--line);border-radius:12px;background:var(--ground);overflow:hidden}.choice,.rows>*{padding:12px 14px;border-top:1px solid var(--line)}.choice:first-child,.rows>:first-child{border-top:0}.choices-group{padding:10px 14px 4px;border-top:1px solid var(--line);font-size:12px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);background:var(--surface)}.choices-group:first-child{border-top:0}.choices-group+.choice{border-top:0}.choice{display:flex;align-items:center;justify-content:space-between;gap:16px;cursor:pointer}.choice .words{display:grid;gap:1px;min-width:0}.choice b{font-weight:600}
input[type=checkbox].switch{appearance:none;-webkit-appearance:none;width:40px;height:24px;border-radius:12px;background:var(--line);position:relative;flex:none;margin:0;cursor:pointer;transition:background .15s}input[type=checkbox].switch::after{content:"";position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:50%;background:#fff;transition:left .15s}input[type=checkbox].switch:checked{background:var(--online)}input[type=checkbox].switch:checked::after{left:19px}
.setting{display:flex;align-items:center;justify-content:space-between;gap:18px}.setting .words{display:grid;gap:2px;min-width:0;flex:1}.setting .words b{font-weight:600}.setting .acts{flex:none}.setting .toggle{flex:none}
.seg{display:inline-flex;align-items:center;height:44px;padding:4px;gap:2px;border:1px solid var(--line);border-radius:999px;background:var(--surface);flex:none}.seg button{width:34px;height:34px;border-radius:50%;display:grid;place-items:center;color:var(--muted)}.seg button:hover{color:var(--text)}.seg button.on{background:var(--accent-soft);color:var(--accent)}
.plan-wrap{position:relative;flex:none}.plan{display:flex;align-items:stretch;height:44px;padding:0;border:1px solid transparent;border-radius:999px;overflow:hidden;background:var(--surface);color:var(--muted)}.plan:hover,.plan[aria-expanded="true"]{border-color:var(--muted)}.plan-part{display:flex;align-items:center;gap:10px;padding:0 14px;background:var(--online-soft)}.plan-part:first-child{padding-left:16px}.plan-part+.plan-part{border-left:2px solid var(--surface)}.plan-part.att,.plan-status.att{background:var(--warn-soft)}.plan-part.off,.plan-status.off{background:var(--danger-soft)}.plan-words{display:grid;text-align:left;line-height:1.2}.plan-words small{font-size:11px}.plan-words b{font-size:13px;font-weight:600;color:var(--text);white-space:nowrap;font-variant-numeric:tabular-nums}.plan .caret{flex:none;margin-left:2px;transition:transform .15s}.plan[aria-expanded="true"] .caret{transform:rotate(180deg)}
.ring{width:22px;height:22px;flex:none;transform:rotate(-90deg)}.ring circle{fill:none;stroke:color-mix(in srgb,var(--online) 24%,transparent);stroke-width:3}.ring .fill{stroke:var(--online);stroke-linecap:round}.plan-part.att .ring circle{stroke:color-mix(in srgb,var(--warn) 24%,transparent)}.plan-part.att .ring .fill{stroke:var(--warn)}.plan-part.off .ring circle{stroke:color-mix(in srgb,var(--danger) 24%,transparent)}.plan-part.off .ring .fill{stroke:var(--danger)}
progress{appearance:none;-webkit-appearance:none;display:block;width:100%;height:6px;border:0;border-radius:3px;background:var(--online-soft);overflow:hidden}progress::-webkit-progress-bar{background:var(--online-soft)}progress::-webkit-progress-value{background:var(--online);border-radius:3px}progress::-moz-progress-bar{background:var(--online);border-radius:3px}.use.att progress,.use.att progress::-webkit-progress-bar{background:var(--warn-soft)}.use.att progress::-webkit-progress-value{background:var(--warn)}.use.att progress::-moz-progress-bar{background:var(--warn)}.use.off progress,.use.off progress::-webkit-progress-bar{background:var(--danger-soft)}.use.off progress::-webkit-progress-value{background:var(--danger)}.use.off progress::-moz-progress-bar{background:var(--danger)}
.plan-status{display:flex;align-items:center;gap:12px;margin:14px 16px;padding:10px 14px;border-radius:10px;background:var(--online-soft);font-size:14px}.plan-status b{font-weight:600}.usage{display:grid;gap:14px;padding:16px;border-top:1px solid var(--line)}.use{display:grid;grid-template-columns:1fr auto;gap:7px 12px;align-items:baseline;font-size:13px}.use span{color:var(--muted)}.use b{font-weight:600;font-variant-numeric:tabular-nums}.use progress{grid-column:1/-1}
.pop{position:absolute;right:0;top:52px;width:360px;max-width:calc(100vw - 32px);padding:0;z-index:20;box-shadow:0 12px 40px rgba(21,23,28,.18)}.facts{display:grid;grid-template-columns:1fr auto;gap:8px 16px;margin:0;padding:14px 16px;border-top:1px solid var(--line);font-size:13px}.facts dt{color:var(--muted)}.facts dd{margin:0;font-weight:600;text-align:right}
.bell-wrap{position:relative;flex:none}
/* THE TOP BAR NEVER STICKS OUT SIDEWAYS (measured 2026-09-21: it did in two bands of width, on every page, and the whole page scrolled sideways with it). Just
   above the width where the rail appears, the rail takes 248px of the window and the two pills, the theme switch and the bell no longer fit: there the Data
   transfer part keeps its ring and gives up its numbers. On the smallest phones the License pill keeps its coloured dot and gives up its word. Both open the
   same panel, which says everything in full, and the button keeps its name for a screen reader (`title`). */
@media (min-width:761px) and (max-width:900px){.plan-part+.plan-part .plan-words{display:none}}
@media (max-width:359px){.top{gap:8px}}
/* THE LICENSE ON A PHONE IS AN ICON IN THE STATE'S COLOR (Simon 2026-10-09, "mnetrok the word license walla menkhalle l point bass walla chi icon"): a 44 px bordered circle like the theme control beside it, with a smaller circle inside tinted by the worst state and the license mark in the state's color, the theme control's chosen button's look, no words; the words are the button's accessible name and the panel it opens (the practice: a status indicator in a crowded bar may be an icon when it carries a name and opens the full status). */
/* THE THEME CONTROL ON A PHONE IS ONE ICON (Simon 2026-10-09): the current theme alone until a tap opens the three (`data-expanded`, app.js); the opener stays where it is, so nothing moves under the finger. */
@media (max-width:760px){[data-theme-pick]:not([data-expanded]) button:not(.on){display:none}}
.plan-icon{display:none}@media (max-width:760px){.plan{width:44px;padding:4px;align-items:center;justify-content:center;border-color:var(--line)}.plan .plan-part{width:34px;height:34px;padding:0;justify-content:center;border-radius:50%;background:var(--online-soft)}.plan.att .plan-part{background:var(--warn-soft)}.plan.off .plan-part{background:var(--danger-soft)}.plan .plan-words,.plan .dot{display:none}.plan .plan-icon{display:block;color:var(--online)}.plan.att .plan-icon{color:var(--warn)}.plan.off .plan-icon{color:var(--danger)}}.bell{position:relative}.bell .count{position:absolute;top:-4px;right:-4px;min-width:20px;height:20px;padding:0 6px;border-radius:999px;background:var(--accent);color:var(--on-accent);font-size:11px;font-weight:600;font-style:normal;display:inline-flex;align-items:center;justify-content:center;font-variant-numeric:tabular-nums;border:2px solid var(--ground)}.bell[aria-expanded="true"]{color:var(--text);border-color:var(--muted)}
.inbox-panel{position:absolute;right:0;top:52px;width:420px;max-width:calc(100vw - 32px);padding:0;z-index:20;box-shadow:0 12px 40px rgba(21,23,28,.18)}.pop .inbox-foot{justify-content:center}.inbox-head{display:flex;align-items:center;justify-content:space-between;padding:14px 16px 10px;border-bottom:1px solid var(--line)}.inbox-panel .list.inbox{padding:0 8px;max-height:min(60dvh,520px);overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--line) transparent}.inbox-panel .list.inbox::-webkit-scrollbar{width:6px}.inbox-panel .list.inbox::-webkit-scrollbar-track{background:transparent}.inbox-panel .list.inbox::-webkit-scrollbar-thumb{background:var(--line);border-radius:3px}.inbox-panel .list.inbox::-webkit-scrollbar-thumb:hover{background:var(--muted)}.inbox-empty{padding:26px 16px;text-align:center;margin:0}.inbox-foot{display:flex;justify-content:space-between;padding:12px 16px;border-top:1px solid var(--line);font-size:13px}.inbox-foot a{color:var(--accent);font-weight:600}
/* A notice row is a grid (2026-10-08, Simon: the words reached half the row and the rest of the line stayed empty): the title line holds the time and the actions, the words run the full width under it. */.list.inbox{gap:0}.list.inbox .item{background:none;border:0;border-bottom:1px solid var(--line);border-radius:0;padding:11px 8px;position:relative;display:grid;grid-template-columns:auto minmax(0,1fr) auto auto auto auto;column-gap:0;row-gap:2px;align-items:center}.list.inbox .item>.badge{grid-column:1;grid-row:1/3;align-self:start;margin-right:12px}.list.inbox .what{grid-column:2;grid-row:1;min-width:0}.list.inbox .body>.seen{grid-column:2/-1;grid-row:2;min-width:0}.list.inbox .item>.when{grid-column:3;grid-row:1;white-space:nowrap;padding-top:0;margin-left:12px}.list.inbox .item>.asks{grid-column:4;grid-row:1;display:flex;gap:8px;margin-left:12px}.list.inbox .item>.asks form{margin:0}.list.inbox .item .read-one .mark-icon{display:none}.list.inbox .item:last-child,.list.inbox .item:has(+ .day),.list.inbox .item:has(+ [hidden]){border-bottom:0}.list.inbox .body{display:contents}.list.inbox .what b{font-weight:600}.list.inbox .item.unread{background:var(--accent-soft);box-shadow:inset 3px 0 0 var(--accent)}.list.inbox .item .chev{grid-column:6;grid-row:1;margin-left:12px}.list.inbox .item .read-one{grid-column:5;grid-row:1;margin:0 0 0 12px}.list.inbox .item .tag{margin-left:6px;vertical-align:middle}.list.inbox .what .status{margin-left:8px;vertical-align:middle;position:relative;top:-1px}.inbox-foot form{margin:0}.inbox-foot .btn-link{font-size:13px}.list.inbox .day{padding:14px 8px 2px}.list.inbox.compact .item{padding:10px 8px}.list.inbox.compact .what b{font-weight:600;font-size:14px}
.head{display:flex;align-items:flex-end;justify-content:space-between;gap:16px;flex-wrap:wrap;margin-bottom:var(--gap)}
h1{font-size:24px;line-height:1.2;margin:0;font-weight:700;text-wrap:balance}h2{font-size:17px;margin:0 0 12px;font-weight:700}.head p{margin:4px 0 0;color:var(--muted)}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;height:44px;padding:0 18px;border-radius:12px;font-weight:600;border:1px solid var(--line);background:var(--surface);white-space:nowrap}.btn.primary{background:var(--accent);color:var(--on-accent);border-color:var(--accent);box-shadow:0 6px 16px rgba(21,94,117,.25)}.btn.danger{color:var(--danger)}.btn.small{height:34px;padding:0 12px;border-radius:9px;font-size:13px;font-weight:500}
.books{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:14px}.pill{height:36px;padding:0 14px;border-radius:999px;border:1px solid var(--line);background:var(--surface);display:inline-flex;align-items:center;gap:8px;font-weight:500;color:var(--muted)}.pill.on{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}.pill i{font-style:normal;font-size:12px;opacity:.75;font-variant-numeric:tabular-nums}
.tags{display:flex;gap:8px;align-items:center;color:var(--muted);font-size:13px;margin-bottom:14px;flex-wrap:wrap}.tag{display:inline-flex;align-items:center;height:26px;padding:0 10px;border-radius:8px;background:var(--accent-soft);color:var(--accent);font-size:12px;font-weight:600}.tag.grey{background:var(--ground);color:var(--muted);border:1px solid var(--line)}
.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:18px}.card+.card:not(dialog){margin-top:var(--gap)}
.main>:is(.card,.table,.stack,.foot,.cards,#rows)+:is(.card,.table,.stack):not(dialog){margin-top:var(--gap)}
/* A table clips its rounded corners with overflow clip, never hidden: hidden makes the table its own scroll container, and a sticky header then sticks inside the table instead of under the top bar (2026-10-08, found on Watch, Customers, Packages, Usage and the Overview; Devices, Users and Logs each carried the clip on their own). */
.table{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);overflow:clip}
.row{display:grid;grid-template-columns:20px 1.6fr 1fr 150px 130px 90px 150px;gap:14px;align-items:center;padding:14px 18px;border-top:1px solid var(--line)}.row:first-child{border-top:0;padding:10px 18px;color:var(--muted);font-size:12px;letter-spacing:.06em;text-transform:uppercase;font-weight:600}
/* EVERY TABLE'S HEADER STICKS under the top bar while its rows scroll (Simon 2026-10-08: the Watch page's did not, Logs' did; Devices, Users and Logs each had a rule of their own). One rule for every table's label row, the Usage table's included, which stands after the table's title. */
.table>.row:first-child,.row.usage.is-head{position:sticky;top:var(--bar);z-index:3;background:var(--surface);cursor:default}
/* A CUSTOMER'S ENTRY (Simon 2026-10-09, "it can be a card multyrow ma daroure tkoun row wa7ad"): one bordered list with dividers, each entry several rows: the head with the name, the state, the marks, the phone and the actions, then labelled facts in two columns, one on a phone (the list rule: in a narrow panel facts stand as rows). */
.row.customers{grid-template-columns:minmax(0,1fr);gap:10px;align-items:start}.row.customers:first-child{padding:14px 18px;color:inherit;font-size:inherit;letter-spacing:normal;text-transform:none;font-weight:inherit;position:static;cursor:auto}.row.customers .cust-head{display:flex;align-items:flex-start;justify-content:space-between;gap:8px 16px;flex-wrap:wrap}.row.customers .cust-name{display:flex;align-items:center;flex-wrap:wrap;gap:4px 10px;flex:1 1 240px;min-width:0}.row.customers .cust-name b{font-size:16px}.row.customers .cust-head .acts{display:flex;gap:8px;flex:none;margin-left:auto;position:static}.cust-facts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:4px 32px;margin:0}.cust-facts>div{display:grid;grid-template-columns:104px minmax(0,1fr);gap:0 12px;align-items:center;min-height:26px}.cust-facts dt{color:var(--muted);font-size:13px}.cust-facts dd{margin:0;min-width:0;display:flex;flex-wrap:wrap;align-items:center;gap:4px 8px;font-size:14px}
/* Platform health: a line per fact, its value tinted by its state, its advice shown only when it is not green. */
.mb-gap{margin-bottom:var(--gap)}.pill .mark{margin-right:2px}.newlines{position:sticky;top:var(--bar);z-index:4;display:flex;justify-content:center;margin:-4px 0 10px;pointer-events:none}.newlines .btn{pointer-events:auto;box-shadow:var(--shadow)}.row.watch .seen a{color:inherit;text-decoration:none;border-bottom:1px dotted var(--muted)}.row.watch .seen a:hover{color:var(--accent);border-bottom-color:var(--accent)}/* Platform health, grouped (the second pass, 2026-09-24): six groups, each a bordered list; the header row carries the group's state; every line shows, nothing folded (Simon 2026-09-28 night). */.health-groups{display:grid;grid-template-columns:minmax(0,1fr);gap:12px;align-items:start}.health-group{border:1px solid var(--line);border-radius:12px;background:var(--surface);overflow:hidden}.health-group-head{display:flex;align-items:center;gap:10px;min-height:32px;padding:10px 12px}.health-group-head b{font-weight:600;font-size:14px;min-width:0}.health-group-head .status{flex:none}.health-group-head .seen{font-size:13px;white-space:nowrap}.health-group .rows{border:0;border-radius:0;background:var(--ground);border-top:1px solid var(--line)}[data-part="health"] .card-head .acts,[data-part="bans"] .card-head .acts{flex-wrap:wrap;justify-content:flex-end;row-gap:8px;margin-inline-start:auto}
/* A card's title never breaks into two lines on a phone (Simon 2026-10-09, the emails card): the title keeps its line and the actions wrap under it, on the right. */
.card-head h3{flex:none;max-width:100%}[data-part="bans"] h3.sub{margin:16px 0 8px;font-size:13px;font-weight:600;color:var(--muted)}[data-part="bans"] .sub-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:16px}[data-part="bans"] .sub-head h3.sub{margin:0}[data-part="bans"] .sub-head+.rows{margin-top:8px}[data-part="bans"] .empty-line{margin:0;font-size:13px}.row.ban,.rows>.row.ban:first-child{grid-template-columns:minmax(0,1fr) auto;gap:12px;padding:10px 12px;align-items:center;font-size:13px;letter-spacing:0;text-transform:none;font-weight:400;color:var(--text);border-top:0}.rows>.row.ban+.row.ban{border-top:1px solid var(--line)}.row.ban .what{display:grid;gap:3px;min-width:0}.row.ban .line{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.row.ban .status{white-space:nowrap}.row.ban .what b{font-weight:600}.row.ban .what b a{color:var(--text)}.row.ban .what .seen{font-weight:400}.row.ban .ban-acts{display:flex;justify-content:flex-end;margin:0}.row.health,.rows>.row.health:first-child{grid-template-columns:auto minmax(0,1fr) 32px;gap:12px;padding:10px 12px;align-items:start;font-size:13px;letter-spacing:0;text-transform:none;font-weight:400;color:var(--text);border-top:0}.rows>.row.health+.row.health{border-top:1px solid var(--line)}.row.health .status{min-width:74px;justify-content:center;white-space:nowrap}.row.health .what{display:grid;gap:3px;min-width:0}.row.health .what b{font-weight:600}.row.health .what .tag{justify-self:start}.row.health.accepted .what b{color:var(--muted)}.row.health .chev{margin:0;justify-self:end}.rows>.row.drawer{grid-template-columns:1fr;padding:0 12px 12px}
/* A line of the owner's security watch, with a chevron that opens what it means and what to do. */
.row.watch{grid-template-columns:150px minmax(0,1.2fr) minmax(0,.9fr) 230px 64px 90px 36px}.row.watch .kind{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.row.watch .kind .mini{display:none;margin:0}.row.watch .times{font-variant-numeric:tabular-nums}.watch-story{display:grid;grid-template-columns:1.2fr 1fr;gap:8px 32px;padding:4px 2px 8px}.watch-story h3{margin:12px 0 6px;font-size:13px;font-weight:600;letter-spacing:0;text-transform:none;color:var(--text)}.watch-story p{margin:0;max-width:62ch;line-height:1.5}.watch-story .facts{max-width:none}
.place{white-space:nowrap}.flag{display:inline-block;width:16px;height:12px;border-radius:2px;box-shadow:0 0 0 1px var(--line);vertical-align:-1px;margin:0 3px 0 2px}.row.users{grid-template-columns:minmax(0,1.6fr) 100px minmax(0,1.1fr) 100px 190px minmax(0,1fr) 44px;cursor:pointer;min-height:56px}.row.users:not(:first-child):hover{background:var(--ground)}.row.books{grid-template-columns:1.6fr 1fr 110px 110px 120px}.row.entries{grid-template-columns:20px minmax(0,1.4fr) minmax(0,1.6fr) 96px}.row.grants{grid-template-columns:1.6fr 1fr 120px 160px}
.dot{width:10px;height:10px;border-radius:50%;background:var(--line);flex:none}.dot.on{background:var(--online);box-shadow:0 0 0 4px var(--online-soft)}
.dot.live{background:var(--accent);box-shadow:0 0 0 4px var(--accent-soft)}.dot.ok{background:var(--online);box-shadow:0 0 0 4px var(--online-soft)}.dot.warn{background:var(--warn);box-shadow:0 0 0 4px var(--warn-soft)}.dot.bad{background:var(--danger);box-shadow:0 0 0 4px var(--danger-soft)}
.name b{display:block;font-weight:600}.name span{color:var(--muted);font-size:13px}.name a{color:inherit}
.id{font-family:var(--mono);font-size:13px;color:var(--muted)}.seen{color:var(--muted);font-size:13px}.seen.now{color:var(--online);font-weight:600}
.grants{font-variant-numeric:tabular-nums}
.acts{display:flex;gap:8px;justify-content:flex-end;align-items:center}.acts:not(:has(>:not([hidden]))){display:none}
.toggle{width:40px;height:24px;border-radius:12px;background:var(--online);position:relative;flex:none;display:inline-block;border:0;padding:0}.toggle::after{content:"";position:absolute;top:3px;left:19px;width:18px;height:18px;border-radius:50%;background:#fff;transition:left .15s}.toggle.off{background:var(--line)}.toggle.off::after{left:3px}
.status{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;padding:3px 8px;border-radius:999px}.status.att{background:var(--warn-soft);color:var(--warn)}.status.rec{background:var(--accent-soft);color:var(--accent)}.status.off{background:var(--danger-soft);color:var(--danger)}.status.ok{background:var(--online-soft);color:var(--online)}
.cards{display:none}
.foot{display:flex;justify-content:space-between;color:var(--muted);font-size:13px;padding:14px 4px}
.foot.more{flex-direction:column;align-items:center;gap:10px;padding:18px 4px 4px;text-align:center}.foot.more .acts{justify-content:center;width:100%}.foot.more .btn{min-width:220px}.foot.more a:not(.btn){color:var(--accent)}
.form{display:grid;gap:14px;max-width:560px}.field{display:grid;gap:6px}.field label{font-size:13px;font-weight:600;color:var(--muted)}.field .hint{font-size:12px;color:var(--muted)}[hidden]{display:none!important}.field.check{grid-template-columns:auto 1fr;align-items:center;gap:10px}.field.check label{color:var(--text);font-weight:500;font-size:15px}
.qr{background:#fff;padding:12px;border-radius:10px;display:inline-block;line-height:0;margin-bottom:12px}.qr svg{width:196px;height:196px;display:block}/* Recovery codes fill their card in even columns: four across on a wide screen, two on a phone. Printed, the page is the codes and nothing else. */
.codes{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(140px,100%),1fr));gap:8px}.m-t0{margin-top:0}
@media print{.rail,.top,.acts,.toast,dialog{display:none!important}body{background:#fff}.main{padding:0}.card{box-shadow:none;border:0;padding:0}}.codes .code{text-align:center;font-size:15px}
.list .day{font-size:12px;font-weight:600;color:var(--muted);letter-spacing:.06em;text-transform:uppercase;padding:10px 2px 0}.list .day:first-child{padding-top:0}
.row.day{grid-template-columns:1fr;padding:8px 18px;background:var(--ground);color:var(--muted);font-size:12px;font-weight:600;letter-spacing:.06em;text-transform:uppercase}
.list .item .title{flex:1;min-width:0}.list .item .title b{font-weight:600}
.list.wide .item{flex-wrap:wrap}.list.wide .item b,.list.wide .item .title{flex:1 1 60%;min-width:0}.list.wide .item .seen{flex:0 0 100%}.list.wide .item .dot~.seen{padding-left:20px}.list.wide .item .acts{flex:none;margin-left:auto}.list.wide .item .acts .chev{margin-left:0}.list.wide .item .acts form{margin:0}
.card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px}.card-head h2{margin:0}
/* A long page names its groups (General, Security, Notifications) the way the rail names its own: small, quiet, above the cards. A card under a
   group label is one heading level down and looks the same; what sits inside a card under its title is smaller. */
.group{margin:10px 2px -12px;font-size:12px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}.stack>.group:first-child{margin-top:0}
/* What an account holds, as text (the practice: a settings page shows, and one action in the card's head starts the editing, in a dialog). A label and its value
   side by side, the value wrapping wherever it must; on a phone the label sits above its value. */
.read{display:grid;grid-template-columns:minmax(110px,160px) minmax(0,1fr);gap:12px 16px;margin:0}.read dt{color:var(--muted);font-size:13px;padding-top:2px}.read dd{margin:0;min-width:0;display:flex;flex-wrap:wrap;align-items:center;gap:6px 8px;overflow-wrap:anywhere}.read dd>*{min-width:0}.read .status{margin:0}
@media (max-width:520px){.read{grid-template-columns:minmax(0,1fr);gap:2px}.read dd{margin-bottom:12px}.read dd:last-child{margin-bottom:0}}
.card>h3,.card-head h3{font-size:17px;margin:0 0 12px;font-weight:700}.acts{flex-wrap:wrap}
/* WHERE BUTTONS STAND (Simon, 2026-09-21: "the buttons are either on the right or on the left", he had found both on one page). ON THE RIGHT, everywhere: in a
   dialog, under a form, at the foot of a card, in a row; a card's ONE action stands in its head, on the right. The main button is the last one, what cancels or
   goes back stands before it. What stays where it is: a field with its own button beside it (add a tag, grant a user), rows of filters and of checkboxes, and the centred Load more. */
.card-head .with-state{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.card-head .with-state .status{margin:0}.stack>.card>p:last-child,.stack>.card>.read+p{margin-bottom:0}.card-head h3{margin:0}.card h4{font-size:14px;font-weight:600;margin:20px 0 4px}
/* A part of a card with a name and, when it has one, an action of its own: the name to the left, the action to the right, like the card's own head, one size down. */
.sub-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:22px 0 2px;padding-top:16px;border-top:1px solid var(--line)}.sub-head h4{margin:0}.sub-head+p{margin-top:6px}
/* Working hours: a switch, a day and its window on one line, the windows in one column; a day that is off says so instead. On a phone the window goes under its day and fills the line. */
.days{display:grid;gap:6px}.day{display:grid;grid-template-columns:1fr auto;align-items:center;gap:4px 12px;min-height:48px}.day-name{display:flex;align-items:center;gap:10px;font-size:14px;font-weight:600;cursor:pointer}.day-times{display:flex;align-items:center;gap:8px}.day-times input{width:132px;padding:0 8px}.day .day-off{display:none;min-height:44px;align-items:center}.day:has(.switch:not(:checked)) .day-times{display:none}.day:has(.switch:not(:checked)) .day-off{display:flex}.day .problem{grid-column:1/-1;margin-bottom:6px}.days .hint{margin-top:6px;font-size:12px;color:var(--muted)}
@media (max-width:520px){.days{gap:12px}.day:has(.switch:checked){grid-template-columns:1fr}.day:has(.switch:checked) .day-times input{flex:1;width:auto;min-width:0}}
.list.feed{gap:0}.list.feed .item{background:none;border:0;border-bottom:1px solid var(--line);border-radius:0;padding:11px 2px;gap:12px;align-items:flex-start}.list.feed .item:last-child,.list.feed .item:has(+ .day),.list.feed .item:has(+ [hidden]){border-bottom:0}
.ico{width:30px;height:30px;border-radius:50%;background:var(--ground);border:1px solid var(--line);color:var(--muted);display:grid;place-items:center;flex:none}.ico.off{background:var(--danger-soft);border-color:transparent;color:var(--danger)}
.list.feed .body,.list.alerts .body{flex:1;min-width:0;display:grid;gap:2px}.list.feed .body{padding-top:4px}.list .what b,.change b{font-weight:600}.list .when{flex:none;padding-top:5px;white-space:nowrap;font-variant-numeric:tabular-nums}.list.feed .day{padding:14px 2px 2px}
.list.alerts .item{align-items:flex-start;gap:12px}.badge{width:32px;height:32px;border-radius:10px;display:grid;place-items:center;flex:none}.badge.rec{background:var(--accent-soft);color:var(--accent)}.badge.att{background:var(--warn-soft);color:var(--warn)}.badge.off{background:var(--danger-soft);color:var(--danger)}.list.alerts .when{padding-top:5px}
.live-pill{display:inline-flex;align-items:center;gap:8px;height:26px;padding:0 10px 0 9px;border-radius:999px;font-size:12px;font-weight:600;color:var(--accent);background:var(--accent-soft)}.live-pill i{position:relative;width:8px;height:8px;border-radius:50%;background:var(--accent);flex:none}.live-pill i::after{content:"";position:absolute;inset:-3px;border-radius:50%;border:2px solid var(--accent);animation:pulse 2s ease-out infinite}
@keyframes pulse{0%{transform:scale(.6);opacity:.9}100%{transform:scale(1.8);opacity:0}}
.live-pill.stale{color:var(--muted);background:var(--ground);border:1px solid var(--line)}.live-pill.stale i{background:var(--muted)}.live-pill.stale i::after{animation:none;display:none}
.list.live .item{display:grid;grid-template-columns:10px minmax(0,1fr) auto auto;grid-template-areas:"dot title timer acts" "dot seen timer acts";align-items:center;column-gap:10px;row-gap:2px}
.list.live .item .dot{grid-area:dot;align-self:start;margin-top:7px}.list.live .item .title{grid-area:title}.list.live .item .seen{grid-area:seen}.list.live .item .acts{grid-area:acts;margin-left:0}.list.live .item .acts form{margin:0}.list.live .item .acts .chev{margin-left:0}
.list.live .item .timer{grid-area:timer;display:grid;justify-items:end;text-align:right;padding:0 14px;line-height:1.2;border-right:1px solid var(--line);margin-right:2px}.list.live .item .timer time{font-size:20px;font-weight:600;font-variant-numeric:tabular-nums;letter-spacing:.01em}.list.live .item .timer small{font-size:12px;color:var(--muted);white-space:nowrap}
.list .item.arrived{animation:arrive 1.2s ease-out}@keyframes arrive{0%,40%{background:var(--accent-soft)}100%{background:var(--ground)}}.list .item.leaving,.list .drawer.leaving{opacity:0;transition:opacity .25s}
.list.stack .item{flex-wrap:wrap}.list.stack .item b{flex:1 1 auto}.list.stack .item form{margin-left:auto}.list.stack .item .seen{flex:0 0 100%;order:3}
.seen.warn{color:var(--warn);margin:8px 0 0}.btn:disabled{opacity:.45;cursor:default;box-shadow:none}/* Every button answers the mouse (Simon, 2026-10-03: the trial buttons sat still under the pointer). One rule for both modes: mixing in the text colour darkens in light mode and lightens in dark mode. Only where a pointer can hover, so a tap on a phone leaves nothing stuck. */@media (hover:hover){.btn:hover:not(:disabled){background:color-mix(in oklab,var(--surface),var(--text) 8%);border-color:color-mix(in oklab,var(--line),var(--text) 18%)}.btn.primary:hover:not(:disabled){background:color-mix(in oklab,var(--accent),var(--text) 12%);border-color:color-mix(in oklab,var(--accent),var(--text) 12%)}}
/* One dialog for the whole interface: a title that names the decision, a few words on what follows, the actions at the right with the one that acts last, a cross that cancels. The page behind is dimmed and does not scroll. */
html:has(dialog:modal){overflow:hidden;padding-right:var(--scrollbar,0px)}
/* A touched screen drags the page behind a dialog whatever its overflow says: there the page is PINNED while a dialog is open (web/static/app.js sets where). The bar at the
   top of the page is sticky and a pinned page has nothing to stick to, so it is held in place by hand. A dialog that scrolls stops at its own ends. */
@media (pointer:coarse){html.locked body{position:fixed;left:0;right:0;top:var(--locked-at,0px);overflow:hidden}html.locked .top{position:relative;top:calc(var(--locked-at,0px) * -1)}}
dialog.modal{overscroll-behavior:contain}dialog.modal::backdrop{overscroll-behavior:contain;touch-action:none}html:has(body.open){overflow:hidden}
dialog{color:var(--text)}dialog.modal{border:0;padding:0;max-width:480px;width:calc(100% - 32px);max-height:calc(100dvh - 32px);overflow:hidden;border-radius:18px;background:var(--surface);box-shadow:0 24px 64px rgba(0,0,0,.3)}
/* THE SCROLL LIVES INSIDE THE ROUNDED BOX (2026-10-15, his phone: the bar stuck out of the dialog's rounded corner). A dialog that scrolls itself paints its bar over its own corners on every engine, so the dialog clips and one inner box, made by app.js once for every dialog that is not a sheet, carries the padding and the scrolling. */
.modal-scroll{padding:24px;max-height:calc(100dvh - 32px);overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain}dialog.modal.confirm{max-width:420px}dialog.modal::backdrop{background:rgba(10,12,16,.55)}dialog.modal .form{max-width:none}
.modal-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:6px}.modal-head h2{margin:0;padding-top:4px;font-size:18px;line-height:1.3;text-wrap:balance}.modal .x{flex:none;width:36px;height:36px;margin:-4px -8px 0 0;border-radius:50%;display:grid;place-items:center;color:var(--muted)}.modal-head h2:focus{outline:none}dialog:focus{outline:none}.no-ring:focus-visible{outline:none}@media (pointer:coarse){.modal .x{width:44px;height:44px;margin:-8px -12px 0 0}}.modal .x:hover{background:var(--ground);color:var(--text)}.modal-words{margin:0;color:var(--muted);font-size:14px}.modal-words:empty{display:none}dialog.modal>.acts,.modal-scroll>.acts{margin-top:24px}.modal.confirm [data-confirm-field]{margin-top:16px}dialog.modal .form>.acts{margin-top:10px}
.btn.danger.solid{background:var(--danger-strong);border-color:var(--danger-strong);color:#fff;box-shadow:0 6px 16px rgba(220,38,38,.25)}
@media (prefers-reduced-motion:no-preference){dialog.modal[open]{animation:rise .16s ease-out}dialog.modal[open]::backdrop{animation:dim .16s ease-out}}@keyframes rise{from{opacity:0;transform:translateY(8px) scale(.98)}}@keyframes dim{from{opacity:0}}
.toast{position:fixed;z-index:30;right:24px;bottom:24px;display:flex;align-items:center;gap:12px;width:max-content;max-width:min(420px,calc(100% - 48px));padding:12px 10px 12px 16px;border-radius:14px;background:var(--surface);color:var(--text);border:1px solid var(--line);box-shadow:0 12px 32px rgba(21,23,28,.16);font-size:14px;font-weight:500;overflow:hidden;animation:toast-in .25s ease-out;transition:opacity .2s,transform .2s}.toast::before{content:"";position:absolute;left:0;top:0;bottom:0;width:4px;background:var(--online)}.toast.warn::before{background:var(--warn)}.toast.danger::before{background:var(--danger)}.toast .dot{flex:none}@keyframes toast-in{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}.toast.gone{opacity:0;transform:translateY(8px)}.toast .x{flex:none;width:32px;height:32px;border-radius:50%;display:grid;place-items:center;color:var(--muted)}.toast .x:hover{background:color-mix(in srgb,var(--text) 8%,transparent);color:var(--text)}
.people{display:grid;gap:10px}.people>.person.card{padding:0;margin-top:0}
.quiet-link{font-size:13px;font-weight:600;color:var(--accent)}.quiet-link:hover{text-decoration:underline}button.quiet-link{background:none;border:0;padding:0;font:inherit;font-size:13px;font-weight:600;color:var(--accent);cursor:pointer}.hint-line{margin:10px 0 0}.btn-link{color:var(--accent);font-weight:600;font-size:12px;padding:0}.btn-link:hover{text-decoration:underline}.hint .status{margin-right:4px}.strength{display:flex;align-items:center;gap:12px}.strength .bars{display:grid;grid-template-columns:repeat(4,1fr);gap:4px;width:180px}.strength .bars i{height:5px;border-radius:3px;background:var(--line)}.strength .word{font-size:12px;font-weight:600}.strength[data-score="0"] .bars i:nth-child(-n+1),.strength[data-score="1"] .bars i:nth-child(-n+1){background:var(--danger)}.strength[data-score="2"] .bars i:nth-child(-n+2){background:var(--warn)}.strength[data-score="3"] .bars i:nth-child(-n+3){background:var(--online)}.strength[data-score="4"] .bars i{background:var(--online)}/* Our Show carries the guard a browser's own reveal control has, so that one steps aside and nobody sees two. */
::-ms-reveal{display:none}
.pw{position:relative;display:block}.pw input{padding-right:68px}.pw-show{position:absolute;right:6px;top:6px;height:32px;padding:0 10px;border-radius:8px;font-size:13px;font-weight:600;color:var(--accent)}.pw-show:hover{background:var(--accent-soft)}.pw-show:disabled{color:var(--muted);cursor:not-allowed}.pw-show:disabled:hover{background:none}/* The rules under a new password: one line each, each with its own state, all looking alike (a dot until it is met, a green tick once it is, an orange cross
   while what is typed breaks it), so the list never reads as half one thing and half another. The weight never changes, so nothing shifts as a rule ticks. */
/* Setting two factor up (the practice: ONE flow in one card, the steps as words next to the QR code, the key as text with Copy for whoever cannot scan). The QR
   code stands to the left of the steps on a wide screen and centred above them on a phone. The key shows in fours for the eye; its groups are apart by a margin,
   not by spaces, so Copy hands over the key as it is. A phone cannot scan its own screen: there a link opens the authenticator app itself. */
.setup{display:grid;grid-template-columns:auto minmax(0,1fr);gap:8px 32px;align-items:start}.setup-qr{display:grid;place-items:center}.setup .qr{margin:0;border:1px solid var(--line)}
.steps.big{margin:0;gap:20px;font-size:14px;color:var(--muted)}.steps.big li::marker{color:var(--text);font-weight:600}.steps.big li>b{display:block;margin-bottom:2px;font-size:15px;color:var(--text)}
.steps.big .copy{margin-top:10px}.steps.big .field{margin-top:10px;color:var(--text)}.copy .code.groups{flex:1;min-width:0;flex-wrap:wrap;gap:4px .7em}.code.groups span{flex:none;white-space:nowrap;word-break:normal}
.touch-only{display:none}@media (pointer:coarse){.touch-only{display:inline-block;margin-top:10px}}
@media (max-width:620px){.setup{grid-template-columns:minmax(0,1fr);gap:20px}}
/* Steps a person follows somewhere else (their browser's settings, their phone): numbered, one action each. */
.steps{margin:12px 0 16px;padding-left:22px;display:grid;gap:8px;font-size:14px;line-height:1.45}.steps b{font-weight:600}
.rules{list-style:none;margin:4px 0 0;padding:0;display:grid;gap:3px;font-size:12px;color:var(--muted)}.rules li{display:flex;gap:6px;align-items:baseline}.rules li::before{content:"\2022";flex:none;width:12px;text-align:center}.rules li.ok{color:var(--online)}.rules li.ok::before{content:"\2713"}.rules li.no{color:var(--warn)}.rules li.no::before{content:"\2715"}
.copy{display:flex;gap:8px;align-items:stretch;margin-top:16px}.copy .code{flex:1;display:flex;align-items:center;font-size:16px;letter-spacing:.02em}
.errors{color:var(--danger);font-size:13px}/* What a field says when it is wrong: one line under the input, with a mark that does not rely on colour, and a red edge on the field. The list is what the server prints; the script turns it into the same line. */
.problem,.field .errorlist li{display:flex;align-items:flex-start;gap:6px;margin:0;color:var(--danger);font-size:13px;font-weight:500;line-height:1.4}.problem::before,.field .errorlist li::before{content:"!";flex:none;display:grid;place-items:center;width:15px;height:15px;margin-top:1.5px;border-radius:50%;background:var(--danger);color:var(--surface);font-size:10.5px;font-weight:700;line-height:1}.errorlist{list-style:none;margin:0;padding:0;color:var(--danger);font-size:13px}[aria-invalid="true"],.field:has(.errorlist) input,.field:has(.errorlist) textarea,.chips-input.bad{border-color:var(--danger)}[aria-invalid="true"]:focus-visible,.chips-input.bad:focus-within{outline-color:var(--danger)}
/* A field with a rule of its own says that rule when it is wrong, so the hint that repeats it steps aside meanwhile. */
[data-problem][aria-invalid="true"]+.problem:not([data-from="server"])+.hint{display:none}
/* minmax(0,1fr), not 1fr: a plain 1fr column grows to its widest content, and one row of buttons that doesn't fit then pushes EVERY card past a small phone's window. */
.stack{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--gap)}.stack>.card+.card:not(dialog){margin-top:0}.two{display:grid;grid-template-columns:minmax(0,2fr) minmax(0,1fr);gap:14px;align-items:start}
/* A page of forms is one fixed column centred in the content area, its fields as wide as the card. Tables and logs keep the full width. */
.main.narrow>:not(.top,dialog){max-width:var(--narrow);margin-inline:auto}.main.narrow .form{max-width:none}.main.narrow .head>div:first-child{flex:1 1 300px;min-width:0}
.list{display:grid;gap:8px}
/* A row that carries buttons of its own (a login, a client, a trusted browser, a device that gets push). Who it is comes first, with its tag; the facts
   sit under it; the buttons stand to the right of both on a wide screen and UNDER the facts on a phone, so a long name, in any script, wraps inside its own
   column and never runs under a button (Simon saw that on his phone, 2026-09-21). */
.list .item.tall{display:grid;grid-template-columns:minmax(0,1fr) auto;grid-template-areas:"title acts" "seen acts";gap:2px 12px;align-items:center}
.list .item.tall .title{grid-area:title;display:flex;flex-wrap:wrap;align-items:center;gap:4px 8px;min-width:0;overflow-wrap:anywhere}.list .item.tall .title b{flex:0 1 auto}
.list .item.tall .seen{grid-area:seen;min-width:0}.list .item.tall .acts{grid-area:acts;margin:0;width:auto}
@media (max-width:520px){.list .item.tall{grid-template-columns:minmax(0,1fr);grid-template-areas:"title" "seen" "acts"}.list .item.tall .acts{justify-content:flex-end;margin-top:8px}}.list .item{display:flex;align-items:center;gap:10px;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--ground)}.list .item b{flex:1;font-weight:600}.list .item.new{box-shadow:inset 3px 0 0 var(--accent)}
.code{font-family:var(--mono);font-size:13px;background:var(--ground);border:1px solid var(--line);border-radius:10px;padding:10px 12px;word-break:break-all;user-select:all}
.empty{text-align:center;padding:48px 20px;color:var(--muted)}.empty .ico{width:56px;height:56px;border-radius:50%;background:var(--ground);display:grid;place-items:center;margin:0 auto 14px;color:var(--muted)}.empty b{display:block;color:var(--text);font-size:18px;margin-bottom:4px}.empty a{color:var(--accent);font-weight:600}.sub-words{margin:-4px 0 10px;font-size:13px}.sub-words a{color:var(--accent);font-weight:600}.empty .line{display:block;max-width:560px;margin:10px auto 0}.nowrap{white-space:nowrap}.empty .hint{display:block;margin-top:14px;font-size:13px}
.login{min-height:100dvh;display:grid;place-items:center;padding:72px 16px 24px}.login .card{width:100%;max-width:400px;padding:28px}/* An address in a heading can be longer than a phone is wide: it breaks where it must and the card keeps its width. */.login .card{min-width:0}.login .empty{overflow-wrap:anywhere;min-width:0}.empty .address{display:block;margin-bottom:8px;color:var(--text);font-weight:600}/* A card page whose words sit above a form, not alone: less air around them, and the line that offers the other way out is centred like them. */.login .empty.compact{padding:8px 8px 22px}.login .form .alone{margin:0;text-align:center}.login .form .center-link{justify-self:center;padding:4px 8px}
/* The login card arrives, once: it rises a little as it clears, its parts a beat behind each other. Only opacity and transform move, nothing waits for it (the first box has the focus from the first frame), a page that came back with an error stands still, and whoever asked their system for less motion gets none. */
/* THE LOGIN PAGE HOLDS ITS FORM BACK WHILE IT ASKS whether the browser holds a login (Simon 2026-09-29: whoever is led on saw the form for a blink first). WHAT SHOWS MEANWHILE IS A LOADING SCREEN, NOT AN EMPTY FORM (his catch the same day: the card with nothing in it read as one): the name and a ring under it, on the page itself, the card's box not drawn. The practice of launch screens: the mark, centred, a quiet motion, no words; a ring is the sign for a short wait on who somebody is. While the page asks, the name and the ring stand in the MIDDLE of the page (the card is moved down by half its height less the 70 px from its top to the middle of the two); at the answer the name glides up to its place in the card while the box and the form come. THEY ALWAYS COME: the script brings them at the answer, and with no script at all, or one that broke, the rules below bring them by themselves after three seconds. */
.login .card{position:relative;transition:background-color .25s ease-out,border-color .25s ease-out,box-shadow .25s ease-out,transform .4s cubic-bezier(.16,1,.3,1)}
.login .checking-sign{display:none}
.login.checking .card{background:transparent;border-color:transparent;box-shadow:none;transform:translateY(calc(50% - 70px));animation:checked-card 0s linear 3s forwards}
body.login.checked:not(.checking) .card{animation:none}
.login.checking .form{visibility:hidden;animation:checked 0s linear 3s forwards}
.login.checking .checking-sign{display:flex;position:absolute;inset:0;justify-content:center;align-items:center;margin:0;color:var(--muted);font-size:14px;animation:checking-ends 0s linear 3s forwards}
.checking-sign .ring{width:26px;height:26px;border-radius:50%;border:2.5px solid var(--line);border-top-color:var(--accent)}
.checking-sign .words{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
@keyframes checked{to{visibility:visible}}
@keyframes checked-card{to{background:var(--surface);border-color:var(--line);box-shadow:var(--shadow);transform:none}}
@keyframes welcome-waiting{from{opacity:0}}
@keyframes checking-ends{to{visibility:hidden}}
@keyframes turn{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:no-preference){.checking-sign .ring{animation:turn .8s linear infinite}}
@media (prefers-reduced-motion:reduce){.checking-sign .ring{display:none}.checking-sign .words{position:static;width:auto;height:auto;overflow:visible;clip-path:none}}
@keyframes welcome{from{opacity:0;transform:translateY(12px)}}
@media (prefers-reduced-motion:no-preference){body.login:not(.still) .card{animation:welcome .5s cubic-bezier(.16,1,.3,1) both}body.login.checking:not(.still) .card{animation:welcome-waiting .5s cubic-bezier(.16,1,.3,1) both,checked-card 0s linear 3s forwards}.login:not(.still) .page-top .brand,body.login:not(.still):not(.checking) .card .form>*{animation:welcome .55s cubic-bezier(.16,1,.3,1) both}.login:not(.still) .page-top .brand{animation-delay:.05s}body.login:not(.still) .card .form>:nth-child(2){animation-delay:.1s}body.login:not(.still) .card .form>:nth-child(3){animation-delay:.15s}body.login:not(.still) .card .form>:nth-child(4){animation-delay:.2s}body.login:not(.still) .card .form>:nth-child(n+5){animation-delay:.25s}}.login .page-top{position:fixed;inset-block-start:0;inset-inline-start:0;padding:14px 24px;z-index:1}.login .page-top .brand{padding:4px 0;color:inherit;text-decoration:none}/* The brand sits in the corner like the public pages' and leads home (Simon, 2026-10-03: no way out of the login form). The card centres under it, never behind it. */
.drawer-bg{display:none;position:fixed;inset:0;background:rgba(0,0,0,.35);z-index:9}
body.offline main{max-width:480px;margin:80px auto;padding:0 20px}
.chips{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:14px}.chip{display:inline-flex;align-items:center;gap:6px;height:30px;padding:0 12px;border:1px solid var(--line);border-radius:999px;background:var(--surface);font-size:13px;font-weight:500;color:var(--text)}.chip:hover{border-color:var(--muted)}.chip.on{background:var(--accent-soft);border-color:var(--accent);color:var(--accent)}.chip .x{font-size:16px;line-height:1;margin-right:-3px}.chev{width:32px;height:32px;padding:0;border-radius:8px;border:1px solid transparent;background:transparent;color:var(--muted);display:grid;place-items:center;flex:none;margin-left:auto;cursor:pointer}.chev:hover,.chev[aria-expanded="true"]{color:var(--text);border-color:var(--line);background:var(--surface)}.row .chev:hover{background:var(--ground)}.row.opened .chev{background:var(--surface)}.chev svg{transition:transform .15s}.chev[aria-expanded="true"] svg{transform:rotate(180deg)}.list.wide .item .seen{order:3}.list .item.opened{border-radius:10px 10px 0 0}.list .drawer{margin-top:-8px;border:1px solid var(--line);border-top:0;border-radius:0 0 10px 10px;background:var(--surface);padding:12px 14px 12px 13px}.row.opened,.row.drawer{background:var(--ground)}.row.drawer{grid-template-columns:1fr;border-top:0;padding:0 18px 16px 52px}.drawer>.seen{margin:0}.drawer .links{margin:8px 0 0;font-size:13px;color:var(--muted)}
/* A visit's drawer tells each connection the person has open on the machine under the name of its kind (fleet/visits.py), in the look of a list's day head. */
.story-kind{margin:12px 0 4px;font-size:12px;font-weight:600;color:var(--muted);letter-spacing:.06em;text-transform:uppercase}.story-kind:first-child{margin-top:0}.drawer .links a{color:var(--accent)}.story{list-style:none;margin:0;padding:0}.story>li{position:relative;display:grid;grid-template-columns:82px 1fr;gap:12px;padding:5px 0 5px 20px;font-size:14px}.story>li::before{content:"";position:absolute;left:0;top:12px;width:8px;height:8px;border-radius:50%;background:#B4BAC6}.story>li::after{content:"";position:absolute;left:3px;top:22px;bottom:-7px;width:2px;background:var(--line)}.story>li:last-child::after{display:none}.story>li.ok::before{background:var(--online);box-shadow:0 0 0 3px var(--online-soft)}.story>li.info::before{background:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}.story>li.live::before{background:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}.story>li.warn::before{background:var(--warn);box-shadow:0 0 0 3px var(--warn-soft)}.story>li.bad::before{background:var(--danger);box-shadow:0 0 0 3px var(--danger-soft)}.story time{color:var(--muted);font-size:13px;font-variant-numeric:tabular-nums;padding-top:1px;white-space:nowrap}.story .what{min-width:0;overflow-wrap:anywhere}.story .what b{font-weight:600}.story .note{color:var(--muted)}.story .files{list-style:none;margin:4px 0 0;padding:0;font-size:13px;color:var(--muted)}.story .files li{display:flex;justify-content:space-between;gap:12px;padding:3px 0;border-top:1px dashed var(--line)}
/* The name wraps, the size never breaks (2026-10-15, his look on the phone: a long name pushed `0 bytes` into two lines, broken mid word). */
.story .files li span:first-child{min-width:0;overflow-wrap:anywhere}.story .files li span:last-child{flex:none;white-space:nowrap}
@media (max-width:1100px){.row{grid-template-columns:20px 1.6fr 1fr 140px 100px}.row.watch{grid-template-columns:160px minmax(0,1.2fr) minmax(0,.8fr) 160px 80px 36px}.row.watch .times{display:none}.row.watch .kind .mini{display:inline-flex}.row.users{grid-template-columns:minmax(0,1.6fr) 100px minmax(0,1fr) 90px 170px 44px}.row.users .reach{display:none}.row .grants,.row .acts{display:none}.two{grid-template-columns:1fr}}
@media (max-width:760px){.list.wide .item .acts{order:3;flex:0 0 100%;justify-content:flex-end}.list.live .item{grid-template-columns:10px minmax(0,1fr) auto;grid-template-areas:"dot title timer" "dot seen seen" "acts acts acts";row-gap:6px}.list.live .item .timer{border-right:0;padding:0;margin:0;align-self:start}.list.live .item .timer time{font-size:17px}.list.live .item .acts{justify-content:flex-end}.story>li{grid-template-columns:1fr;gap:2px}.row{position:relative}.row .chev{position:absolute;right:8px;top:8px}.row.drawer{padding:0 14px 14px}.row.drawer .chev{display:none}:root{--gap:18px;--bar:65px}.app{grid-template-columns:1fr}.rail{position:fixed;left:0;top:0;width:280px;transform:translateX(-100%);transition:transform .2s;z-index:10;box-shadow:var(--shadow)}body.open .rail{transform:none}body.open .drawer-bg{display:block}.menu{display:grid}.main{padding:14px 16px 90px}.top{margin:-14px -16px 14px;padding:10px 16px}.top .top .spacer{display:none}.search{max-width:none}
.head .btn.primary{width:100%}.head .acts{flex:1 1 100%}.head .acts .btn{flex:1}.head .acts.beside{flex:0 0 auto}.head .acts.beside .btn{flex:none}.head:has(>.acts.beside)>div:first-child{flex:1 1 0;min-width:0}.foot.more .btn{width:100%}h1{font-size:22px}.books{overflow-x:auto;flex-wrap:nowrap;padding-bottom:4px;margin-inline:-16px;padding-inline:16px}.books::-webkit-scrollbar{display:none}.pill{flex:none}.books.wrap{flex-wrap:wrap;overflow:visible;margin-inline:0;padding-inline:0}.list .item{flex-wrap:wrap}.list .item b,.list .item .title{flex:1 1 60%;min-width:0}.list .item .seen{flex:0 0 100%}.list .item .dot~.seen{padding-left:20px}
.row.users,.row.books,.row.entries,.row.grants{grid-template-columns:1fr;gap:6px}.cust-facts{grid-template-columns:minmax(0,1fr)}.card-head{flex-wrap:wrap}.card-head>.btn,.card-head>form,.card-head>.acts{margin-left:auto}.row.day{padding:8px 14px}.row.users:first-child,.row.books:first-child,.row.entries:first-child,.row.grants:first-child{display:none}.row .acts{justify-content:flex-end}.row.health{grid-template-columns:minmax(0,1fr) 32px}.row.ban{grid-template-columns:minmax(0,1fr) auto}.row.health,.row.ban{padding:10px 12px;row-gap:6px}.row.health .status{grid-column:1;justify-self:start;white-space:normal;text-align:left}.row.health .what{grid-column:1}.row.health .chev{grid-column:2;grid-row:1}.row.health .chev{position:static}.health-groups{grid-template-columns:1fr}[data-part="health"] .card-head,[data-part="bans"] .card-head{flex-wrap:wrap}[data-part="health"] .card-head .acts,[data-part="bans"] .card-head .acts{width:100%;justify-content:flex-start}}
@media (prefers-reduced-motion:reduce){.live-pill i::after{animation:none}.list .item.arrived{animation:none}*{transition:none!important}}

@media (max-width:760px){.list .item .when{flex:none;width:auto;padding-top:2px}.inbox-panel,.pop{position:fixed;left:16px;right:16px;top:74px;width:auto}.toast{left:16px;right:16px;top:auto;bottom:calc(16px + env(safe-area-inset-bottom,0px));width:auto;max-width:none}.toast.gone{transform:translateY(8px)}.plan-part+.plan-part,.plan-words b{display:none}.plan-words small{font-size:13px;font-weight:600;color:var(--text)}.plan.att .dot{background:var(--warn);box-shadow:none}.plan.off .dot{background:var(--danger);box-shadow:none}.setting{align-items:flex-start;gap:14px}.setting .toggle{margin-top:2px}}
/* Small helpers that replace what used to be inline styles. The content security policy drops inline styles, so a one off adjustment is a class here. They carry !important because that is the weight an inline style had. */
.start{justify-content:flex-start!important}.wrap{flex-wrap:wrap!important}.gap-6{gap:6px!important}.gap-12{gap:12px!important}.w-auto{width:auto!important}.w-110{width:110px!important}.w-200{width:200px!important}.w-220{width:220px!important}.box-14{width:14px!important;height:14px!important}.m-0{margin:0!important}.m-b6{margin:0 0 6px!important}.mt-n6{margin-top:-6px!important}.mt-12{margin-top:12px!important}.mt-14{margin-top:14px!important}.mt-16{margin-top:16px!important}.mt-18{margin-top:18px!important}.mb-14{margin-bottom:14px!important}.mr-8{margin-right:8px!important}.ml-auto{margin-left:auto!important}.underline{text-decoration:underline!important}.rel{position:relative!important}.inline{display:inline!important}.inline-block{display:inline-block!important}.inline-row{display:inline-flex!important;align-items:center!important}.pointer{cursor:pointer!important}.no-marker{list-style:none!important}.drop{position:absolute!important;right:0!important;top:52px!important;width:min(92vw,520px)!important;z-index:5!important;text-align:left!important;font-weight:400!important;color:var(--text)!important;white-space:normal;cursor:auto}

/* A FINGER NEEDS 44px (Simon, 2026-09-21: "do them all"; measured on 16 pages at phone width, the small button was 34px high, the theme switch 34, Show in a password
   box 32, the close and expand icons 32, a filter pill 36, a chip 30, a switch 24, a chip's remove cross 22). ONLY WHERE THE SCREEN IS TOUCHED (`pointer:coarse`), so a
   desktop keeps its look to the pixel. What is a button GROWS (small buttons, pills, chips). What is an icon, or sits inside something else, KEEPS ITS LOOK and gets
   an area of 44px to tap, laid over it without being seen. A checkbox is tapped by its label, which takes the height. Links inside a sentence stay as they are. */
/* THE SETTINGS PAGE (Simon, 2026-09-23, docs/plan.md has the sources). A card set apart: an accent edge and a soft ring, the mark beside its title. A slider
   for how long logs are kept, with the exact number beside it (the practice for a slider whose value matters: the two are linked); the track is painted up
   to the thumb through a custom property the script sets. Saved after a switch flips sits at the right of the sub head. */
.card.accent{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft),var(--shadow)}.card-head .with-mark{display:flex;align-items:center;gap:10px;min-width:0}
.card-head .mark{width:28px;height:28px;display:block;flex:none}
/* SAVED IS GREEN EVERYWHERE, one rule for every head (2026-10-15, his look: grey in the Settings cards' heads while green in a sheet's parts); what could not save stands in its place in red. In a card head it sits at the right, before the card's button. */
[data-saved]{font-weight:500;color:var(--online)}[data-saved].bad{color:var(--danger)}.card-head>[data-saved]{margin-inline-start:auto}.sub-head.plain{border-top:0;padding-top:0;margin-top:16px}
.rows .row-line{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.rows .address{flex:1 1 160px;min-width:0;overflow-wrap:anywhere}.rows .row-line form{margin-left:auto}.rows .status{margin:0;flex:none}
.range{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:14px;align-items:start}.range .track{display:grid;gap:2px;min-width:0}.range .days{display:flex;align-items:center;gap:8px;font-size:14px;color:var(--muted)}.range .days input{width:96px;color:var(--text)}
.range-ends{display:flex;justify-content:space-between;font-size:12px;color:var(--muted);margin-top:-6px}
input[type=range]{-webkit-appearance:none;appearance:none;height:44px;padding:0;border:0;border-radius:0;background:transparent;width:100%;cursor:pointer;--fill:0%}
input[type=range]::-webkit-slider-runnable-track{height:6px;border-radius:3px;background:linear-gradient(to right,var(--accent) var(--fill),var(--line) var(--fill))}
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:22px;height:22px;border-radius:50%;background:var(--surface);border:2px solid var(--accent);margin-top:-8px;box-shadow:0 1px 3px rgba(21,23,28,.2)}
input[type=range]::-moz-range-track{height:6px;border-radius:3px;background:var(--line)}input[type=range]::-moz-range-progress{height:6px;border-radius:3px;background:var(--accent)}
input[type=range]::-moz-range-thumb{width:22px;height:22px;border-radius:50%;background:var(--surface);border:2px solid var(--accent);box-shadow:0 1px 3px rgba(21,23,28,.2)}
input[type=range]:focus-visible{outline:0}input[type=range]:focus-visible::-webkit-slider-thumb{outline:2px solid var(--accent);outline-offset:2px}input[type=range]:focus-visible::-moz-range-thumb{outline:2px solid var(--accent);outline-offset:2px}
@media (max-width:520px){.range{grid-template-columns:minmax(0,1fr);gap:8px}.range .days{justify-content:flex-end}}
@media (pointer:coarse){
.btn.small{height:44px;padding:0 16px;font-size:14px}.pill,.chip{height:44px}.chips-input .chip{height:36px}.switcher .leave{min-height:44px}
.seg button,.modal .x,.toast .x,.search .x,.chev,.chips-input .chip button,input[type=checkbox].switch{position:relative}
.seg button::after,.modal .x::after,.toast .x::after,.search .x::after,.chev::after,.pw-show::after,.chips-input .chip button::after{content:"";position:absolute}
.seg button::after{inset:-5px 0}.seg.filter a{position:relative}.seg.filter a::after{content:"";position:absolute;inset:-5px 0}.modal .x::after,.toast .x::after{inset:-6px}.search .x::after{inset:-4px}@media (pointer:coarse){.modal .x::after{inset:0}}.chev::after{inset:-8px}.pw-show::after{inset:-6px 0}.chips-input .chip button::after{inset:-11px -5px}
.toggle::before,input[type=checkbox].switch::before{content:"";position:absolute;inset:-10px -2px}
details.btn>summary::after{content:"";position:absolute;inset:0}
.field.check{min-height:44px}.field.check label{display:flex;align-items:center;min-height:44px;margin-left:-32px;padding-left:32px}
.glance .fleet-key{margin:-3px 0 -11px}.fleet-key a,.fleet-key span{min-height:44px}
.platform-line{gap:0 24px}.platform-line>a{min-height:44px}.month-pick .btn.small{width:44px}.usage-now .quiet-link{position:relative}.usage-now .quiet-link::after{content:"";position:absolute;inset:-12px -8px}.attn-cell button.status::after{content:"";position:absolute;inset:-11px -10px}
.glance-note a,.glance .card-head .quiet-link{position:relative}.glance-note a::after{content:"";position:absolute;inset:-14px 0}.glance .card-head .quiet-link::after{content:"";position:absolute;inset:-12px -8px}
}

/* DEVICES, 2026-09-27: books live on this page (docs/books-into-devices-2026-09-27.md, every element by the practice). The tiles are filters, the books a pill row with New book at its end, a book's head strip with Edit book, a toolbar with the search and the tag chips that a selection bar replaces, a table with a checkbox column, one Status column of a dot and its words, an ID with a copy button, an Access column, rows that open on a click, a sticky header; cards on a phone; the panel a sheet from the right. */
.who-group{display:inline-flex;align-items:center}.who{width:26px;height:26px;border-radius:50%;background:var(--accent-soft);color:var(--accent);display:grid;place-items:center;font-weight:700;font-size:10px;border:2px solid var(--surface);margin-left:-8px;flex:none}.who:first-child{margin-left:0}.who.more{background:var(--ground);color:var(--muted);border-color:var(--surface);font-size:11px}
.pick{display:grid;place-items:center;width:24px;height:24px;margin:-3px;cursor:pointer}
.tabs{display:flex;align-items:flex-end;justify-content:space-between;gap:16px;border-bottom:1px solid var(--line);margin-bottom:14px}.tab-row{display:flex;gap:2px;min-width:0;margin-bottom:-1px;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;scrollbar-width:none}.tab-row::-webkit-scrollbar{display:none}.tab{display:inline-flex;align-items:center;gap:8px;padding:10px 12px 12px;border-bottom:2px solid transparent;color:var(--muted);font-weight:500;white-space:nowrap;min-height:44px}.tab:hover{color:var(--text)}.tab.on{color:var(--accent);border-bottom-color:var(--accent);font-weight:600}.tab>i{font-style:normal;font-size:12px;line-height:1;color:var(--muted);font-variant-numeric:tabular-nums;background:var(--ground);border:1px solid var(--line);border-radius:999px;padding:4px 7px}.tab.on>i{color:var(--accent);background:var(--accent-soft);border-color:transparent}.tab>span{max-width:220px;overflow:hidden;text-overflow:ellipsis}.tab .new-mark{width:6px;height:6px;border-radius:50%;background:var(--accent);display:inline-block;flex:none}.tabs .new-book{margin:0 0 8px;flex:none}.tabs .new-book:disabled{opacity:.5;cursor:not-allowed}.tab-gap{width:1px;align-self:stretch;margin:10px 6px;background:var(--line);flex:none}.tab.system{color:var(--muted)}.tab.system svg{flex:none}.tab>i.warn{background:var(--warn-soft);color:var(--warn);border-color:transparent}.tab.system.on{color:var(--accent)}.seg.filter{gap:0;max-width:100%;overflow:hidden}.seg-scroll{display:flex;align-items:center;gap:2px;flex:1 1 auto;min-width:0;height:100%;margin:-4px;padding:4px;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;scrollbar-width:none}.seg-scroll::-webkit-scrollbar{display:none}.seg.filter a{height:34px;padding:0 12px;border-radius:999px;display:inline-flex;align-items:center;gap:6px;color:var(--muted);font-size:14px;font-weight:500;white-space:nowrap}.seg.filter a:hover{color:var(--text)}.seg.filter a.on{background:var(--accent-soft);color:var(--accent);font-weight:600}.seg.filter i{font-style:normal;font-size:12px;font-variant-numeric:tabular-nums;opacity:.85}.seg.filter .dot{width:8px;height:8px}.combo{position:relative}.combo input{padding-right:38px;border-radius:12px;cursor:text}.combo>svg{position:absolute;right:12px;top:14px;pointer-events:none;color:var(--muted)}.combo ul,.picker-list{position:absolute;top:calc(100% + 6px);left:0;right:0;max-height:320px;overflow-y:auto;margin:0;padding:6px;list-style:none;background:var(--surface);border:1px solid var(--line);border-radius:12px;box-shadow:var(--shadow);z-index:7}.combo li,.picker-list li{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:10px 12px;border-radius:8px;cursor:pointer;min-height:40px}.combo li:hover,.combo li.hi,.picker-list li:hover,.picker-list li.hi{background:var(--ground)}.combo li[aria-selected=true]{color:var(--accent);font-weight:600}.combo li i{font-style:normal;color:var(--muted);font-size:12px;font-variant-numeric:tabular-nums}.combo li>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tag-picker{position:relative}.tag-picker .picker-list{position:static;margin-top:6px;max-height:220px;box-shadow:none}.tag-picker .chips-input input{flex:1 1 140px;min-width:0;border:0;height:30px;padding:0 4px;font-size:15px;background:none;outline:0}.tag-picker .chip.some{background:transparent;border:1px dashed var(--accent);color:var(--accent)}.picker-list [data-create]{color:var(--accent);font-weight:600}.picker-list [data-create] b{margin-left:4px}.combo ul,.boxes.tall,.list.tall,dialog.modal.sheet,.modal-scroll{scrollbar-width:thin;scrollbar-color:var(--line) transparent}
/* THE RAIL'S MENU SAYS IT SCROLLS (Simon 2026-10-09, "small fade min fo2 w min ta7et to show its scrollable, w zabbit l scrollbar"): a 24 px fade at the end that holds more, marked by the same script as the rows that scroll sideways (`data-more`, lists.js); the fade is a mask, so it holds in both themes and never paints over the words (the practice: a masked viewport, faded only where more remains). THE BAR (his question the same night, a mouse with no wheel): thin, its gutter reserved so the links never shift, invisible until the pointer is over the menu or the menu holds the keyboard's focus (the VS Code and Gmail labels practice; the W3C note on hidden scrollbars: some judge length by them, some drag them), none at all on a touch screen, where a finger drags and the fade is the cue. The menu takes the focus too (`tabindex`), so the arrow keys, Page Up and Down and Space scroll it. The bar rides the rail's edge, not the items' highlight (his word: "kheda 3al edge mich fo2 l selection"): the menu's box reaches the rail's border through its padding. */
.rail .nav{margin-right:-16px;padding-right:16px;scrollbar-width:thin;scrollbar-gutter:stable;scrollbar-color:transparent transparent;transition:scrollbar-color .15s}.rail .nav:hover,.rail .nav:focus-visible{scrollbar-color:var(--line) transparent}@media (pointer:coarse){.rail .nav{scrollbar-width:none;scrollbar-gutter:auto}}.rail .nav[data-more="bottom"]{-webkit-mask-image:linear-gradient(to bottom,#000 calc(100% - 24px),transparent);mask-image:linear-gradient(to bottom,#000 calc(100% - 24px),transparent)}.rail .nav[data-more="top"]{-webkit-mask-image:linear-gradient(to bottom,transparent,#000 24px);mask-image:linear-gradient(to bottom,transparent,#000 24px)}.rail .nav[data-more="top bottom"]{-webkit-mask-image:linear-gradient(to bottom,transparent,#000 24px,#000 calc(100% - 24px),transparent);mask-image:linear-gradient(to bottom,transparent,#000 24px,#000 calc(100% - 24px),transparent)}
/* A menu link's hover lives under a pointer alone: a touch screen keeps :hover on the last tapped link, so a tapped item stood gray beside the current one in the iPhone's drawer (Simon 2026-10-09, "fi a 2nd selection 3am betbayyine 3al iphone"; the practice, hover styles behind the hover media query, a pressed state of its own). */
@media (hover:hover){.nav a:not(.on):hover{background:var(--ground)}}.combo ul::-webkit-scrollbar,.picker-list::-webkit-scrollbar,.boxes.tall::-webkit-scrollbar,.list.tall::-webkit-scrollbar,dialog.modal.sheet::-webkit-scrollbar,.modal-scroll::-webkit-scrollbar,.rail .nav::-webkit-scrollbar{width:6px}.combo ul::-webkit-scrollbar-track,.boxes.tall::-webkit-scrollbar-track,.list.tall::-webkit-scrollbar-track,dialog.modal.sheet::-webkit-scrollbar-track,.modal-scroll::-webkit-scrollbar-track,.rail .nav::-webkit-scrollbar-track{background:transparent}.combo ul::-webkit-scrollbar-thumb,.boxes.tall::-webkit-scrollbar-thumb,.list.tall::-webkit-scrollbar-thumb,dialog.modal.sheet::-webkit-scrollbar-thumb,.modal-scroll::-webkit-scrollbar-thumb,.rail .nav::-webkit-scrollbar-thumb{background:var(--line);border-radius:3px}.combo ul::-webkit-scrollbar-thumb:hover,.boxes.tall::-webkit-scrollbar-thumb:hover,.list.tall::-webkit-scrollbar-thumb:hover,dialog.modal.sheet::-webkit-scrollbar-thumb:hover,.modal-scroll::-webkit-scrollbar-thumb:hover,.rail .nav::-webkit-scrollbar-thumb:hover{background:var(--muted)}.rail .nav::-webkit-scrollbar-thumb{background:transparent}.rail .nav:hover::-webkit-scrollbar-thumb{background:var(--line)}.select{position:relative;display:inline-flex;min-width:0}.select select{appearance:none;-webkit-appearance:none;width:auto;max-width:260px;padding:0 38px 0 14px;border-radius:12px;cursor:pointer}.select svg{position:absolute;right:12px;top:50%;transform:translateY(-50%);pointer-events:none;color:var(--muted)}.head .acts .new-book:disabled{opacity:.5;cursor:not-allowed}.chip-more{display:inline-flex;align-items:center;height:26px;padding:0 4px;border:0;border-radius:6px;background:none;color:var(--accent);font:inherit;font-size:12px;font-weight:600;white-space:nowrap;cursor:pointer}.chip-more:hover{text-decoration:underline}.part-head .btn.small{height:30px;padding:0 10px}.part-head>h3{margin-right:auto}.search.narrow{margin-bottom:10px}.boxes.tall,.list.tall{max-height:320px;overflow-y:auto}.boxes.tall label[hidden],.list.tall .item[hidden]{display:none}.toolbar .clear{font-size:13px;color:var(--accent);font-weight:600}.limit-line{margin:-4px 0 14px}
/* The head of a book (Devices page, 2026-10-15, his look: "too blah"): a mark, the name over its facts, the one action small on the right, on the phone too (the head rule: a secondary action never takes a row of its own). */
.book-head{display:flex;flex-wrap:wrap;align-items:center;gap:14px;padding:14px 18px;margin-bottom:14px}.book-mark{width:40px;height:40px;border-radius:12px;background:var(--accent-soft);color:var(--accent);display:grid;place-items:center;flex:none}.book-head .book-words{display:grid;gap:3px;min-width:0;flex:1 1 160px}.book-head .book-words>b{font-size:16px;line-height:1.3}.book-head .book-words .seen{font-size:13px;line-height:1.45}.book-head .book-words .tag{justify-self:start;margin-top:2px}.book-head .btn{flex:none;margin-left:auto;white-space:nowrap}
.toolbar{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:14px;min-height:44px}.toolbar .search{flex:1 1 220px;max-width:360px}.toolbar .chips{margin:0}
.select-bar{position:sticky;top:var(--bar);z-index:6;display:grid;grid-template-columns:1fr auto auto;grid-template-areas:"count acts clear";align-items:center;gap:12px;min-height:56px;padding:6px 6px 6px 16px;margin-bottom:14px;border-radius:12px;background:var(--accent-soft);border:1px solid color-mix(in srgb,var(--accent) 30%,transparent);font-weight:600;color:var(--accent);box-shadow:var(--shadow)}.select-bar [data-select-count]{grid-area:count}.select-bar .acts{grid-area:acts;flex-wrap:wrap}.select-bar .x{grid-area:clear;width:36px;height:36px;border-radius:50%;display:grid;place-items:center;color:var(--accent)}.select-bar .x:hover{background:color-mix(in srgb,var(--accent) 12%,transparent)}
.row.devices{grid-template-columns:24px minmax(0,1.5fr) 200px minmax(0,1fr) minmax(0,.8fr) 140px 100px 44px;cursor:pointer;min-height:56px}.row.devices .name>a,.row.devices .name>a>b,.row.devices .name>span:not(.marks):not(.row-note):not(.chips-line){display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.row.devices:not(:first-child):hover{background:var(--ground)}.row.devices.picked{background:color-mix(in srgb,var(--accent-soft) 60%,var(--surface))}.row.devices input[type=checkbox]{display:block}
.state{display:inline-flex;align-items:center;gap:8px;font-size:13px;color:var(--muted)}.state.on{color:var(--online);font-weight:600}.state.off{color:var(--danger);font-weight:600}.state .dot{margin:0}
.name .marks{display:flex;gap:6px;flex-wrap:wrap;margin-top:4px}.chips-line{display:flex;flex-wrap:wrap;gap:4px;align-items:center}.name .chips-line{margin-top:4px}.tag.outline{background:transparent;border:1px dashed var(--line);color:var(--muted);font-weight:500}
.id-cell{display:inline-flex;align-items:center;gap:2px;min-width:0}.copy-btn{width:28px;height:28px;border-radius:8px;display:grid;place-items:center;color:var(--muted);flex:none}.copy-btn:hover{background:var(--ground);color:var(--text)}.copy-btn .done{display:none}.copy-btn.copied .done{display:block;color:var(--online)}.copy-btn.copied .idle{display:none}
.boxes{display:grid;border:1px solid var(--line);border-radius:12px;overflow:hidden;background:var(--ground)}.boxes label{display:flex;align-items:center;gap:12px;padding:12px 14px;border-top:1px solid var(--line);cursor:pointer;min-height:48px}.boxes label:first-child{border-top:0}.boxes label b{font-weight:600}.boxes label .seen{margin-left:auto;text-align:right}
dialog.modal.sheet{max-width:480px;width:min(480px,100%);height:100dvh;max-height:100dvh;margin:0 0 0 auto;border-radius:0;padding:0 24px 40px;overflow-y:auto;overflow-x:hidden;box-shadow:-16px 0 48px rgba(0,0,0,.25)}dialog.modal.sheet>.modal-head{position:sticky;top:0;z-index:2;background:var(--surface);padding:24px 0 10px;margin-bottom:0;box-shadow:0 8px 12px -10px rgba(0,0,0,.15)}dialog.modal.sheet .sheet-part{padding:18px 0;border-top:1px solid var(--line)}dialog.modal.sheet .sheet-part:first-of-type{border-top:0;padding-top:8px}.sheet-part h3{margin:0 0 10px;font-size:15px;font-weight:700}.part-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:10px}.part-head h3{margin:0}.matrix{width:100%;border-collapse:separate;border-spacing:0;border:1px solid var(--line);border-radius:12px;background:var(--ground);overflow:hidden;font-size:14px}.matrix th,.matrix td{padding:12px 14px;border-top:1px solid var(--line);text-align:left;vertical-align:middle}.matrix thead th{border-top:0;padding:10px 14px 8px;font-size:12px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);background:var(--surface)}.matrix tbody th{font-weight:400}.matrix tbody th b{display:block;font-weight:600}.matrix tbody th .seen{display:block;font-size:13px}.matrix th:not(:first-child),.matrix td{width:64px;text-align:center;padding-left:8px;padding-right:8px}.matrix td .switch{vertical-align:middle}.sheet-part .list .item form[data-autosave]{display:inline-flex;align-items:center}.sheet-part .list .item{flex-wrap:wrap}.sheet-part .list .item .seen{flex:1 1 100%}.sheet-part .tags{margin-bottom:10px}
/* HOW A SHEET MOVES (Simon 2026-10-08, "on the phone the sheets blink and don't rise and fall properly"; docs/plan.md has the practice and the sources): whole, from its edge and back to it, never fading, since a sheet that fades lets the page show through it; the dimmed page fades in and out with it. One length and one curve both ways, those of the web's common drawer (0.5 s, cubic-bezier(.32,.72,0,1), the curve of the phone's own sheets). sheets.js puts `closing` on and closes the sheet when the slide ends; a sheet that comes back after a form sent from inside it was never seen to leave and stands at once (`still`, app.js). */
@media (prefers-reduced-motion:no-preference){dialog.modal.sheet[open]{animation:sheet-in .5s cubic-bezier(.32,.72,0,1)}dialog.modal.sheet[open]::backdrop{animation:dim .5s cubic-bezier(.32,.72,0,1)}dialog.modal.sheet.closing{animation:sheet-out .5s cubic-bezier(.32,.72,0,1) forwards}dialog.modal.sheet.closing::backdrop{animation:undim .5s cubic-bezier(.32,.72,0,1) forwards}dialog.modal.sheet.still:not(.closing),dialog.modal.sheet.still:not(.closing)::backdrop{animation:none}}@keyframes sheet-in{from{transform:translateX(100%)}}@keyframes sheet-out{to{transform:translateX(100%)}}@keyframes undim{to{opacity:0}}dialog.modal.sheet.closing{pointer-events:none}
/* THE PAGE WAITS FOR ITS SHEET (early.js, 2026-10-08): held unseen, so the browser keeps the last page on the screen, until app.js shows it where it stood with its sheet open; by itself after a second, whatever happens. */
html.held body{visibility:hidden;animation:checked 0s linear 1s forwards}
/* A ROW THAT OPENS ON A TAP answers the finger at once in the tone a pointer gets over it, instead of the browser's own flash of a coloured box (2026-10-08). */
/* NOTHING TAPPABLE SELECTS (Simon 2026-10-08: a swipe that closed a sheet left the title selected): the sheet's head and handle, the sheet while it is pulled, every button, pill, tab, toggle, chevron and switch row. The words of a row and a sheet stay selectable, their ID has Copy. */
dialog.modal.sheet>.modal-head,.modal.sheet.pulled,.modal.sheet.closing,button,.btn,.icon-btn,.x,.chev,.tab,.tab-row,.seg a,.pill,.toggle,.switch,.copy-btn,.nav a,.fold,.bell-wrap,label.choice,summary,.theme-pick,[data-theme-pick]{user-select:none;-webkit-user-select:none;-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent}
[data-row-link]{-webkit-tap-highlight-color:transparent}@media (pointer:coarse){[data-row-link]:active{background:var(--ground)}}
.empty .empty-act{display:block;margin-top:16px}
@media (max-width:1100px){.row.devices{grid-template-columns:24px minmax(0,1.5fr) 170px minmax(0,1fr) minmax(0,.8fr) 130px 44px}.row.devices .reach{display:none}}
/* A plain user's Devices (2026-10-08): no checkbox, no plug, no tags, no Access column of other people; Your access says when they may connect. */
.row.devices.mine{grid-template-columns:minmax(0,1.6fr) 200px minmax(0,1fr) 140px minmax(0,1fr)}.row.devices.mine .access{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;color:var(--muted)}
@media (max-width:1100px){.row.devices.mine{grid-template-columns:minmax(0,1.5fr) 170px minmax(0,1fr) 130px minmax(0,.9fr)}}
.mcard.devices.mine{grid-template-columns:minmax(0,1fr) auto}.mcard.devices.mine .name{grid-column:1}.mcard.devices.mine .chev{grid-column:2}.mcard.devices.mine .state,.mcard.devices.mine .chips-line,.mcard.devices.mine .meta,.mcard.devices.mine .row-note{grid-column:1/3}
.figures.three{grid-template-columns:repeat(3,minmax(0,1fr))}
@media (max-width:760px){.tabs{flex-wrap:wrap;margin-inline:-16px;padding-inline:16px}.tab-row{flex:1 1 100%;margin-inline:-16px;padding-inline:16px}.tabs .new-book{margin:8px 0 8px auto}.book-head{gap:12px;padding:12px 14px}.toolbar .search{max-width:none;flex-basis:100%}.toolbar .select,.toolbar .combo{flex:1 1 100%;min-width:0}.select-bar{grid-template-columns:1fr auto;grid-template-areas:"count clear" "acts acts";padding:10px 12px}.select-bar .acts{justify-content:stretch;flex-wrap:nowrap}.select-bar .acts .btn{flex:1 1 0;min-width:0}.pick{width:44px;height:44px;margin:-13px -10px -13px -13px}dialog.modal.sheet{width:100%;max-width:none;overflow-x:hidden;height:auto;max-height:calc(100dvh - 44px - env(safe-area-inset-top,0px));margin:auto 0 0;border-radius:18px 18px 0 0;padding:8px 16px calc(40px + env(safe-area-inset-bottom,0px));box-shadow:0 -16px 48px rgba(0,0,0,.25)}dialog.modal.sheet::before{content:"";display:block;width:36px;height:4px;border-radius:2px;background:var(--line);margin:0 auto 10px}dialog.modal.sheet>.modal-head{top:-8px;padding:8px 0 8px;box-shadow:none}dialog.modal.sheet::before{position:sticky;top:-8px;z-index:3}dialog.modal.sheet{transform:translateY(var(--pull,0px))}dialog.modal.sheet::backdrop{opacity:calc(1 - var(--pulled,0))}@keyframes sheet-rise{from{transform:translateY(100%)}}@keyframes sheet-sink{to{transform:translateY(100%)}}@media (prefers-reduced-motion:no-preference){dialog.modal.sheet{transition:transform .5s cubic-bezier(.32,.72,0,1)}dialog.modal.sheet::backdrop{transition:opacity .5s cubic-bezier(.32,.72,0,1)}dialog.modal.sheet.pulled,dialog.modal.sheet.pulled::backdrop{transition:none}dialog.modal.sheet[open]{animation:sheet-rise .5s cubic-bezier(.32,.72,0,1)}dialog.modal.sheet.closing{animation:sheet-sink .5s cubic-bezier(.32,.72,0,1) forwards}}}

/* The tag chips on Settings carry their own cross, like the picker's chips. */
.tags form.chip{padding:0 6px 0 12px;gap:4px}.tags form.chip button{width:22px;height:22px;border-radius:50%;display:grid;place-items:center;color:inherit;font-size:15px;line-height:1;background:none;border:0;cursor:pointer;padding:0}.tags form.chip button:hover{background:var(--ground);color:var(--danger)}
/* A limited box's count, under it at the right. */
.field .count{font-size:12px;color:var(--muted);justify-self:end}.field .count.full{color:var(--warn)}
/* The toolbar keeps its shape while a search is typed (the table must never move under the reader, 2026-09-27): Clear filters holds its place unseen through a class, since the stylesheet's [hidden] is display none; and where one row cannot hold every filter the states pill takes a row of its own through a zero height line break in the flex order (a flex item's max width never forces a wrap): under 1600 px, under 1900 px when the book box is in the toolbar, and on a phone, where the link sits beside the tag box and the pill's row is its own. The rows are 16 px apart: the break's line carries a row gap on each side, so the gap is halved. */
.toolbar .clear.idle{visibility:hidden}
/* On a desktop the visual order is the tab order (search, tags, then the pill and Clear filters after it on the second row, 2026-09-28); on a phone the link sits beside the tag box, where a keyboard's order matters least. */
@media (min-width:761px) and (max-width:1599px){.toolbar .seg.filter,.toolbar .clear{order:3}.toolbar{row-gap:8px}.toolbar::after{content:"";flex:0 0 100%;height:0;order:2}}
@media (min-width:761px) and (max-width:1899px){.toolbar.books .seg.filter,.toolbar.books .clear{order:3}.toolbar.books{row-gap:8px}.toolbar.books::after{content:"";flex:0 0 100%;height:0;order:2}}
@media (max-width:760px){.toolbar .clear{order:1;margin-left:auto}.toolbar .seg.filter{order:3}}
/* The device note in the list (Simon 2026-09-28, "under the ip"): one muted line under the machine's line with a note icon, cut with an ellipsis, the whole note in the title; typed text, so isolated in its own bdi. On a phone card it is a row of its own under the facts. */
.row-note{display:flex;align-items:flex-start;gap:6px;min-width:0;max-width:100%;margin-top:3px;color:var(--muted);font-size:13px;font-weight:400}.row-note svg{flex:none;opacity:.8;margin-top:3px}.row-note bdi{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.row-note bdi:has(mark){white-space:normal}.mcard .row-note{grid-column:2;font-size:12px;margin-top:0}
/* ACCESS IN PLACE (Simon 2026-09-28, docs/plan.md): the people who may connect, as rows (a face, the name, when and from where, Rules, the switch, Remove), the rules opening under the row instead of on a page, days as toggle chips, an add row last. */
.people{display:grid;gap:8px}
.person{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:6px 12px;align-items:center;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--ground)}
.person .who{margin:0;width:32px;height:32px;font-size:12px;border:0}
.person .words{display:grid;gap:1px;min-width:0}.person .words b{font-weight:600}.person .words .seen{font-size:13px}
.person .acts{display:flex;align-items:center;gap:8px;margin:0;justify-content:flex-end}.person .acts>form:last-child{margin-left:6px}
.person .rules{grid-column:1/-1;display:grid;gap:12px;padding:12px 0 2px;border-top:1px solid var(--line);margin-top:4px}
.person .rules .label{font-size:13px;font-weight:600;color:var(--muted)}.person .rules .two{grid-template-columns:repeat(2,minmax(0,1fr))}.person .rules .two .field{min-width:0}
.day-picks{display:flex;gap:6px;flex-wrap:wrap}
.day-pick{position:relative;display:inline-flex}.day-pick input{position:absolute;inset:0;width:100%;height:100%;margin:0;opacity:0;cursor:pointer}
.day-pick span{display:inline-flex;align-items:center;justify-content:center;min-width:44px;height:34px;padding:0 10px;border-radius:999px;border:1px solid var(--line);background:var(--surface);color:var(--muted);font-size:13px;font-weight:500}
.day-pick input:checked+span{background:var(--accent-soft);color:var(--accent);border-color:transparent;font-weight:600}
.day-pick:has(input:focus-visible) span{outline:2px solid var(--accent);outline-offset:2px}
[data-address-picker] input{flex:1 1 200px;min-width:0;border:0;height:30px;padding:0 4px;font-size:15px;background:none;outline:0}
.hint.bad{color:var(--danger)}
.picks{display:grid;gap:6px;margin-top:4px}.picks .chips{display:flex;flex-wrap:wrap;gap:6px}
.chip.recent{display:inline-flex;align-items:center;gap:8px;height:34px;padding:0 12px;border:1px solid var(--line);border-radius:999px;background:var(--surface);color:var(--text);font:inherit;font-size:13px;cursor:pointer}
.chip.recent:hover{border-color:var(--accent);color:var(--accent)}.chip.recent .seen{font-size:12px}.chip.recent .place{display:inline-flex;align-items:center;gap:6px;font-variant-numeric:tabular-nums}
/* A pick is a toggle: ticked and tinted while its address is in the list, a tap on a ticked one takes it out (the filter chip practice: the tick says selected, and only then). */
.chip.recent .tick{display:none}.chip.recent[aria-pressed="true"]{background:var(--accent-soft);border-color:transparent;color:var(--accent)}.chip.recent[aria-pressed="true"] .tick{display:inline-block}.chip.recent[aria-pressed="true"] .seen{color:var(--accent);opacity:.8}
.add-person{display:flex;align-items:center;gap:8px;margin-top:12px}.add-person .select{flex:0 1 260px;min-width:0}.add-person select{width:100%;max-width:none}
@media (max-width:760px){.person{grid-template-columns:auto minmax(0,1fr)}.person .acts{grid-column:1/-1;justify-content:flex-end}.add-person .select{flex:1 1 auto}}
/* A search box typed in Arabic runs right to left (dir=auto), and the browser puts its clear cross at the text's end, the left; the box mirrors with it, so the magnifier stands at the right and the cross keeps the left to itself (seen 2026-09-28). */
.search:has(input:dir(rtl)){flex-direction:row-reverse}
/* A row that follows the company default: its permission switches show the default and cannot be touched. */
input[type=checkbox].switch:disabled{opacity:.45;cursor:default}.choice:has(input.switch:disabled) .words{opacity:.6}.matrix tr:has(input.switch:disabled) th{opacity:.6}
.status.own{background:var(--accent-soft);color:var(--accent)}.status.ask{background:var(--warn-soft);color:var(--warn)}
.shown{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.shown .code{font-size:15px;padding:8px 12px;border:1px solid var(--line);border-radius:10px;background:var(--ground)}
.mb-12{margin-bottom:12px}

/* The Own switch of a section stands apart as the master plug (Simon 2026-09-27): a white row with an accent bar, the switch in the accent colour. */
.choice.own{background:var(--surface);box-shadow:inset 4px 0 0 var(--accent)}.choice.own .words b{color:var(--accent)}input[type=checkbox].switch[data-own-switch]:checked{background:var(--accent)}
/* A transfer's file names on a line of their own; a settings card reached by a link sits under the top bar. */
.item .files{flex:1 1 100%;overflow-wrap:anywhere}/* A section a link lands on stands 16 px under the bar: the page's scroll padding alone does it (2026-10-08: cards carried a scroll margin as well, and the two added up to 105 px of room). */
/* The search's matches in a row. */
mark{background:#FFC94D;color:#1B1B1B;padding:0 2px;border-radius:3px}


/* USERS (docs/users-2026-09-28.md): the same table as Devices, a face in the first cell, the sheet from the right. */
.person-cell{display:flex;align-items:center;gap:12px;min-width:0}.person-cell .words{display:grid;gap:2px;min-width:0}.person-cell .words>a{min-width:0}.person-cell .words b{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.person-cell .words>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.row.users .status{min-width:0}.row.users:first-child>span{white-space:nowrap}.toggle:disabled{opacity:.45;cursor:not-allowed}.when-cell{display:grid;gap:2px;font-size:13px}.when-cell .seen{font-size:12px}.when-cell .place{white-space:nowrap}.row.users .reach{font-size:13px}.is-off .avatar{opacity:.55}.avatar.big{width:48px;height:48px;font-size:16px}.identity{align-items:center;gap:14px}.identity-words{display:grid;gap:2px;min-width:0;flex:1}.identity-words h2{padding-top:0;display:flex;align-items:center;flex-wrap:wrap;gap:2px 6px;text-wrap:pretty}.identity-words h2 .mini{margin-left:2px}.identity-words .seen{font-size:13px;overflow-wrap:anywhere;line-height:1.35}.identity .x{align-self:flex-start}form.inline{display:inline;margin:0}.mcard.users .avatar{width:40px;height:40px;font-size:13px;grid-row:1/3;align-self:start}.mcard.users .chips-line{grid-column:2;gap:6px}.mcard.users .meta{grid-column:2}.who.target{background:var(--ground);color:var(--muted);border:1px solid var(--line)}
/* THE FACE'S COLOUR PICKER (Simon 2026-09-28 night): every free colour as a round swatch with the letters on it, across the row, the picked one ringed (the swatch practice: a ring marks the selection), the radios themselves out of sight but reachable. */
.faces{display:flex;flex-wrap:wrap;gap:10px 12px;padding:4px 2px}.face-pick{position:relative;cursor:pointer;border-radius:50%}.face-pick input{position:absolute;inset:0;width:100%;height:100%;margin:0;opacity:0;cursor:pointer}.face-pick .avatar{width:38px;height:38px;font-size:13px;box-shadow:0 0 0 2px transparent;transition:box-shadow .1s,transform .1s}.face-pick:hover .avatar{transform:scale(1.06)}.face-pick input:checked+.avatar{box-shadow:0 0 0 2px var(--surface),0 0 0 4px var(--accent)}.face-pick input:focus-visible+.avatar{outline:2px solid var(--accent);outline-offset:3px}
/* A small marker pill (Simon 2026-09-28 night, "the You chip and the N times chips could look better"): You on the reader's own row, the count of a repeated notice or alert; one look for both. */
.mini{display:inline-flex;align-items:center;height:20px;padding:0 8px;margin-left:6px;border-radius:999px;background:var(--accent-soft);color:var(--accent);font-size:11px;font-weight:600;line-height:1;vertical-align:middle;white-space:nowrap;font-variant-numeric:tabular-nums}.who.target svg{width:14px;height:14px}
/* The search's matches pop (Simon 2026-09-28 night, "a colour that stands out, here and on Devices"): amber like a find in page, dark letters on it in both themes. */
@media (prefers-color-scheme: dark){:root:not([data-theme="light"]) mark{background:#E0A400;color:#0F1115}}
:root[data-theme="dark"] mark{background:#E0A400;color:#0F1115}
/* FACES MADE START: written by accounts/faces.py css(), never by hand; web/test_faces.py holds the two together. */
.h0{--hue:0}.h1{--hue:1}.h2{--hue:2}.h3{--hue:3}.h4{--hue:4}.h5{--hue:5}.h6{--hue:6}.h7{--hue:7}.h8{--hue:8}.h9{--hue:9}.h10{--hue:10}.h11{--hue:11}.h12{--hue:12}.h13{--hue:13}.h14{--hue:14}.h15{--hue:15}.h16{--hue:16}.h17{--hue:17}.h18{--hue:18}.h19{--hue:19}.h20{--hue:20}.h21{--hue:21}.h22{--hue:22}.h23{--hue:23}.h24{--hue:24}.h25{--hue:25}.h26{--hue:26}.h27{--hue:27}.h28{--hue:28}.h29{--hue:29}.h30{--hue:30}.h31{--hue:31}.h32{--hue:32}.h33{--hue:33}.h34{--hue:34}.h35{--hue:35}.h36{--hue:36}.h37{--hue:37}.h38{--hue:38}.h39{--hue:39}.h40{--hue:40}.h41{--hue:41}.h42{--hue:42}.h43{--hue:43}.h44{--hue:44}.h45{--hue:45}.h46{--hue:46}.h47{--hue:47}.h48{--hue:48}.h49{--hue:49}.h50{--hue:50}.h51{--hue:51}.h52{--hue:52}.h53{--hue:53}.h54{--hue:54}.h55{--hue:55}.h56{--hue:56}.h57{--hue:57}.h58{--hue:58}.h59{--hue:59}.h60{--hue:60}.h61{--hue:61}.h62{--hue:62}.h63{--hue:63}.h64{--hue:64}.h65{--hue:65}.h66{--hue:66}.h67{--hue:67}.h68{--hue:68}.h69{--hue:69}.h70{--hue:70}.h71{--hue:71}.h72{--hue:72}.h73{--hue:73}.h74{--hue:74}.h75{--hue:75}.h76{--hue:76}.h77{--hue:77}.h78{--hue:78}.h79{--hue:79}.h80{--hue:80}.h81{--hue:81}.h82{--hue:82}.h83{--hue:83}.h84{--hue:84}.h85{--hue:85}.h86{--hue:86}.h87{--hue:87}.h88{--hue:88}.h89{--hue:89}.h90{--hue:90}.h91{--hue:91}.h92{--hue:92}.h93{--hue:93}.h94{--hue:94}.h95{--hue:95}.h96{--hue:96}.h97{--hue:97}.h98{--hue:98}.h99{--hue:99}.h100{--hue:100}.h101{--hue:101}.h102{--hue:102}.h103{--hue:103}.h104{--hue:104}.h105{--hue:105}.h106{--hue:106}.h107{--hue:107}.h108{--hue:108}.h109{--hue:109}.h110{--hue:110}.h111{--hue:111}.h112{--hue:112}.h113{--hue:113}.h114{--hue:114}.h115{--hue:115}.h116{--hue:116}.h117{--hue:117}.h118{--hue:118}.h119{--hue:119}.h120{--hue:120}.h121{--hue:121}.h122{--hue:122}.h123{--hue:123}.h124{--hue:124}.h125{--hue:125}.h126{--hue:126}.h127{--hue:127}.h128{--hue:128}.h129{--hue:129}.h130{--hue:130}.h131{--hue:131}.h132{--hue:132}.h133{--hue:133}.h134{--hue:134}.h135{--hue:135}.h136{--hue:136}.h137{--hue:137}.h138{--hue:138}.h139{--hue:139}.h140{--hue:140}.h141{--hue:141}.h142{--hue:142}.h143{--hue:143}.h144{--hue:144}.h145{--hue:145}.h146{--hue:146}.h147{--hue:147}.h148{--hue:148}.h149{--hue:149}.h150{--hue:150}.h151{--hue:151}.h152{--hue:152}.h153{--hue:153}.h154{--hue:154}.h155{--hue:155}.h156{--hue:156}.h157{--hue:157}.h158{--hue:158}.h159{--hue:159}.h160{--hue:160}.h161{--hue:161}.h162{--hue:162}.h163{--hue:163}.h164{--hue:164}.h165{--hue:165}.h166{--hue:166}.h167{--hue:167}.h168{--hue:168}.h169{--hue:169}.h170{--hue:170}.h171{--hue:171}.h172{--hue:172}.h173{--hue:173}.h174{--hue:174}.h175{--hue:175}.h176{--hue:176}.h177{--hue:177}.h178{--hue:178}.h179{--hue:179}.h180{--hue:180}.h181{--hue:181}.h182{--hue:182}.h183{--hue:183}.h184{--hue:184}.h185{--hue:185}.h186{--hue:186}.h187{--hue:187}.h188{--hue:188}.h189{--hue:189}.h190{--hue:190}.h191{--hue:191}.h192{--hue:192}.h193{--hue:193}.h194{--hue:194}.h195{--hue:195}.h196{--hue:196}.h197{--hue:197}.h198{--hue:198}.h199{--hue:199}.h200{--hue:200}.h201{--hue:201}.h202{--hue:202}.h203{--hue:203}.h204{--hue:204}.h205{--hue:205}.h206{--hue:206}.h207{--hue:207}.h208{--hue:208}.h209{--hue:209}.h210{--hue:210}.h211{--hue:211}.h212{--hue:212}.h213{--hue:213}.h214{--hue:214}.h215{--hue:215}.h216{--hue:216}.h217{--hue:217}.h218{--hue:218}.h219{--hue:219}.h220{--hue:220}.h221{--hue:221}.h222{--hue:222}.h223{--hue:223}.h224{--hue:224}.h225{--hue:225}.h226{--hue:226}.h227{--hue:227}.h228{--hue:228}.h229{--hue:229}.h230{--hue:230}.h231{--hue:231}.h232{--hue:232}.h233{--hue:233}.h234{--hue:234}.h235{--hue:235}.h236{--hue:236}.h237{--hue:237}.h238{--hue:238}.h239{--hue:239}.h240{--hue:240}.h241{--hue:241}.h242{--hue:242}.h243{--hue:243}.h244{--hue:244}.h245{--hue:245}.h246{--hue:246}.h247{--hue:247}.h248{--hue:248}.h249{--hue:249}.h250{--hue:250}.h251{--hue:251}.h252{--hue:252}.h253{--hue:253}.h254{--hue:254}.h255{--hue:255}.h256{--hue:256}.h257{--hue:257}.h258{--hue:258}.h259{--hue:259}.h260{--hue:260}.h261{--hue:261}.h262{--hue:262}.h263{--hue:263}.h264{--hue:264}.h265{--hue:265}.h266{--hue:266}.h267{--hue:267}.h268{--hue:268}.h269{--hue:269}.h270{--hue:270}.h271{--hue:271}.h272{--hue:272}.h273{--hue:273}.h274{--hue:274}.h275{--hue:275}.h276{--hue:276}.h277{--hue:277}.h278{--hue:278}.h279{--hue:279}.h280{--hue:280}.h281{--hue:281}.h282{--hue:282}.h283{--hue:283}.h284{--hue:284}.h285{--hue:285}.h286{--hue:286}.h287{--hue:287}.h288{--hue:288}.h289{--hue:289}.h290{--hue:290}.h291{--hue:291}.h292{--hue:292}.h293{--hue:293}.h294{--hue:294}.h295{--hue:295}.h296{--hue:296}.h297{--hue:297}.h298{--hue:298}.h299{--hue:299}.h300{--hue:300}.h301{--hue:301}.h302{--hue:302}.h303{--hue:303}.h304{--hue:304}.h305{--hue:305}.h306{--hue:306}.h307{--hue:307}.h308{--hue:308}.h309{--hue:309}.h310{--hue:310}.h311{--hue:311}.h312{--hue:312}.h313{--hue:313}.h314{--hue:314}.h315{--hue:315}.h316{--hue:316}.h317{--hue:317}.h318{--hue:318}.h319{--hue:319}.h320{--hue:320}.h321{--hue:321}.h322{--hue:322}.h323{--hue:323}.h324{--hue:324}.h325{--hue:325}.h326{--hue:326}.h327{--hue:327}.h328{--hue:328}.h329{--hue:329}.h330{--hue:330}.h331{--hue:331}.h332{--hue:332}.h333{--hue:333}.h334{--hue:334}.h335{--hue:335}.h336{--hue:336}.h337{--hue:337}.h338{--hue:338}.h339{--hue:339}.h340{--hue:340}.h341{--hue:341}.h342{--hue:342}.h343{--hue:343}.h344{--hue:344}.h345{--hue:345}.h346{--hue:346}.h347{--hue:347}.h348{--hue:348}.h349{--hue:349}.h350{--hue:350}.h351{--hue:351}.h352{--hue:352}.h353{--hue:353}.h354{--hue:354}.h355{--hue:355}.h356{--hue:356}.h357{--hue:357}.h358{--hue:358}.h359{--hue:359}
.t0{background:oklch(0.93 0.05 var(--hue));color:oklch(0.42 0.13 var(--hue))}.t1{background:oklch(0.85 0.09 var(--hue));color:oklch(0.34 0.12 var(--hue))}.t2{background:oklch(0.74 0.11 var(--hue));color:oklch(0.24 0.10 var(--hue))}
@media (prefers-color-scheme: dark){:root:not([data-theme="light"]) .t0{background:oklch(0.30 0.06 var(--hue));color:oklch(0.88 0.08 var(--hue))}:root:not([data-theme="light"]) .t1{background:oklch(0.38 0.08 var(--hue));color:oklch(0.92 0.06 var(--hue))}:root:not([data-theme="light"]) .t2{background:oklch(0.47 0.10 var(--hue));color:oklch(0.97 0.03 var(--hue))}}
:root[data-theme="dark"] .t0{background:oklch(0.30 0.06 var(--hue));color:oklch(0.88 0.08 var(--hue))}:root[data-theme="dark"] .t1{background:oklch(0.38 0.08 var(--hue));color:oklch(0.92 0.06 var(--hue))}:root[data-theme="dark"] .t2{background:oklch(0.47 0.10 var(--hue));color:oklch(0.97 0.03 var(--hue))}
/* FACES MADE END */
/* THE WATCH TOOLBAR (Simon 2026-09-28 night, like the Devices page): the range and its dates, the account or company combo and the own check switch sit in the one toolbar. */
.toolbar .dates{display:inline-flex;gap:8px}.toolbar .setting.small{gap:8px;padding:0;border:0;background:none;min-height:44px;align-items:center}
/* THE PERSON'S SHEET (Simon 2026-09-29, "it feels like a draft and incomplete"; docs/plan.md has the practice and the sources). Three facts under the name stand as ROWS, ONE LINE EACH, the label at the left and the value at the right in line with the Details under them (his words the same day: three columns crammed the words, then two lines per fact were big for nothing; the practice for a narrow panel is one column of pairs). A value never wears a title's look: 14 px at 600 inside its row, where a part's title is 15 px at 700. A fact of the login is a row with its own action at its right; the week is rows; the numbers of Activity are a strip of figures; the lists under it stand under small labels. */
.summary{display:grid;grid-template-columns:161px minmax(0,1fr);margin:14px 0 6px;border:1px solid var(--line);border-radius:12px;background:var(--ground);overflow:hidden}.summary dt,.summary dd{margin:0;padding:9px 14px;border-top:1px solid var(--line)}.summary dt{padding-top:10px;padding-right:0;font-size:13px;color:var(--muted)}.summary dd{font-size:14px;font-weight:600;overflow-wrap:anywhere}.summary dd .seen{font-weight:400}.summary .named{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.summary dt:first-of-type,.summary dt:first-of-type+dd{border-top:0}
.figures{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));margin:0 0 4px;border:1px solid var(--line);border-radius:12px;background:var(--ground);overflow:hidden}.figures>div{display:grid;gap:2px;align-content:start;min-width:0;padding:11px 14px;border-left:1px solid var(--line)}.figures>div:first-child{border-left:0}.figures dt{font-size:12px;color:var(--muted)}.figures dd{margin:0;font-size:14px;font-weight:600}
.fact-row{display:flex;align-items:center;justify-content:space-between;gap:16px}.fact-row .words{display:grid;gap:2px;min-width:0}.fact-row .words b{display:flex;align-items:center;flex-wrap:wrap;gap:4px 8px;font-weight:600}.fact-row .words .seen{font-size:13px}.fact-row form{flex:none;margin:0}.fact-row .mini,.fact-row .status{margin:0}.part-head form{margin:0}
.hours{display:grid;grid-template-columns:minmax(96px,132px) minmax(0,1fr);margin:0 0 12px;border:1px solid var(--line);border-radius:12px;background:var(--ground);overflow:hidden}.hours dt,.hours dd{margin:0;padding:10px 14px;border-top:1px solid var(--line)}.hours dt{color:var(--muted);font-size:13px;padding-top:12px}.hours dd{font-size:14px;font-weight:600}.hours dd.seen{font-weight:400}.hours dt:first-of-type,.hours dt:first-of-type+dd{border-top:0}
/* On a phone the days take what they need and no more, so a window of hours stands on one line (2026-09-29: `9:00 AM to 5:00 PM` broke before its PM at 320). */
@media (max-width:480px){.hours{grid-template-columns:96px minmax(0,1fr)}.hours dt{padding-right:6px}.hours dd{padding-left:6px}}
.sub-label{margin:18px 0 8px;font-size:12px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
/* WHAT IS CUSTOM ABOUT A PERSON SHOWS IN THEIR ROW (his word the same day: custom hours, custom access "and all such things"), under the name, in the tint a machine's Custom settings mark wears. */
.person-cell .words>.marks{overflow:visible;white-space:normal;margin-top:2px}
/* A STATE THAT IS FINE IS A SMALL MARK (his word the same day: the word Confirmed was long and took a line of its own for nothing): a tick in the state's tint beside the value, its name for a screen reader and on hover. Words are kept for what needs somebody. */
.tick{display:inline-grid;place-items:center;flex:none;width:18px;height:18px;border-radius:50%;background:var(--online-soft);color:var(--online)}.ticked{min-width:0;overflow-wrap:anywhere}.ticked .tick{margin-left:6px;vertical-align:-4px}
/* A LIST OF SHORT LINES IS TIGHT (his word the same day, the logins were too spread out): one line per login, the time in its own column, hairlines between, the day as a quiet head inside the one bordered list. */
.sheet-part .list.tight{gap:0;border:1px solid var(--line);border-radius:12px;background:var(--ground);overflow:hidden}.sheet-part .list.tight .day{display:block;min-height:0;padding:7px 14px 5px;border-top:1px solid var(--line);background:var(--surface);font-size:11px}.sheet-part .list.tight .day:first-child{border-top:0}.sheet-part .list.tight .item{flex-wrap:nowrap;align-items:baseline;gap:12px;padding:7px 14px;border:0;border-top:1px solid var(--line);border-radius:0;background:none}.sheet-part .list.tight .item:focus-visible{outline-offset:-2px}.sheet-part .list.tight .item b{flex:0 0 70px;font-size:13px;font-weight:600;font-variant-numeric:tabular-nums}.sheet-part .list.tight .item .seen{flex:1 1 auto;min-width:0;font-size:13px}
/* The foot of a tight list is one line: how many are shown at the left, a small Load more at the right (the picture he approved had it so). */
.sheet-part .list.tight+.foot.more{flex-direction:row;justify-content:space-between;align-items:center;gap:12px;padding:8px 2px 0;text-align:left}.sheet-part .list.tight+.foot.more .acts{width:auto}.sheet-part .list.tight+.foot.more .btn{min-width:0;width:auto;height:34px;padding:0 12px;border-radius:9px;font-size:13px;font-weight:500}
/* The sessions under the logins stand in the same tight list (the sibling, swept the same day): a session keeps its two lines, its dot and its chevron, and its story opens under its row inside the list. */
.sheet-part .list.tight.wide .item{flex-wrap:wrap;align-items:center;gap:2px 10px;padding:9px 14px}.sheet-part .list.tight.wide .item .title{flex:1 1 60%;min-width:0;font-size:14px}.sheet-part .list.tight.wide .item .title b{flex:none;font-size:14px;font-variant-numeric:normal}.sheet-part .list.tight.wide .item .seen{flex:0 0 100%;order:3}.sheet-part .list.tight .item.opened{border-radius:0}.sheet-part .list.tight .drawer{margin:0;border:0;border-top:1px solid var(--line);border-radius:0}
/* THE ACCESS ROWS, in every place they stand (the device sheet, the book panel, Settings, the person's sheet): ONE bordered list with dividers, never a box per row (the polish checklist), and the sentence that says who connects to what gets the row's whole width under the name, instead of wrapping on six lines beside three controls. The markup is as it was: the words become children of the row's grid. A divider stands only under a row that shows, so a hidden first row leaves no double line. */
.people{gap:0;border:1px solid var(--line);border-radius:12px;background:var(--ground);overflow:hidden}.people>p{margin:0;padding:12px 14px}.person{grid-template-columns:32px minmax(0,1fr) auto;gap:4px 12px;padding:12px 14px;border:0;border-radius:0;background:none}.person:not([hidden])~.person{border-top:1px solid var(--line)}.person>.words{display:contents}.person>.who{grid-column:1;grid-row:1}.person>.words>b{grid-column:2;grid-row:1;min-width:0;overflow-wrap:anywhere}.person>.acts{grid-column:3;grid-row:1}.person>.words>.seen{grid-column:2/-1;grid-row:2}.person>.rules{grid-row:3}
@media (max-width:760px){.person>.acts{grid-column:1/-1;grid-row:3}.person>.rules{grid-row:4}.summary{grid-template-columns:104px minmax(0,1fr)}.figures{grid-template-columns:repeat(2,minmax(0,1fr))}.figures>div:nth-child(3){border-left:0}.figures>div:nth-child(n+3){border-top:1px solid var(--line)}.fact-row{align-items:flex-start}}
/* A LINE AFTER AN ACTION OVER AN OPEN SHEET (app.js liftToasts): shown by hand as a popover it stands in the layer dialogs are drawn in, so the browser's own popover box (centred, its own edges) is put back where our line stands. */
.toast[popover]{inset:auto 24px 24px auto;margin:0}
@media (max-width:760px){.toast[popover]{inset:auto 16px calc(16px + env(safe-area-inset-bottom,0px)) 16px}}
/* A BUTTON IN A PART'S HEAD UNDER A FINGER (found 2026-09-29 while measuring the person's sheet): Edit, Change and the like stay 30 px tall in a sheet's part heads, which a finger misses; they keep their look and get the room to tap around them, the way the small icons do. */
@media (pointer:coarse){.part-head .btn.small{position:relative}.part-head .btn.small::after{content:"";position:absolute;inset:-7px 0}.sheet-part .list.tight+.foot.more .btn{height:44px;padding:0 16px;font-size:14px}}
/* THE TOOLBAR'S BOXES, HIS FOUR ASKS OF 2026-09-30 (docs/plan.md has the practice). ONE SIZE for every filter box of a toolbar on every page, the select and the combo alike. ON A TOUCHED SCREEN a combo is the platform's own picker (a select), which never fights the keyboard and never opens behind anything; the box you type into is for a mouse. ON A PHONE the boxes and the states pill wait behind a Filters button that carries the count of what is applied (the disclosure the small screen practice folds a toolbar into); the search stays out, it is the first thing. */
.toolbar .select,.toolbar .combo{flex:0 1 240px;min-width:170px}.toolbar .select select{width:100%;max-width:none}
.combo .native{display:none;appearance:none;-webkit-appearance:none;padding:0 38px 0 14px;border-radius:12px;cursor:pointer;width:100%}
@media (pointer:coarse){.combo [data-combo-input],.combo ul{display:none!important}.combo .native{display:block}}
.fold{display:none;position:relative;gap:8px}.fold .count{display:none;min-width:22px;height:22px;padding:0 7px;border-radius:999px;background:var(--accent);color:var(--on-accent);font-style:normal;font-size:12px;font-weight:600;line-height:22px;text-align:center;font-variant-numeric:tabular-nums}.fold .count:not(:empty){display:inline-block}.fold[aria-expanded="true"]{background:var(--accent-soft);border-color:transparent;color:var(--accent)}
/* The Notifications kind pills fold the same way, beside the rail already (2026-09-30: sixteen pills wrapped into six rows at 768 px and eight on a phone). */

/* THE FILTERS ON A PHONE (Simon 2026-10-08, "the Filters button on a row of its own eats a row; just an icon after the search bar on the same row, and size the boxes properly when it opens"; the practice: a filter button beside the search with the applied count on it, the applied filters cleared from inside): the search and the icon button share the first row, the count sits on the icon's corner, the word stays for a screen reader; open, every box takes the whole row (a select, the book box, the two dates side by side, the switch), the states pills keep their scrolling row, and Clear filters stands last. */
@media (max-width:760px){.toolbar{gap:10px 12px}.toolbar .search{order:-1;flex:1 1 0;min-width:0}.toolbar .fold{display:inline-flex;order:0;flex:none;width:44px;height:44px;padding:0;justify-content:center}.fold .named{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.fold .count{position:absolute;top:-6px;right:-6px;min-width:18px;height:18px;line-height:18px;padding:0 5px;font-size:11px}.toolbar :is(.select,.dates,.combo,.setting.small){order:2;flex:1 1 100%;min-width:0}.toolbar .select select{width:100%;max-width:none}.toolbar .dates{display:flex;gap:8px}.toolbar .dates input{flex:1 1 0;min-width:0}.toolbar .seg.filter{order:3;flex:1 1 100%}.toolbar .clear{order:4;flex:1 1 100%;margin-left:0}.toolbar:not(.open) :is(.select,.dates,.combo,.seg.filter,.setting.small,.clear){display:none}}
/* A ROW THAT SCROLLS SIDEWAYS SAYS SO (Simon 2026-10-08, "let it show that it scrolls, faded at the edge"; the practice: a fade only on the side that holds more, never a static one, the overlay passing touches): lists.js marks `data-more` with the sides that overflow, and a mask fades those edges; the tab rows and the states pills. The row never scrolls up and down (a tab's underline overran its box by a pixel, and the row danced under a finger) and its sideways overscroll never reaches the page. The active tab is brought into view on arrival. */
.tab-row{-webkit-mask-image:none;mask-image:none}.tab-row[data-more="right"]{-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 40px),transparent);mask-image:linear-gradient(to right,#000 calc(100% - 40px),transparent)}.tab-row[data-more="left"]{-webkit-mask-image:linear-gradient(to right,transparent,#000 40px);mask-image:linear-gradient(to right,transparent,#000 40px)}.tab-row[data-more="left right"]{-webkit-mask-image:linear-gradient(to right,transparent,#000 40px,#000 calc(100% - 40px),transparent);mask-image:linear-gradient(to right,transparent,#000 40px,#000 calc(100% - 40px),transparent)}
/* The states pills fade INSIDE their oval (Simon 2026-10-08, "the fade should start on the edges of the oval, not outside, and its edges shouldn't fade"; the practice, shadcn's scroll fade: the border on a wrapper, the fade on the inner scroller): the oval keeps the border and the padding, the scroller inside it runs through the padding to the border (negative margin, its own padding) and wears the mask, so the pills fade up to the border and the border never fades. */
.seg-scroll[data-more="right"]{-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 40px),transparent);mask-image:linear-gradient(to right,#000 calc(100% - 40px),transparent)}.seg-scroll[data-more="left"]{-webkit-mask-image:linear-gradient(to right,transparent,#000 40px);mask-image:linear-gradient(to right,transparent,#000 40px)}.seg-scroll[data-more="left right"]{-webkit-mask-image:linear-gradient(to right,transparent,#000 40px,#000 calc(100% - 40px),transparent);mask-image:linear-gradient(to right,transparent,#000 40px,#000 calc(100% - 40px),transparent)}
/* The owner's platform line fades too, narrowly, at the screen's edges alone (Simon 2026-10-09: "zaghro 3al edges bass heik bit polishing"): 20 px, the 16 px gutter and a hair of the words. */
.platform-line[data-more="right"]{-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 20px),transparent);mask-image:linear-gradient(to right,#000 calc(100% - 20px),transparent)}.platform-line[data-more="left"]{-webkit-mask-image:linear-gradient(to right,transparent,#000 20px);mask-image:linear-gradient(to right,transparent,#000 20px)}.platform-line[data-more="left right"]{-webkit-mask-image:linear-gradient(to right,transparent,#000 20px,#000 calc(100% - 20px),transparent);mask-image:linear-gradient(to right,transparent,#000 20px,#000 calc(100% - 20px),transparent)}
/* THE PLUS ON A PHONE (Simon 2026-10-08, "New book takes a row of its own, just a plus before Unassigned, and the same on the other pages"; the practice: one primary create action per list page, as an icon in the header when the header is simple): New book is a 36 px plus at the end of the tab row, sharing it with the tabs (his word: on the right, smaller), and New user, New customer and New package are a plus beside the title; the words stay for a screen reader and come back above 760 px. */
@media (max-width:760px){.tabs .new-book{order:1;margin:0 0 0 10px;width:36px;height:36px;padding:0;border-radius:10px;justify-content:center;align-self:center}.tabs .new-book .named,.head .btn.primary .named{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.tabs .tab-row{flex:1 1 0;margin-inline:-16px 0;padding-inline:16px 0}.head:has(>.btn.primary),.head:has(>.acts.one){flex-wrap:nowrap;align-items:flex-start}.head:has(>.btn.primary)>div:first-child,.head:has(>.acts.one)>div:first-child{flex:1 1 0;min-width:0}.head .acts.one{flex:none}.head>.btn.primary,.head .acts.one>.btn.primary{position:relative;flex:none;width:44px;height:44px;padding:0;justify-content:center}.head .btn.primary>svg{display:inline}}@media (min-width:761px){.head .btn.primary>svg{display:none}}
.btn.icon{width:44px;padding:0;flex:none}.head>.btn.icon{margin-left:auto;align-self:flex-start}.head:has(>.btn.icon)>div:first-child{flex:1 1 200px;min-width:0}
/* THE TABLES BESIDE THE RAIL (Simon 2026-09-30, the todo of 2026-09-21): between 761 and about 1020 px the rail stands and takes 248 px, and the Devices and Users columns no longer fit in what is left (measured at 768: headings over each other, a name a word per line). Under 1020 px their lines are the cards the phone has, and the Watch's lines stack as they do on a phone; the Logs tables do the same in their own block. */
@media (max-width:1020px){.table.devices,.table.users{display:none}.cards{display:grid;gap:10px}
.mcard{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:14px;box-shadow:var(--shadow);display:grid;grid-template-columns:auto 1fr auto;gap:12px;align-items:center}.mcard .name b{font-size:15px}.mcard .meta{grid-column:2/4;display:flex;gap:8px;flex-wrap:wrap;align-items:center;font-size:12px;color:var(--muted)}.mcard .meta .id{font-size:12px}
.mcard{grid-template-columns:auto minmax(0,1fr) auto;cursor:pointer}.mcard .name{grid-column:2}.mcard>form{grid-column:3;grid-row:1}.mcard .state{grid-column:2;justify-self:start}.mcard.picked{background:color-mix(in srgb,var(--accent-soft) 60%,var(--surface))}.mcard .state{font-size:12px}.mcard .meta{grid-column:2;align-items:center}
.row.watch{grid-template-columns:1fr;gap:6px;position:relative;padding-right:52px}.row.watch:first-child{display:none}.row.watch .kind .mini{display:inline-flex}.row.watch .times{display:none}.row.watch .chev{position:absolute;right:10px;top:10px}.watch-story{grid-template-columns:1fr}}
/* THE DEVICE CARD (Simon 2026-10-08, docs/plan.md THE DEVICES CARD; the practice: one destination, the whole card the target, a chevron as the visible cue where there is no hover, a hover tone where there is one): the state's dot before the name, hostname and OS under it, the switch and the chevron at the right, the state's words on their line with only the pills that matter now, the books and the tags as chips, then the ID, the address, who can reach and the flags; a switched off machine dims and wears a hollow dot; two columns on a tablet. */
.mcard.devices{grid-template-columns:auto minmax(0,1fr) auto auto;gap:10px 12px;align-items:center;cursor:pointer}.mcard.devices .name{grid-column:2;display:grid;gap:2px;min-width:0}.mcard.devices .name .title{display:flex;align-items:center;gap:8px;min-width:0}.mcard.devices .name .title a{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mcard.devices .name .title .dot{flex:none}.mcard.devices .sub{font-size:12px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mcard.devices>form{grid-column:3;grid-row:1}.mcard.devices .chev{grid-column:4;grid-row:1;color:var(--muted);display:grid;place-items:center;margin-right:-4px}.mcard.devices .state{grid-column:2/5;justify-self:start;font-size:13px;color:var(--text);gap:0;flex-wrap:wrap}.mcard.devices .state .seen{color:var(--muted)}.mcard.devices .state .status{margin-left:8px}.mcard.devices .chips-line{grid-column:2/5}.mcard.devices .meta{grid-column:2/5;align-items:center}.mcard.devices .row-note{grid-column:2/5}.mcard.devices.is-off .name,.mcard.devices.is-off .state,.mcard.devices.is-off .chips-line,.mcard.devices.is-off .meta,.mcard.devices.is-off .row-note{opacity:.5}.row.devices.is-off .name{opacity:.55}/* The table's Status cell: who is in use stands under the state's words, in line with them past the dot. */
.row.devices .state{flex-wrap:wrap}.row.devices .state>.seen{flex-basis:100%;padding-left:18px;font-size:12px}.status.live{background:var(--accent-soft);color:var(--accent)}.status.warn{background:var(--warn-soft);color:var(--warn)}@media (hover:hover){.mcard.devices:hover{background:var(--ground)}}@media (min-width:880px) and (max-width:1020px){.cards.devices{grid-template-columns:repeat(2,minmax(0,1fr))}}
/* THE LOGS PAGE LIKE DEVICES (Simon 2026-09-30, docs/plan.md has the practice): tabs with counts, the one toolbar, ONE LINE PER ROW (the sheet's tight lists, the log explorers' rows), every fact in a column of its own, the outcome a dot and its word tinted by the state, a sticky header; under 1300 px the columns that fit least step aside, under 1020 px (an upright tablet beside the rail, where the tables broke, the todo of 2026-09-21) the lines are cards, as on Devices and Users on a phone. The owner's log shares every rule. */
.table.logs .row{min-height:52px;padding:10px 18px}.table.logs .row.day{padding:8px 18px}
.table.logs .row>span{min-width:0}.table.logs .one{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.table.logs .one b{font-weight:600}.table.logs .one a{color:inherit}.table.logs .one .mini{margin-left:6px}.session-who{display:flex;flex-wrap:wrap;align-items:center;gap:4px 6px;min-width:0}.session-who .one{flex:0 0 auto;max-width:100%}.table.logs .session-who .mini{margin-left:0}.table.logs .one bdi{unicode-bidi:isolate}
.table.logs .cell{font-size:14px;color:var(--text)}.table.logs .cell b{font-weight:600}.table.logs .num{font-variant-numeric:tabular-nums;white-space:nowrap}.table.logs time{white-space:nowrap;font-variant-numeric:tabular-nums}.table.logs .what,.table.logs .change{min-width:0;overflow-wrap:anywhere}.table.logs .from{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.outcome{display:flex;flex-wrap:wrap;align-items:center;gap:2px 8px;min-width:0}.outcome .seen{min-width:0}.outcome .state{white-space:nowrap}
.state.live{color:var(--accent);font-weight:600}.state.ok{color:var(--online);font-weight:600}.state.warn{color:var(--warn);font-weight:600}.state.bad{color:var(--danger);font-weight:600}
.row.sessions{grid-template-columns:minmax(0,1.2fr) minmax(0,1fr) 190px 82px 124px minmax(150px,1.3fr) 90px 36px}
.row.transfers{grid-template-columns:176px 112px minmax(0,1.5fr) minmax(0,1fr) minmax(0,.9fr) minmax(0,.8fr) 150px 82px}
.row.alarms{grid-template-columns:150px minmax(0,1.8fr) minmax(0,.9fr) 210px 82px}
.row.logins{grid-template-columns:minmax(0,1.4fr) minmax(0,1.3fr) 140px 220px 82px}
.row.changes{grid-template-columns:minmax(0,2.2fr) minmax(0,.8fr) 170px 82px}
.row.mail{grid-template-columns:minmax(0,1.6fr) minmax(0,1.2fr) 100px 82px}
@media (max-width:1300px){.row.sessions{grid-template-columns:minmax(0,1.2fr) minmax(0,1fr) 82px 124px minmax(150px,1.3fr) 36px}.row.sessions .from,.row.sessions .bytes{display:none}.row.transfers{grid-template-columns:176px 112px minmax(0,1.6fr) minmax(0,.9fr) minmax(0,.8fr) 82px}.row.transfers .folder,.row.transfers .from{display:none}.row.alarms{grid-template-columns:150px minmax(0,1.8fr) minmax(0,.9fr) 82px}.row.alarms .from{display:none}.row.logins{grid-template-columns:minmax(0,1.4fr) minmax(0,1.3fr) 140px 82px}.row.logins .from{display:none}.row.changes{grid-template-columns:minmax(0,2.2fr) minmax(0,.8fr) 82px}.row.changes .from{display:none}}
/* A line as a card: who or what in bold, the state beside it, the facts on one muted line, the chevron at the right; its story opens under the card. */
/* The pill sits on the title's line, beside it, like the Users rows (2026-10-15, his look on the phone: a transfer's card spent its second line on the pill alone); when the title is long the pill wraps under it, whole. The chevron keeps the top right corner. */
.lcard{display:flex;flex-wrap:wrap;align-items:center;gap:4px 10px;position:relative;padding:12px 14px;background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}
.lcard .title{flex:0 1 auto;min-width:0;overflow-wrap:anywhere;font-size:15px}.lcard .title b{font-weight:600}.lcard .title a{color:inherit}.lcard>.status,.lcard>.outcome{flex:0 1 auto;min-width:0}.lcard .meta{flex:1 1 100%;min-width:0;overflow-wrap:anywhere;color:var(--muted);font-size:13px;line-height:1.45}.lcard .meta a{color:inherit;text-decoration:underline;text-decoration-color:var(--line)}.lcard .chev{position:absolute;top:10px;right:10px;margin:0}.lcard:has(>.chev){padding-right:48px}.lcard.opened{border-radius:var(--radius) var(--radius) 0 0}
.cards .drawer{margin-top:-10px;border:1px solid var(--line);border-top:0;border-radius:0 0 var(--radius) var(--radius);background:var(--surface);padding:12px 14px;box-shadow:var(--shadow)}
@media (max-width:1020px){.table.logs{display:none}.cards.logs{display:grid;gap:10px}}
/* THE OVERVIEW, ATTENTION FIRST (2026-09-30, Option A): the card that says what needs a decision and the calm line when nothing does, the setup steps of a new company. A wrapper that may be empty takes no place of its own. */
[data-part="alarms"]{display:contents}
.attn-card{margin-bottom:var(--gap)}
.attn-card.calm{display:flex;align-items:center;gap:12px;padding:14px 18px;color:var(--online);font-weight:600}
.attn,.first-steps{list-style:none;margin:0;padding:0;display:grid;gap:6px}
.attn li>a,.attn li>.plain,.first-steps li{display:flex;align-items:center;gap:12px;min-height:44px;padding:8px 12px;border:1px solid var(--line);border-radius:10px;background:var(--ground);color:inherit;text-decoration:none}
.attn li>a:hover{border-color:var(--accent)}
.attn .what{flex:1;min-width:0}
.attn .arrow{flex:none;color:var(--muted)}
.first-steps li.done{color:var(--muted)}.first-steps li.done span{text-decoration:line-through}
.first-steps .todo{width:18px;height:18px;border:2px solid var(--line);border-radius:50%;flex:none}
.first-steps .did{color:var(--online);flex:none}
.first-steps a{flex:1;color:inherit;text-decoration:none}.first-steps a:hover{color:var(--accent)}
/* THE SETUP GUIDE (2026-10-08, docs/plan.md THE SETUP GUIDE): the tidy list, the count in the head, the controls quiet at the foot, buttons on the right. */
.guide .card-head .seen{font-size:13px}.guide-foot{display:flex;justify-content:flex-end;gap:8px;margin-top:12px}
/* THE TWO CARDS under it (his pick of 2026-10-01, the plan's entry `THE OVERVIEW'S TWO CARDS`): the machines by their state as one bar with its legend, the week of sessions as a chart. Side by side they are one height at every width (one row of the grid, each card stretched to it); under 1280 px of window, where the chart beside its words would shrink until its letters cannot be read, and on a phone they stand one under the other. */
.glance{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));grid-auto-rows:1fr;gap:var(--gap);margin-bottom:var(--gap)}
.glance>.card,.glance>.card+.card:not(dialog){margin:0;min-width:0}
.glance-value{margin:0;color:var(--muted);font-size:15px;line-height:34px}
.glance-value b{color:var(--text);font-size:30px;margin-inline-end:4px;font-variant-numeric:tabular-nums}
.glance-value.is-none{color:var(--text);font-size:20px;font-weight:600}
.glance-note{margin:0;color:var(--muted);font-size:13px;line-height:22px}
.glance-note a{color:inherit;text-decoration:none;white-space:nowrap}.glance-note a:hover{text-decoration:underline}
.glance-note .is-alert{color:var(--warn);font-weight:600}
.fleet-bar{display:block;width:100%;height:10px;margin-top:12px;border-radius:5px}
.fleet-bar .is-online{fill:var(--online)}.fleet-bar .is-offline{fill:var(--muted);opacity:.3}.fleet-bar .is-silent{fill:var(--warn)}
.fleet-key{display:flex;flex-wrap:wrap;align-items:center;gap:0 18px;margin:8px 0 0;color:var(--muted);font-size:13px;line-height:22px}
.fleet-key a,.fleet-key span{display:inline-flex;align-items:center;gap:7px;color:inherit;text-decoration:none}
.fleet-key a:hover{color:var(--accent)}
.fleet-key b{color:var(--text);font-variant-numeric:tabular-nums}
.fleet-key i{width:8px;height:8px;border-radius:50%;flex:none}
.fleet-key .is-online{background:var(--online)}.fleet-key .is-offline{background:var(--muted);opacity:.45}.fleet-key .is-silent{background:var(--warn)}
.week-row{display:flex;align-items:flex-end;justify-content:space-between;gap:20px}
.week-words{min-width:0;align-self:flex-start}
.week-chart{flex:0 1 256px;min-width:0;max-width:48%;height:auto}
.week-chart rect.is-day{fill:var(--accent);opacity:.28}.week-chart rect.is-today{fill:var(--accent)}.week-chart rect.is-none{fill:var(--line);opacity:1}
.week-chart text{fill:var(--muted);font:600 11px var(--font)}.week-chart text.is-today{fill:var(--text)}
@media (max-width:1279px){.glance{grid-template-columns:minmax(0,1fr);grid-auto-rows:auto}}
@media (max-width:480px){.week-row{flex-direction:column;align-items:stretch}.week-chart{flex:none;max-width:none;width:100%}}
/* LICENSE PACKAGES (2026-10-01): the owner's list of packages, a table like Customers that keeps its counts and its button beside the rail and becomes cards on a phone, each count with its word; and the limit boxes of a form, one set that a chosen package locks. */
.row.packages{grid-template-columns:minmax(0,1.6fr) 100px 100px 120px 130px 120px 110px 90px}
.row.packages .grants{display:inline}.row.packages .acts{display:flex;justify-content:flex-end}
@media (max-width:1100px){.row.packages{grid-template-columns:repeat(6,minmax(0,1fr)) 84px;row-gap:6px}.row.packages .name{grid-column:1/7}.row.packages:first-child{display:none}.row.packages .grants::after{content:" " attr(data-unit);color:var(--muted);font-size:13px}.row.packages .acts{grid-row:1/3;grid-column:7;align-self:center}}
@media (max-width:760px){.row.packages{grid-template-columns:1fr;gap:6px}.row.packages .name,.row.packages .acts{grid-row:auto;grid-column:auto}}
fieldset.limits{display:grid;gap:14px;border:0;margin:0;padding:0;min-width:0}
fieldset.limits:disabled input{background:var(--ground);color:var(--muted);cursor:default}
.rows>a{color:inherit;text-decoration:none;min-height:44px;display:flex;align-items:center}.rows>a:hover{color:var(--accent)}
/* A customer's row: the state's pill over the end, the package over its term, the devices over how many are online, each pair one under the other. */
@media (min-width:761px){.row.customers .cust-name>a{min-width:0;max-width:100%}.row.customers .cust-name>a>b{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}
/* THE OWNER'S SETTINGS (2026-10-01): six small numbers in a dialog stand two by two, equal. */
.two.even{grid-template-columns:repeat(2,minmax(0,1fr))}
/* A SUSPENDED COMPANY (2026-10-01, web/suspended.py): the line a manager reads at the head of every page, in the danger tint, the width of the page's own column. */
.license-bar{display:flex;align-items:flex-start;gap:12px;margin-bottom:var(--gap);padding:12px 16px;border:1px solid color-mix(in srgb,var(--danger) 35%,var(--line));border-radius:var(--radius);background:var(--danger-soft);font-size:14px}
.license-bar .dot{margin-top:7px;flex:none}
.license-bar>span{flex:1;min-width:0}.license-bar form{flex:none;margin-left:auto}
/* THE OWNER'S OVERVIEW (part 2, 2026-10-01): the platform line under his head, a few words each, a dot where there is a state and none on a figure; the Attention column, a count in its tone that opens the company. */
.platform-line{display:flex;flex-wrap:wrap;gap:2px 24px;margin-top:10px;color:var(--muted);font-size:13px;line-height:24px}
.platform-line>a,.platform-line>span{display:inline-flex;align-items:center;gap:8px;min-width:0;color:inherit;text-decoration:none}
.platform-line b{color:var(--text);font-weight:600}.platform-line>a:hover b{color:var(--accent)}
.platform-line .dot{width:8px;height:8px;margin-inline:3px 1px;box-shadow:0 0 0 3px var(--line)}.platform-line .dot.ok{box-shadow:0 0 0 3px var(--online-soft)}.platform-line .dot.warn{box-shadow:0 0 0 3px var(--warn-soft)}.platform-line .dot.bad{box-shadow:0 0 0 3px var(--danger-soft)}
@media (max-width:760px){.head-wide{flex:1 1 100%;min-width:0}.platform-line{flex-wrap:nowrap;overflow-x:auto;gap:0 20px;margin-inline:-16px;padding-inline:16px;scrollbar-width:none}.platform-line::-webkit-scrollbar{display:none}.platform-line>a,.platform-line>span{flex:none;white-space:nowrap}}
.attn-cell form{display:inline-flex;margin:0}.attn-cell .status{min-width:28px;justify-content:center;font-variant-numeric:tabular-nums}
.attn-cell button.status{position:relative;border:0;font:inherit;font-size:12px;font-weight:600;cursor:pointer}.attn-cell button.status:hover{box-shadow:inset 0 0 0 1px currentColor}
/* Seven columns need the wide window, five the middle one (Today and Relayed leave); under 900 px a company's row stands as a short list, each fact with its word, as on a phone. */
/* USAGE (part 3 of the Overview, 2026-10-01): the figures strip of the user sheet with larger values and a landmark under each (`.figures.big`), the twelve months as four charts of one size, and a month's table per customer under a head that picks the month. */
.figures.big{margin:0}.figures.big.five{grid-template-columns:repeat(5,minmax(0,1fr))}
.figures.big dt{display:flex;align-items:center;gap:8px;font-size:13px}.figures.big dd{font-size:22px;font-weight:700;line-height:30px;font-variant-numeric:tabular-nums}
.figures.big dd.note{color:var(--muted);font-size:13px;font-weight:400;line-height:20px}.figures.big dd.note .warn{color:var(--warn);font-weight:600}
.figures.big dt .dot{width:8px;height:8px;margin-inline:3px 1px;box-shadow:0 0 0 3px var(--line)}.figures.big dt .dot.ok{box-shadow:0 0 0 3px var(--online-soft)}.figures.big dt .dot.warn{box-shadow:0 0 0 3px var(--warn-soft)}.figures.big dt .dot.bad{box-shadow:0 0 0 3px var(--danger-soft)}
.usage-charts{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:20px 32px}
.usage-charts>div{min-width:0}.usage-charts h3{margin:0 0 12px;font-size:13px;font-weight:600;letter-spacing:0;text-transform:none;color:var(--muted)}
.usage-charts .months-chart{display:block;flex:none;width:100%;max-width:258px;height:auto}.usage-charts .glance-note{margin-top:6px}.months-chart text.is-none{opacity:.45}
.table-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;padding:14px 18px}.table-head h2{margin:0}
.month-pick{display:flex;align-items:center;gap:10px;margin-left:auto}.month-pick b{min-width:124px;text-align:center;font-weight:600}.month-pick .btn.small{padding:0;width:34px}
.row.usage{grid-template-columns:minmax(0,1.6fr) minmax(150px,1fr) 84px 124px 110px 84px 84px 84px}.row.usage .num{font-variant-numeric:tabular-nums}.row.usage .num .warn,.row.usage .relayed-cell .status{margin-left:4px}.row.usage .num .warn{color:var(--warn);font-size:13px}
.row.usage.is-head{padding:10px 18px;color:var(--muted);font-size:12px;letter-spacing:.06em;text-transform:uppercase;font-weight:600}
.row.usage.is-total{background:var(--ground);font-weight:600}
@media (max-width:1380px){.row.usage{grid-template-columns:minmax(0,1.6fr) minmax(150px,1fr) 84px 124px 110px 84px}.row.usage .hide-m{display:none}}
@media (max-width:1100px){.usage-charts{grid-template-columns:repeat(2,minmax(0,1fr))}.figures.big.five{grid-template-columns:repeat(3,minmax(0,1fr))}.figures.big.five>div:nth-child(4){border-left:0}.figures.big.five>div:nth-child(n+4){border-top:1px solid var(--line)}
.row.usage{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 16px}.row.usage .name{flex:0 0 100%}.row.usage.is-head{display:none}.row.usage .hide-m{display:block}.row.usage .num:empty{display:none}.row.usage .num::after{content:" " attr(data-unit);color:var(--muted);font-size:13px;font-weight:400}.row.usage .relayed-cell::after{content:" relayed";color:var(--muted);font-size:13px;font-weight:400}}
@media (max-width:760px){.figures.big.five{grid-template-columns:repeat(2,minmax(0,1fr))}.figures.big.five>div:nth-child(4){border-left:1px solid var(--line)}.figures.big>div:nth-child(odd){border-left:0}.figures.big>div:nth-child(n+3){border-top:1px solid var(--line)}}
@media (max-width:520px){.usage-charts{grid-template-columns:minmax(0,1fr)}.usage-charts .months-chart{max-width:none}}
/* A NOTICE'S DRAWER (2026-10-01, his word: "the text is half the row, squeezed, and under the date there is empty space"): what it is and what to do side by side across the whole row, the facts as one line under them. The watch's and the health's drawers keep their own two columns, which their many facts fill. */
.notice-story{display:grid;gap:10px;padding:4px 2px 8px}
.notice-words{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 32px}
.notice-words h3{margin:8px 0 6px;font-size:13px;font-weight:600;letter-spacing:0;text-transform:none;color:var(--text)}.notice-words p{margin:0;line-height:1.5}
.notice-facts{display:flex;flex-wrap:wrap;gap:4px 28px;margin:0;padding-top:10px;border-top:1px solid var(--line);font-size:13px}
.notice-facts>div{display:flex;gap:8px;min-width:0}.notice-facts dt{color:var(--muted)}.notice-facts dd{margin:0;font-weight:600}
@media (max-width:760px){.notice-words{grid-template-columns:minmax(0,1fr)}.list.inbox .item .read-one .btn{width:32px;height:32px;padding:0;justify-content:center}.list.inbox .item .read-one .named{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.list.inbox .item .read-one .mark-icon{display:block}.list.inbox .item>.asks{grid-column:2/-1;grid-row:3;margin:8px 0 0}.head .acts>a.btn[href="/settings#notifications"]{display:none}.head .acts:has([data-part="read-all"]){justify-content:flex-end}.head .acts:has([data-part="read-all"]) .btn{flex:0 0 auto}}
/* A ROW THAT OPENS (2026-10-01, the mail providers on Platform settings, the customers on a provider or a package): a fact row that is a link, with its arrow. */
.rows>a.fact-row{color:inherit;text-decoration:none}.rows>a.fact-row:hover{background:var(--surface)}.fact-row>.arrow{flex:none;color:var(--muted)}
/* A PILL IN A SHEET'S TITLE stands a step off the name, on its line. */
.modal-head>h2 .status{margin-left:8px;vertical-align:2px}
/* CUSTOMERS AND PACKAGES IN THE SIDE SHEET (2026-10-01): a part the address names comes into view under the sheet's head; the limit boxes' sub titles are a level under a part's own; a name in a table that opens a sheet looks like the name it is. */
dialog.modal.sheet .sheet-part{scroll-margin-top:64px}fieldset.limits h4{margin:6px 0 -4px;font-size:13px;font-weight:700}
.row.customers .cust-name>a:hover b,.row.packages .name>a:hover b{color:var(--accent)}
@media (min-width:761px){dialog.modal.sheet fieldset.limits .two{grid-template-columns:repeat(2,minmax(0,1fr))}}
/* A FACT ROW ON A NARROW SHEET: its words keep a readable width, and the button goes under them, at the right, when both no longer fit on a line. */
.fact-row{flex-wrap:wrap}.fact-row>.words,.fact-row>.person-cell{flex:1 1 170px}.fact-row>form,.fact-row>.btn,.fact-row>.acts{margin-left:auto}.fact-row>.acts form{margin:0}.fact-row bdi[dir=ltr]{white-space:nowrap}.fact-row .words{overflow-wrap:anywhere}
/* THE PHONE NUMBER'S FIELD (2026-10-01, web/static/phone.js): the country beside one box for the number. A plain select by default and on a touch screen, over the box; with a mouse the button (flag and dial code) stands in the box's row and its list opens under the field. Logical properties, so it reads the same from right to left. */
.phone{position:relative;display:grid;gap:8px}.phone .phone-pick{display:none}.phone input[type=tel]{min-width:0}
.phone .phone-native{min-width:0;text-overflow:ellipsis}
.phone-picks .phone{grid-template-columns:auto minmax(0,1fr)}.phone-picks .phone .phone-native{position:absolute;inline-size:1px;block-size:1px;opacity:0;pointer-events:none}
.phone-picks .phone .phone-pick{display:inline-flex;align-items:center;gap:6px;block-size:100%;min-block-size:44px;padding-inline:12px 10px;border:1px solid var(--line);border-radius:12px;background:var(--surface);color:var(--text);font:inherit;font-variant-numeric:tabular-nums;cursor:pointer}
.phone-picks .phone .phone-pick:hover{border-color:var(--muted)}.phone-pick .flag{margin:0}.phone-pick svg{color:var(--muted)}
.phone-list{position:fixed;top:var(--at-top,0);left:var(--at-left,0);inline-size:var(--at-width,320px);z-index:80;padding:6px;border:1px solid var(--line);border-radius:12px;background:var(--surface);box-shadow:var(--shadow)}.phone-list.up{transform:translateY(-100%)}
.phone-list input{inline-size:100%;margin-block-end:6px}.phone-list ul{max-block-size:var(--at-room,260px);overflow-y:auto;margin:0;padding:0;list-style:none;scrollbar-width:thin;scrollbar-color:var(--line) transparent}
.phone-list li{display:flex;align-items:center;gap:8px;min-block-size:40px;padding:8px 10px;border-radius:8px;cursor:pointer}.phone-list li:hover,.phone-list li.hi{background:var(--ground)}
.phone-list li[aria-selected=true]{color:var(--accent);font-weight:600}.phone-list li span{flex:1;min-inline-size:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.phone-list li i{font-style:normal;color:var(--muted);font-size:13px;font-variant-numeric:tabular-nums}.phone-list li .flag{flex:none;margin:0}
.login .signup-line{margin:14px 0 0;text-align:center;color:var(--muted);font-size:13px}
/* A COMPANY'S LOGO (accounts/logos.py). It stands on a plate of its own, white or dark as the logo needs, whatever the
   theme: a logo made for a white page must not vanish in the dark one. The plate keeps its height, so nothing moves
   when the picture arrives. */
:root{--plate:#FFFFFF;--plate-dark:#15171C}
.logo-plate{display:flex;align-items:center;min-block-size:58px;min-inline-size:0;padding:8px 10px;border:1px solid var(--line);border-radius:10px;background:var(--plate)}
.logo-plate.dark{background:var(--plate-dark)}
.logo-plate img,.logo-plate canvas{display:block;inline-size:auto;block-size:auto;max-inline-size:100%;max-block-size:40px}
.logo-plate.logo-shown{inline-size:100%;max-inline-size:240px}
/* Two layouts, by the logo's shape (accounts/logos.py SQUARE_UNTIL): a wide logo stands above the name on a wide plate,
   a square or tall mark beside it on a small square one. */
.logo-plate.square{flex:none;justify-content:center;inline-size:44px;block-size:44px;min-block-size:0;padding:4px}
.logo-plate.square img,.logo-plate.square canvas{max-block-size:100%}
.logo-plate.logo-shown.square{inline-size:72px;block-size:72px;max-inline-size:none;padding:8px}
.switcher.with-logo{flex-direction:column;align-items:stretch}
.switcher.with-logo.square-logo{flex-direction:row;align-items:center;justify-content:flex-start;gap:10px}
.switcher.two-rows>.logo-plate{margin:10px 12px 0}
.switcher-pick>.logo-plate+span{flex:1;min-inline-size:0}
/* The upload: the plain file box is what a page without its script shows; with the script (logo.js marks the form) the
   box steps back and the drop area with its button stands in its place. */
.sr-only{position:absolute;inline-size:1px;block-size:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.file-drop{display:grid;justify-items:start;gap:8px}
.file-drop .drop-mark,.file-drop .drop-words{display:none}
.file-drop input[type=file]{max-inline-size:100%;font:inherit;font-size:16px;color:var(--text)}
.logo-ready .file-drop{justify-items:center;gap:6px;padding:28px 16px;border:1.5px dashed var(--line);border-radius:14px;background:var(--ground);text-align:center;cursor:pointer;transition:border-color .15s,background .15s}
.logo-ready .file-drop:hover{border-color:var(--muted)}
.logo-ready .file-drop .drop-mark{display:grid;place-items:center;inline-size:44px;block-size:44px;margin-block-end:6px;border-radius:50%;background:var(--accent-soft);color:var(--accent)}
.logo-ready .file-drop .drop-words{display:block;font-size:15px;color:var(--text)}
.logo-ready .file-drop .quiet-link{font-size:inherit}
.logo-ready .file-drop .quiet-link:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:4px}
.logo-ready .file-drop input[type=file]{position:absolute;inline-size:1px;block-size:1px;opacity:0;pointer-events:none}
.logo-ready .file-drop.over{border-color:var(--accent);background:var(--accent-soft)}
.logo-ready .file-drop:has(input[aria-invalid=true]){border-color:var(--danger)}
.logo-editor{display:grid;gap:12px}
.logo-editor[hidden]{display:none}
.logo-stage{display:block;inline-size:100%;block-size:240px;border:1px solid var(--line);border-radius:12px;background:var(--ground);touch-action:none;cursor:default}
.logo-stage:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.logo-stage.c-move{cursor:move}.logo-stage.c-ew{cursor:ew-resize}.logo-stage.c-ns{cursor:ns-resize}.logo-stage.c-nwse{cursor:nwse-resize}.logo-stage.c-nesw{cursor:nesw-resize}
/* Under the picture, one row: the result on its plate with its size, then the tools as icons, on the right. */
.logo-bar{display:flex;flex-wrap:wrap;align-items:center;gap:10px 12px}
.logo-bar .logo-plate{flex:none;inline-size:132px;min-block-size:52px;padding:6px 8px}
.logo-bar .logo-plate.square{inline-size:52px;block-size:52px;padding:6px}
.logo-tools{display:flex;gap:8px;margin-inline-start:auto}
.logo-tools .icon-btn{color:var(--text);cursor:pointer}
.logo-tools .icon-btn:hover{border-color:var(--muted)}
/* The login page's foot (2026-10-03, his ask after the public pages): the way to the pricing and the policies for whoever lands here first. Outside the card, so the card's entrance and the loading screen stay as they are; the two rows cluster in the middle of the page. */
body.login{align-content:center}.login .login-links{display:flex;flex-wrap:wrap;justify-content:center;gap:4px 18px;margin:18px 0 0;font-size:13px}.login .login-links a{color:var(--muted);text-decoration:none;padding:4px 2px}.login .login-links a:hover{color:var(--text);text-decoration:underline}

/* THE MASTER LOCK (docs/master-lock.md), the company's breaker at the top of the manager's Overview: the house's highlighted card (the access section's border and ring) tinted by the state, a 2px border so it stands as a breaker, red locked and green open; the switch in the head on the right; the facts across the width in the sheets' figures strip; the pill before the license and the bar at the top of every page while locked, the bar with nothing to press. */
.lock-card.lock-halo.locked{border:2px solid var(--danger);box-shadow:0 0 0 3px var(--danger-soft),var(--shadow)}
.lock-card.lock-halo.open{border:2px solid var(--online);box-shadow:0 0 0 3px var(--online-soft),var(--shadow)}
.lock-head{margin-bottom:10px;flex-wrap:nowrap}
.lock-head form{margin:0}
.lock-title{display:flex;align-items:center;flex-wrap:wrap;gap:6px 10px;min-width:0}
.lock-title h2{margin:0}
.breaker{flex:none;width:56px;height:32px;border-radius:999px;border:0;padding:0;background:var(--online);position:relative;cursor:pointer;transition:background .2s;-webkit-tap-highlight-color:transparent;user-select:none}
.breaker:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
@media (hover:hover){.breaker:hover{filter:brightness(.92)}}
.breaker-knob{position:absolute;top:3px;left:3px;width:26px;height:26px;border-radius:50%;background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.3);transition:transform .2s}
.breaker.on{background:var(--danger)}
.breaker.on .breaker-knob{transform:translateX(24px)}
.lock-state{margin:0 0 12px;font-size:14px}
.lock-figures{margin:0}
.lock-figures.one{grid-template-columns:minmax(0,1fr)}
.lock-figures.two{grid-template-columns:repeat(2,minmax(0,1fr))}
/* The schedule dialog's two clocks stay side by side on the phone, like the hours dialog's: a dialog that scrolls on a phone is a dialog that drags (2026-10-15). */
.two.lock-times{grid-template-columns:repeat(2,minmax(0,1fr))}.two.lock-times .field{min-width:0}
/* A time box on iOS keeps an intrinsic width and pushed the second clock past the dialog's edge (2026-10-15, his phone): it shrinks like any box. */
input[type=time]{-webkit-appearance:none;appearance:none;display:block;min-width:0;max-width:100%;text-align:center;line-height:42px}
/* iOS draws the value as a block at the top of the box once the native look is off (his screenshot, 2026-10-15: the time pushed up, not centred): the value fills the box's height and sits in the middle, left to right and top to bottom. */
input[type=time]::-webkit-date-and-time-value{text-align:center;height:42px;line-height:42px}input[type=time]::-webkit-datetime-edit{white-space:nowrap}
/* Under a finger the whole box opens the picker, so the browser's clock icon only takes room: on his phone it pushed the time to a second line and off centre (2026-10-15). A mouse keeps it. */
@media (pointer:coarse){input[type=time]::-webkit-calendar-picker-indicator{display:none}}
.lock-pill{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 12px;border-radius:999px;background:var(--danger-soft);color:var(--danger);font-weight:600;font-size:13px;white-space:nowrap;text-decoration:none;user-select:none}
.lock-pill .dot{margin:0}
@media (hover:hover){a.lock-pill:hover{filter:brightness(.95)}}
.license-bar.lock-bar{align-items:center}
.lock-schedule{margin:14px 0 0;padding:12px 14px;border:1px solid var(--line);border-radius:12px;background:var(--ground)}
.lock-card{margin-bottom:var(--gap)}
.lock-card .lock-state:last-child{margin-bottom:0}
.breaker::before{content:"";position:absolute;inset:-6px -2px}

