/* Shared styling for the suite's dense admin pages — the Admin Console and the User Directory. Both are mostly tables and toolbars, which is a different job from the wizard pages, so they carry this sheet instead of theme-light.css's form-heavy one. The palette, the square corners and the type are still Carbon's, so the pages read as one product with the rest of the suite. Two scales do all the spacing and all the control sizing; nothing that uses this sheet should invent its own. Page-specific rules (per-ID scroll boxes, column exceptions) stay in the page that owns them. ══ TOKENS ══════════════════════════════════════════════════════════════════ Names only. Every value comes from theme-light.css, which every page loads first — this sheet declares nothing of its own (T3.2 / S5 / C3). The names stay because admin.js and users.js read 21 of them from JavaScript, where a rename fails silently: no build error, no console warning, just an unstyled element. See docs/reference/tokens.md section 9. */ :root{ --bg:var(--cds-background); --surface:var(--cds-layer); --border:var(--cds-border-subtle); --border-strong:var(--cds-border-strong); --text:var(--cds-text-primary); --muted:var(--cds-text-secondary); --dim:var(--cds-ui-04); --accent:var(--cds-interactive-01); --accent-hover:var(--cds-hover-primary); --accent-soft:var(--cds-highlight); --green:var(--cds-support-success); --green-bg:var(--wp-status-success-bg); --red:var(--cds-support-error); --red-bg:var(--wp-status-error-bg); --amber:var(--wp-status-warning-text); --amber-bg:var(--wp-status-warning-bg); --head-bg:var(--cds-layer-accent); /* --zebra is six points lighter than --head-bg, not equal to it: collapsing the two erases the striping on a nine-column table, which is the one place striping is load-bearing. --row-hover is deliberately NOT --accent-soft; see the note above table.grid tbody tr:hover. Both are one-offs and both are named in docs/reference/tokens.md section 8-A/8-B. */ --zebra:var(--wp-table-zebra); --row-hover:var(--wp-table-row-hover); /* The narrow mono stack, kept as-is: widening it changes the rendered face on any machine without IBM Plex Mono installed, and this sheet's two pages are the ones a font change would show up on. T3.5 picks one stack (8-H). */ --mono:var(--wp-font-mono-2); --s1:var(--wp-s1); --s2:var(--wp-s2); --s3:var(--wp-s3); --s4:var(--wp-s4); --s5:var(--wp-s5); --s6:var(--wp-s6); --ctl:var(--wp-ctl); /* every button / input / select that sits in a form row */ --ctl-sm:var(--wp-ctl-sm); } /* every control that sits inside a table cell */ *{ box-sizing:border-box; } body{ margin:0; font-family:var(--wp-font-sans-2); background:var(--bg); color:var(--text); } /* ══ PAGE ══════════════════════════════════════════════════════════════════════ 1240px, not 860: the user table is nine columns wide and at 860 it spilled straight out of its own white card. Wide enough for that table, still a readable measure for the prose, which is capped separately. */ .wrap{ max-width:1240px; margin:0 auto; padding:var(--s6) var(--s5) 80px; } h1{ font-size:20px; line-height:1.2; margin:0 0 2px; } .sub{ color:var(--muted); font-size:13px; line-height:1.5; margin:0 0 var(--s3); max-width:96ch; } a.home{ color:var(--accent); font-size:13px; text-decoration:none; white-space:nowrap; } a.home:hover{ text-decoration:underline; } /* ══ CARDS ═════════════════════════════════════════════════════════════════════ */ .card{ background:var(--surface); border:1px solid var(--border); border-radius:0; padding:var(--s4) var(--s5) var(--s5); margin-bottom:var(--s4); } /* One card header everywhere: small uppercase accent label on a hairline. The scripts also emit h2 for sub-sections inside a card with an inline margin-top — the same treatment reads correctly as a divider there, so both get it. */ .card h2{ font-size:12px; font-weight:600; letter-spacing:.08em; text-transform:uppercase; color:var(--accent); margin:0 0 var(--s3); padding-bottom:var(--s2); border-bottom:1px solid var(--border); } .wrap code{ font-family:var(--mono); font-size:.92em; background:var(--bg); padding:1px 4px; } /* ══ CONTROLS ══════════════════════════════════════════════════════════════════ Every button, input and select in a form row is exactly --ctl tall, so a toolbar is one clean band instead of a ragged one. */ button{ font:inherit; font-size:13px; font-weight:600; line-height:1; white-space:nowrap; height:var(--ctl); padding:0 var(--s3); border-radius:0; cursor:pointer; border:1px solid var(--wp-btn-secondary-border); background:var(--wp-btn-secondary-bg); color:var(--wp-btn-secondary-fg); } button:hover{ border-color:var(--wp-btn-secondary-hover-fg); color:var(--wp-btn-secondary-hover-fg); } button:focus-visible{ outline:2px solid var(--accent); outline-offset:-3px; } button:disabled, button:disabled:hover{ color:var(--dim); border-color:var(--border); background:var(--surface); cursor:default; } button.primary{ background:var(--wp-btn-primary-bg); border-color:var(--wp-btn-primary-bg); color:var(--wp-btn-primary-fg); } button.primary:hover{ background:var(--wp-btn-primary-hover); border-color:var(--wp-btn-primary-hover); color:var(--wp-btn-primary-fg); } button.danger{ border-color:var(--wp-btn-danger-border); color:var(--wp-btn-danger-fg); } button.danger:hover{ background:var(--wp-btn-danger-soft-bg); border-color:var(--wp-btn-danger-border); color:var(--wp-btn-danger-fg); } .row{ display:flex; gap:var(--s2); flex-wrap:wrap; align-items:center; } /* The filter / search / button strip at the top of a card. */ .toolbar{ display:flex; gap:var(--s2); flex-wrap:wrap; align-items:center; margin:0 0 var(--s3); } .toolbar + .banner{ margin-top:0; } .urow{ display:flex; gap:var(--s2); flex-wrap:wrap; align-items:center; } /* Checkboxes are excluded: they are drawn by the platform and want none of a text field's height, padding or border. */ .toolbar input:not([type=checkbox]), .toolbar select, .urow input:not([type=checkbox]), .urow select{ height:var(--ctl); padding:0 var(--s2); font:inherit; font-size:13px; line-height:normal; border:1px solid var(--border-strong); border-radius:0; background:var(--surface); color:var(--text); } .toolbar select, .urow select{ cursor:pointer; padding-right:var(--s1); } .toolbar input:focus-visible, .toolbar select:focus-visible, .urow input:focus-visible, .urow select:focus-visible{ outline:2px solid var(--accent); outline-offset:-2px; } .toolbar > input{ flex:1 1 240px; min-width:150px; } .urow input:not([type=checkbox]){ flex:1 1 140px; min-width:0; } /* Inline checkbox + label, sized to sit on the same line as the buttons. */ .chk{ display:inline-flex; align-items:center; gap:var(--s2); height:var(--ctl); padding:0 var(--s1); font-size:13px; color:var(--muted); white-space:nowrap; cursor:pointer; } .chk input{ width:16px; height:16px; margin:0; accent-color:var(--accent); cursor:pointer; } /* ══ FEEDBACK: banners, notes, console output, key/value ════════════════════════ */ .banner{ margin:var(--s3) 0 0; padding:9px var(--s3); border-radius:0; font-size:13px; font-weight:600; line-height:1.4; border:1px solid var(--border); border-left:3px solid var(--border-strong); background:var(--surface); color:var(--text); } .banner.ok{ background:var(--green-bg); color:var(--green); border-color:var(--wp-status-success-border-a); border-left-color:var(--green); } .banner.bad{ background:var(--red-bg); color:var(--red); border-color:var(--wp-status-error-border-a); border-left-color:var(--red); } .banner.warn{ background:var(--amber-bg); color:var(--amber); border-color:var(--wp-status-warning-border-a); border-left-color:var(--amber); } /* --muted, not --dim: #8d8d8d on white is 3.3:1, under the 4.5:1 floor at 12px, and the boxes the scripts fill are themselves .note — their primary toggle labels inherit this colour. */ .note{ font-size:12px; line-height:1.55; color:var(--muted); margin-top:var(--s2); } .note strong, .note em{ color:var(--text); } pre.out{ background:var(--wp-term-bg); color:var(--wp-term-fg); border-radius:0; padding:var(--s3) var(--s4); font-family:var(--mono); font-size:12px; line-height:1.55; white-space:pre-wrap; max-height:340px; overflow:auto; margin:var(--s3) 0 0; } pre.out .p{ color:var(--wp-term-pass); font-weight:700; } pre.out .f{ color:var(--wp-term-fail); font-weight:700; } table.kv{ border-collapse:collapse; font-size:13px; margin-top:var(--s2); } table.kv th{ text-align:left; padding:var(--s1) var(--s5) var(--s1) 0; color:var(--muted); font-weight:600; white-space:nowrap; } table.kv td{ padding:var(--s1) 0; font-variant-numeric:tabular-nums; font-weight:700; color:var(--text); } /* ══ DATA TABLES ═══════════════════════════════════════════════════════════════ table.users is the name admin.js already emits; table.grid is the same object under the shared name. One rule set serves both, so existing markup picks up the dense styling without being rewritten. border-collapse is separate rather than collapse because a collapsed border does not travel with a sticky header. */ table.grid, table.users{ width:100%; border-collapse:separate; border-spacing:0; font-size:13px; color:var(--text); background:var(--surface); } table.grid th, table.users th{ position:sticky; top:0; z-index:2; background:var(--head-bg); text-align:left; padding:var(--s2) var(--s3); white-space:nowrap; font-size:11px; font-weight:600; letter-spacing:.04em; text-transform:uppercase; color:var(--muted); box-shadow:inset 0 -1px 0 var(--border); } /* Cells never wrap: a wrapped cell turns one user into a 100px tall band and the table stops reading as rows. Anything genuinely long truncates (.ell) or is exempted by name in the page that owns the table. */ table.grid td, table.users td{ padding:var(--s1) var(--s3); border-bottom:1px solid var(--border); vertical-align:middle; white-space:nowrap; } table.grid tbody tr:last-child td, table.users tbody tr:last-child td{ border-bottom:none; } table.grid tbody tr:nth-child(even) td, table.users tbody tr:nth-child(even) td{ background:var(--zebra); } /* A neutral hover, not --accent-soft: that is .tag.admin's fill, and an "all projects" pill sitting on its own colour disappears the moment you hover it. */ table.grid tbody tr:hover td, table.users tbody tr:hover td{ background:var(--row-hover); } /* A row for an account this caller may see but not change. Dimmed as a whole so the disabled controls aren't the only clue. */ table.grid tbody tr.is-locked td, table.users tbody tr.is-locked td{ color:var(--muted); } /* Truncation has to hang off a block INSIDE the cell. max-width on a is advisory under table-layout:auto — the cell just grows to fit and the ellipsis never appears, which is the usual reason this trick looks like it works in the stylesheet and doesn't on the page. The scripts emit . */ .ell{ max-width:240px; } .ell > span{ display:block; max-width:240px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } /* Every action cell the scripts render is a .cellactions, and it must not wrap: unwrapped, the three buttons stack and the row grows fourfold. */ .cellactions{ display:flex; flex-wrap:nowrap; align-items:center; gap:var(--s1); white-space:nowrap; } /* Controls that live in a cell are one step smaller, which is what keeps a row at ~34px instead of ~100px. .chk is form-row sized by default, so it needs saying again here or checkbox rows stand 6px taller than the rest. */ button.mini{ height:var(--ctl-sm); padding:0 var(--s2); font-size:12px; } table.grid td .chk, table.users td .chk{ height:var(--ctl-sm); } select.role-select{ height:var(--ctl-sm); max-width:170px; padding:0 var(--s1) 0 var(--s2); font:inherit; font-size:12px; border:1px solid var(--border-strong); border-radius:0; background:var(--surface); color:var(--text); cursor:pointer; } select.role-select:hover{ border-color:var(--accent); } select.role-select.is-admin{ color:var(--accent); border-color:var(--accent); font-weight:600; } select.role-select:disabled{ color:var(--dim); border-color:var(--border); background:var(--bg); cursor:default; } .tag{ display:inline-block; padding:1px 8px; border-radius:11px; font-size:11px; font-weight:600; line-height:1.55; white-space:nowrap; vertical-align:middle; } .tag.admin{ background:var(--accent-soft); color:var(--accent); } .tag.super{ background:var(--wp-tag-super-bg); color:var(--wp-tag-super-text); } .tag.user{ background:var(--cds-layer-hover); color:var(--muted); } .tag.on{ background:var(--green-bg); color:var(--green); } .tag.off{ background:var(--red-bg); color:var(--red); } .tag.archived{ background:var(--amber-bg); color:var(--amber); } .me-tag{ font-size:11px; color:var(--dim); margin-left:6px; white-space:nowrap; } /* A wide table scrolls inside its own box so the page never scrolls sideways, and the capped height is what gives the sticky header something to do. */ .tscroll{ overflow:auto; max-height:min(70vh,640px); overscroll-behavior:contain; } /* ══ MODALS ════════════════════════════════════════════════════════════════════ The project-access dialog, shared by both pages. */ .modal-ov{ position:fixed; inset:0; background:var(--wp-scrim-cool-modal); display:flex; align-items:center; justify-content:center; z-index:10002; padding:var(--s5); } .modal-box{ background:var(--surface); border-radius:0; max-width:660px; width:100%; max-height:82vh; display:flex; flex-direction:column; overflow:hidden; box-shadow:var(--wp-shadow-modal); } .modal-head{ padding:var(--s3) var(--s4); border-bottom:1px solid var(--border); font-weight:700; } .modal-body{ padding:var(--s3) var(--s4); overflow:auto; } .modal-foot{ padding:var(--s3) var(--s4); border-top:1px solid var(--border); display:flex; gap:var(--s2); justify-content:flex-end; } .pickrow{ display:flex; align-items:center; gap:var(--s3); padding:var(--s2) var(--s1); border-bottom:1px solid var(--border); font-size:13px; } .pickrow:last-child{ border-bottom:none; } .pickrow > label{ display:flex; align-items:center; gap:var(--s2); flex:1; min-width:0; cursor:pointer; } .pickrow > label > span{ overflow:hidden; text-overflow:ellipsis; } /* ══ GATES & WARNINGS ══════════════════════════════════════════════════════════ */ .gate-overlay{ position:fixed; inset:0; background:var(--bg); display:flex; align-items:center; justify-content:center; padding:var(--s5); z-index:9999; } .gate-box{ background:var(--surface); border:1px solid var(--border); border-radius:0; padding:var(--s6); max-width:380px; width:100%; box-shadow:var(--wp-shadow-gate); } .gate-box h2{ margin:0 0 var(--s1); padding:0; border:0; font-size:17px; text-transform:none; letter-spacing:0; color:var(--text); } .gate-box p{ color:var(--muted); font-size:13px; margin:0 0 var(--s4); } .gate-msg{ color:var(--red); font-size:12px; min-height:16px; margin-bottom:var(--s2); } .secwarn{ background:var(--amber-bg); color:var(--amber); border:1px solid var(--amber); border-radius:0; padding:9px 13px; font-size:12px; margin-bottom:var(--s4); } /* ══ NARROW SCREENS ════════════════════════════════════════════════════════════ The page itself must never scroll sideways; the wide tables scroll inside their own box instead, and there they get the full page height to do it. */ @media (max-width:900px){ .wrap{ padding:var(--s4) var(--s3) 60px; } .card{ padding:var(--s3) var(--s4) var(--s4); } .toolbar > input{ flex:1 1 100%; } .tscroll{ max-height:none; } } @media (max-width:620px){ .urow input, .urow select, .urow button{ flex:1 1 100%; } }