/* =============================================================================
   The Code Smithy: control panel stylesheet (control-panel.css)

   READ THIS FIRST if you're editing by hand:
   - This file is for the control panel only (sign in, Welcome back, a game's page,
     New game, Backups, the players' page at a game's address, and the staff pages).
   - It imports tcs.css, which holds every color, font, size and corner as a named
     value (var(--...)), plus the shared pieces: headings (.t-h1 ...), buttons, status
     pills, messages and form fields.
   - Do NOT define colors, fonts or sizes here. Use the var(--...) names from tcs.css.
     If something new is needed, add it to the style guide (StyleGuide.dc.html) and to
     tcs.css first, then use it here.
   - Headings get their look from .t-h1 to .t-h4 on the element. The classes below
     only place things (spacing, layout); they never change a heading's size.

   Sections: 1 Sign in · 2 Page top (title bar, back link) · 3 Welcome back ·
   4 Games list and game panel · 5 New game · 6 Backups · 7 Players' page · 8 Staff pages
   ============================================================================= */
@import url("tcs.css");


/* =============================================================================
   1. Sign in (SignIn.dc.html): one white card in the middle of the page
   ============================================================================= */
.auth { display: flex; justify-content: center; padding-block: 40px 112px; }
.auth-card { width: min(100%, 480px); box-sizing: border-box; background: var(--white); color: var(--ink); border-radius: var(--radius-panel); box-shadow: var(--shadow-panel); padding: 40px; display: flex; flex-direction: column; gap: 24px; }
.auth-card--wide { width: min(100%, 720px); }
.auth-card__intro { font-size: var(--fs-body); color: var(--ink-muted); }
.auth-card .btn { width: 100%; }
.auth-card__foot { font-size: var(--fs-small); text-align: center; color: var(--ink-muted); }
.auth-card__step { display: flex; flex-direction: column; gap: 16px; padding-top: 24px; border-top: 1px solid var(--line-light); }

/* "Continue with Discord / Google / Patreon" */
.provider-list { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.btn-provider { display: flex; align-items: center; justify-content: center; gap: 12px; box-sizing: border-box; width: 100%; min-height: 52px; padding: 12px 20px; font-family: var(--font-body); font-size: var(--fs-body); font-weight: 700; text-decoration: none; border-radius: var(--radius-control); border: 1px solid var(--line-strong); background: var(--white); color: var(--navy-800); cursor: pointer; }
.theme-light .btn-provider, .theme-light .btn-provider:hover { color: var(--navy-800); }
.btn-provider:hover { border-color: var(--navy-800); background: var(--paper); }
.btn-provider__mark { display: inline-grid; place-items: center; width: 28px; height: 28px; border-radius: 50%; background: var(--navy-800); color: var(--white); font-size: var(--fs-caption); font-weight: 700; }

/* Two-factor setup (staff invite, account pages): the QR code to scan with an authenticator app,
   the key to type in instead, and the recovery codes to keep. The QR is an image with real alt text. */
.qr-box { display: grid; place-items: center; width: 200px; height: 200px; box-sizing: border-box; padding: 12px; border: 1px solid var(--line-light); border-radius: var(--radius-card); background: var(--white); }
.qr-box img { width: 100%; height: 100%; }
.key-text { font-family: var(--font-mono); font-size: var(--fs-small); color: var(--navy-800); letter-spacing: 0.08em; overflow-wrap: anywhere; }
.code-list { list-style: none; margin: 0; padding: 16px; display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 8px 16px; border-radius: var(--radius-card); background: var(--paper); font-family: var(--font-mono); font-size: var(--fs-small); color: var(--navy-800); }

/* "or continue with" with a line on each side */
.divider { display: flex; align-items: center; gap: 16px; font-size: var(--fs-small); color: var(--ink-muted); }
.divider::before, .divider::after { content: ""; flex: 1; border-top: 1px solid var(--line-light); }


/* =============================================================================
   2. Page top: the bar above a page (back link or breadcrumb + New game), and the
   title block on pages whose title sits on the dark background
   ============================================================================= */
.page-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 20px; padding-block: 40px 32px; }
.page-head__copy { display: flex; flex-direction: column; gap: 12px; }
.page-head--compact { align-items: center; padding-block: 28px 24px; }


/* =============================================================================
   3. Welcome back (SignedIn.dc.html): greeting, then every game as a card
   ============================================================================= */
.welcome { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 24px; padding-block: 56px 72px; }
.welcome__copy { flex: 999 1 520px; min-width: 0; display: flex; flex-direction: column; gap: 16px; }

/* Game cards: 3 across, 2 on tablets, 1 on phones */
.games-grid { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; }
@media (max-width: 1040px) { .games-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 680px) { .games-grid { grid-template-columns: minmax(0, 1fr); } }
.game-card { display: flex; flex-direction: column; background: var(--white); border: 1px solid var(--line-light); border-radius: var(--radius-card); overflow: hidden; }
.game-card__banner { position: relative; overflow: hidden; aspect-ratio: 4 / 1; background: linear-gradient(135deg, var(--navy-800), var(--art-banner)); display: flex; align-items: center; justify-content: center; }
.game-card__banner-label { font-size: var(--fs-caption); color: var(--text-on-dark-soft); }
.game-card__body { flex: 1; display: flex; flex-direction: column; gap: 8px; padding: 20px 22px 8px; }
/* Title, then the status pill on its own line under it: the same on every card, however long the title */
.game-card__top { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
.game-card__address { font-size: var(--fs-caption); color: var(--ink-muted); overflow-wrap: anywhere; }
.game-card__meta { font-size: var(--fs-small); color: var(--ink); }
.game-card__actions { display: flex; flex-wrap: wrap; gap: 10px; padding: 16px 22px 22px; }
/* The last tile: "+ New game" */
.game-card--new { border: 2px dashed var(--line-strong); background: transparent; }
.game-card__new-link, .theme-light .game-card__new-link { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; min-height: 260px; padding: 24px; text-align: center; text-decoration: none; color: var(--navy-800); }
.game-card__new-link:hover { background: var(--white); }
.game-card__new-plus { display: inline-grid; place-items: center; width: 56px; height: 56px; border-radius: 50%; background: var(--ember-button); color: var(--white); font-size: var(--fs-h2); font-weight: 700; line-height: 1; }

/* Your plan */
.account-plan { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 24px; background: var(--white); border: 1px solid var(--line-light); border-radius: var(--radius-card); padding: 32px; }
.account-plan__details { display: flex; flex-direction: column; gap: 6px; }
.account-plan__actions { display: flex; flex-wrap: wrap; gap: 12px; }

/* Need a hand */
.help-grid { list-style: none; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); gap: 32px; }
.help-grid__item { display: flex; flex-direction: column; gap: 8px; }
.help-grid__item p { color: var(--text-on-dark-soft); }


/* =============================================================================
   4. A game's pages (MyGames.dc.html, Backups.dc.html): the list of games on the
   left, the white panel on the right
   ============================================================================= */
.games-layout { display: flex; flex-wrap: wrap; gap: 32px; align-items: flex-start; padding-bottom: 112px; }
.games-nav { flex: 1 1 300px; min-width: 0; display: flex; flex-direction: column; gap: 12px; }
.games-nav__title { color: var(--muted-on-dark); }
.games-list { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.game-tile { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; width: 100%; box-sizing: border-box; text-align: left; text-decoration: none; padding: 16px 18px; border-radius: var(--radius-card); border: 1px solid var(--line-dark); background: var(--navy-900); color: var(--text-on-dark); font-family: var(--font-body); font-size: var(--fs-body); cursor: pointer; }
.game-tile:hover { border-color: var(--outline-on-dark); color: var(--text-on-dark); }
.game-tile.is-selected { border-color: var(--ember-on-dark); box-shadow: inset 0 0 0 1px var(--ember-on-dark); }
.game-tile__address { font-size: var(--fs-caption); color: var(--muted-on-dark); overflow-wrap: anywhere; }
.game-tile__meta { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; font-size: var(--fs-caption); color: var(--text-on-dark-soft); }

/* The white panel and its sections */
.game-panel { flex: 999 1 560px; min-width: 0; background: var(--white); color: var(--ink); border-radius: var(--radius-panel); box-shadow: var(--shadow-panel); overflow: hidden; }
.panel-section { padding: 24px 28px; border-bottom: 1px solid var(--line-light); display: flex; flex-direction: column; gap: 14px; }
.panel-section:last-child { border-bottom: 0; }
.panel-section--inset { background: var(--paper); }   /* a step in progress (restore) */
/* A dark section at the top of the white panel: makes WHICH game this is impossible to miss
   (a game's Backups page). A lighter navy than the page, set inside the panel so a white frame
   shows all round it. Headings white, eyebrow and links ember-on-dark, as on the navy page. */
.panel-section--dark { margin: 8px 8px 0; border: 0; border-radius: var(--radius-card); background: var(--navy-700); color: var(--text-on-dark); }
.panel-section--dark .t-h1, .panel-section--dark .t-h2, .panel-section--dark .t-h3, .panel-section--dark strong { color: var(--white); }
.panel-section--dark .t-eyebrow, .panel-section--dark a { color: var(--ember-on-dark); }
.panel-section--dark a:hover { color: var(--ember-on-dark-hover); }
.panel-section--dark .panel-head__address { color: var(--text-on-dark-soft); }
.panel-section--dark .panel-note { color: var(--muted-on-dark); }
.panel-section--dark a:focus-visible, .panel-section--dark button:focus-visible { outline-color: var(--ember-on-dark-hover); }
.panel-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-start; gap: 16px; }
.panel-head__name { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.panel-head__address { font-size: var(--fs-small); overflow-wrap: anywhere; }
.panel-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.panel-note { font-size: var(--fs-small); color: var(--ink-muted); }
.panel-row { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 12px; padding-block: 8px; font-size: var(--fs-small); border-top: 1px solid var(--paper-2); }
.panel-row:first-of-type { border-top: 0; }
.panel-row__what { display: flex; flex-direction: column; gap: 2px; }
.panel-row__sub { font-size: var(--fs-caption); color: var(--ink-muted); }
.panel-row a { font-weight: 700; }
.panel-list { display: flex; flex-direction: column; gap: 8px; padding-left: 20px; font-size: var(--fs-small); }   /* short bulleted facts */

/* The game's banner across the top of the panel; placeholder until the GM uploads one */
.game-banner { position: relative; aspect-ratio: 4 / 1; min-height: 140px; box-sizing: border-box; padding: 16px 24px; background: linear-gradient(135deg, var(--navy-800), var(--art-banner)); display: flex; align-items: center; justify-content: center; }
.game-banner--preview { border-radius: var(--radius-card); }
.game-banner__placeholder { max-width: 32em; text-align: center; font-size: var(--fs-small); color: var(--text-on-dark-soft); }
.game-banner__change { position: absolute; right: 16px; bottom: 16px; }
/* The uploaded banner (1600 × 400 WebP) covers the placeholder; alt="" (decorative, the name is the heading) */
.game-banner__image, .game-card__banner-image { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.game-banner--preview { overflow: hidden; }

/* Invite link: the address in a box + Copy */
.invite { display: flex; flex-wrap: wrap; gap: 10px; align-items: stretch; }
.invite__url { flex: 1 1 280px; min-width: 0; box-sizing: border-box; display: flex; align-items: center; min-height: 44px; padding: 10px 14px; font-family: var(--font-mono); font-size: var(--fs-small); color: var(--navy-800); background: var(--paper); border: 1px solid var(--line-light); border-radius: var(--radius-control); overflow-wrap: anywhere; }

/* Game details form */
.details-actions { display: flex; justify-content: flex-end; padding-top: 8px; border-top: 1px solid var(--paper-2); }
.details-actions .btn--card { min-width: 160px; }
.session-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 16px; }
.session-form .field { flex: 1 1 180px; }

/* Rich text editor (description): toolbar + editable area */
.rte { border: 1px solid var(--line-strong); border-radius: var(--radius-control); background: var(--white); overflow: hidden; }
.rte:focus-within { outline: 3px solid var(--ember-on-light); outline-offset: 1px; }
.rte__toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; padding: 6px; border-bottom: 1px solid var(--line-light); background: var(--paper); }
.rte__btn { display: inline-grid; place-items: center; width: 44px; height: 44px; padding: 0; border: 0; border-radius: var(--radius); background: transparent; color: var(--navy-800); cursor: pointer; }
.rte__btn:hover, .rte__btn[aria-pressed="true"] { background: var(--paper-2); }
.rte__icon { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.rte__icon circle { fill: currentColor; stroke: none; }
.rte__sep { width: 1px; height: 24px; margin-inline: 4px; background: var(--line-light); }
.rte__area { min-height: 160px; padding: 14px 16px; font-size: var(--fs-body); line-height: 1.55; color: var(--navy-800); display: flex; flex-direction: column; gap: 10px; }
.rte__area:focus { outline: none; }
.rte__area ul { margin: 0; padding-left: 22px; }
/* An empty editor shows a prompt (not real text, so it's never saved or read as content) */
.rte__area:empty::before { content: attr(data-placeholder); color: var(--ink-muted); pointer-events: none; }
.rte__area h4 { margin: 0; font-family: var(--font-display); font-size: var(--fs-h4); font-weight: 700; line-height: 1.25; }

/* In the app the editor is Quill (wwwroot/lib/quill, BSD licence). Its own stylesheet is NOT used
   (it brings its own fonts and colors); these are the few rules it needs, in our look. Quill turns
   .rte__area into a container with an editable .ql-editor inside, so the padding moves there. */
.rte__area.ql-container { position: relative; display: block; padding: 0; }
.rte__area .ql-editor { min-height: 160px; box-sizing: border-box; padding: 14px 16px; outline: none; white-space: pre-wrap; overflow-wrap: break-word; display: flex; flex-direction: column; gap: 10px; }
.rte__area .ql-editor p, .rte__area .ql-editor ol { margin: 0; }
.rte__area .ql-editor ol { padding-left: 0; counter-reset: list-0; }
.rte__area .ql-editor li { list-style-type: none; position: relative; padding-left: 1.5em; }
.rte__area .ql-editor li > .ql-ui { position: absolute; left: 0; }
.rte__area .ql-editor li[data-list="bullet"] > .ql-ui::before { content: "\2022"; }
.rte__area .ql-editor li[data-list="ordered"] { counter-increment: list-0; }
.rte__area .ql-editor li[data-list="ordered"] > .ql-ui::before { content: counter(list-0, decimal) ". "; }
.rte__area .ql-editor.ql-blank::before { content: attr(data-placeholder); position: absolute; left: 16px; right: 16px; color: var(--ink-muted); pointer-events: none; }
.rte__area .ql-clipboard { position: absolute; left: -100000px; top: 50%; height: 1px; overflow-y: hidden; }   /* Quill pastes through this, off screen */


/* =============================================================================
   5. New game (NewGame.dc.html): one white form card
   ============================================================================= */
.newgame { max-width: 880px; padding-bottom: 112px; }
.form-card { background: var(--white); color: var(--ink); border-radius: var(--radius-panel); box-shadow: var(--shadow-panel); padding: 8px 40px 40px; }
.form-card__section { display: flex; flex-direction: column; gap: 18px; padding-block: 32px; border-bottom: 1px solid var(--line-light); }
.form-card__section[hidden] { display: none; }   /* a choice not taken (Import: folder or backup) */

/* The address: "<username>-" [game part] ".foundry.thecodesmithy.com" */
.address-field { display: flex; align-items: stretch; border: 1px solid var(--line-strong); border-radius: var(--radius-control); overflow: hidden; background: var(--white); }
.address-field:focus-within { outline: 3px solid var(--ember-on-light); outline-offset: 1px; }
.address-field__part { display: flex; align-items: center; padding: 0 12px; background: var(--paper); color: var(--ink-muted); font-family: var(--font-mono); font-size: var(--fs-small); white-space: nowrap; }
.address-field__input { flex: 1 1 140px; min-width: 0; min-height: 48px; padding: 10px 12px; border: 0; color: var(--navy-800); background: transparent; font-family: var(--font-mono); font-size: var(--fs-small); }
.address-field__input:focus { outline: none; }
@media (max-width: 640px) { .address-field { flex-wrap: wrap; } .address-field__part { flex: 1 1 100%; padding-block: 6px; } }
.address-preview { font-size: var(--fs-small); color: var(--ink); overflow-wrap: anywhere; }

/* Upload box (e.g. a banner): click anywhere in it to choose a file, or drag one onto it. The real
   <input type="file"> covers the whole box, invisible, so both work (and keyboard and screen
   readers get a normal file field); the button inside is only its look. While a file is dragged
   over it the box turns solid navy-edged (.is-dragover, set by js/upload-drop.js).
   Use: <div class="upload-drop"><input class="upload-drop__input" type="file" ...>
        <span class="upload-drop__button" aria-hidden="true">Choose an image</span>
        <span class="upload-drop__text" aria-hidden="true">or drag it here</span></div> */
.upload-drop { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; min-height: 140px; box-sizing: border-box; padding: 24px; text-align: center; border: 2px dashed var(--line-strong); border-radius: var(--radius-card); background: var(--paper); }
.upload-drop:focus-within { outline: 3px solid var(--ember-on-light); outline-offset: 2px; }
.upload-drop.is-dragover { border-style: solid; border-color: var(--navy-800); background: var(--paper-2); }
.upload-drop__input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
.upload-drop__button { display: inline-flex; align-items: center; min-height: 44px; box-sizing: border-box; padding: 10px 16px; font-size: var(--fs-small); font-weight: 700; border-radius: var(--radius-control); background: var(--navy-800); color: var(--white); }
.upload-drop__text { font-size: var(--fs-small); color: var(--ink-muted); }


/* =============================================================================
   6. Backups (Backups.dc.html): the table of backups, and paging under it
   ============================================================================= */
.backup-table { width: 100%; border-collapse: collapse; font-size: var(--fs-small); }
.backup-table th, .backup-table td { padding: 12px 10px; text-align: left; vertical-align: middle; border-top: 1px solid var(--paper-2); }
.backup-table thead th { border-top: 0; color: var(--ink-muted); }
.backup-table__when { font-weight: 700; color: var(--navy-800); }
.backup-table__actions { text-align: right; white-space: nowrap; }
.backup-table__actions .btn--card { margin-left: 8px; }
.backup-table__label { display: none; }
.backup-table tr.is-selected { background: var(--paper); box-shadow: inset 4px 0 0 var(--ember-on-light); }
/* Narrow screens: each backup becomes a small card with labels */
@media (max-width: 760px) {
  .backup-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); }
  .backup-table, .backup-table tbody, .backup-table tr, .backup-table th, .backup-table td { display: block; }
  .backup-table tr { padding: 12px 0; border-top: 1px solid var(--line-light); }
  .backup-table th, .backup-table td { border: 0; padding: 4px 0; }
  .backup-table__label { display: inline-block; min-width: 72px; color: var(--ink-muted); }
  .backup-table__actions { text-align: left; white-space: normal; display: flex; flex-wrap: wrap; gap: 8px; padding-top: 8px; }
  .backup-table__actions .btn--card { margin-left: 0; }
}

/* Paging: "Showing 1–10 of 94" on the left, page buttons on the right */
.pager { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; padding-top: 12px; border-top: 1px solid var(--line-light); }
.pager__status { font-size: var(--fs-small); color: var(--ink-muted); }
.pager__status strong { color: var(--navy-800); }
.pager__list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.pager__btn { min-height: 44px; min-width: 44px; padding: 8px 14px; font-family: var(--font-body); font-size: var(--fs-small); font-weight: 700; border-radius: var(--radius-control); border: 1px solid var(--line-strong); background: var(--white); color: var(--navy-800); cursor: pointer; }
.pager__btn--page { padding-inline: 8px; }
.pager__btn:hover:not([disabled]):not(.is-current) { border-color: var(--navy-800); }
.pager__btn:focus-visible { outline: 3px solid var(--ember-on-light); outline-offset: 2px; }
.pager__btn.is-current { background: var(--navy-800); border-color: var(--navy-800); color: var(--white); cursor: default; }
.pager__btn[disabled] { background: var(--disabled-bg); border-color: var(--disabled-bg); color: var(--disabled-text); cursor: not-allowed; }
.pager__gap { display: inline-block; min-width: 20px; text-align: center; color: var(--ink-muted); }


/* =============================================================================
   7. Players' page (Join.dc.html): shown at a game's address while it's resting.
   No site menu: players aren't our customers. One card: banner, the game's name
   (the h1), what's happening, the next session, and the GM's description.
   ============================================================================= */
.join { min-height: 100vh; display: flex; flex-direction: column; }
.join__main { flex: 1; width: 100%; max-width: 760px; margin: 0 auto; padding: 40px 16px 32px; box-sizing: border-box; display: flex; flex-direction: column; gap: 20px; }
.join-card { background: var(--white); color: var(--ink); border-radius: var(--radius-panel); overflow: hidden; box-shadow: var(--shadow-panel); }
.join-card__body { padding: 28px clamp(20px, 4vw, 40px) 36px; display: flex; flex-direction: column; gap: 24px; }
.join-card__head { display: flex; flex-direction: column; gap: 4px; }
.join-card__gm { font-size: var(--fs-small); color: var(--ink-muted); }
.join-state { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; padding: 20px; border-radius: var(--radius-card); background: var(--paper); border: 1px solid var(--line-light); }
.join-state__actions { display: flex; flex-wrap: wrap; gap: 12px; padding-top: 4px; }
/* The live page holds every state's view in the box and shows one (the others are [hidden]). */
.join-state__view { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; }
.join-state__view[hidden] { display: none; }
.join-next { display: flex; flex-direction: column; gap: 4px; padding-left: 16px; border-left: 4px solid var(--ember-on-light); }
.join-next__when { font-size: var(--fs-lead); font-weight: 700; color: var(--navy-800); }
.join-next__note { font-size: var(--fs-small); color: var(--ink-muted); }
.join-about { display: flex; flex-direction: column; gap: 10px; }
.join-about__text { display: flex; flex-direction: column; gap: 10px; font-size: var(--fs-body); }
.join-about__text ul { padding-left: 22px; }
.join__footer { text-align: center; font-size: var(--fs-small); color: var(--muted-on-dark); }


/* =============================================================================
   8. Staff pages (StaffServers.dc.html, StaffGames.dc.html, StaffJob.dc.html): the
   same site, a little tighter, because staff scan many rows at once. Under the
   site header: "Staff" + the page's H1, the staff tabs, then white panels holding
   tables, facts and logs. Every staff page sits behind the two-factor sign-in.
   ============================================================================= */
.staff-main { display: flex; flex-direction: column; gap: 24px; padding-bottom: 112px; }

/* Staff tabs: Overview · Servers · Games · Jobs · Invites. The current one carries
   aria-current="page" and turns white. Wraps onto two lines on a phone. */
.staff-nav { display: flex; flex-wrap: wrap; gap: 4px; padding: 4px; border: 1px solid var(--line-dark); border-radius: var(--radius-card); background: var(--navy-900); }
.staff-nav__link, .staff-nav__link:hover { display: inline-flex; align-items: center; min-height: 44px; box-sizing: border-box; padding: 0 18px; border-radius: var(--radius-control); font-size: var(--fs-small); font-weight: 600; color: var(--text-on-dark-soft); text-decoration: none; }
.staff-nav__link:hover { color: var(--white); background: var(--navy-700); }
.staff-nav__link[aria-current="page"] { color: var(--navy-800); background: var(--white); }
.staff-nav__link:focus-visible { outline: 3px solid var(--ember-on-dark-hover); outline-offset: 2px; }

/* A white panel for a staff page's content (same look as the game panel, full width). ALWAYS
   with theme-light (class="staff-panel theme-light"), like every white area on the dark page:
   without it, headings and links keep their on-dark colors and vanish (2026-10-11).
   Inside it: .panel-section, .panel-head, .panel-actions, .panel-note from section 4. */
.staff-panel { background: var(--white); color: var(--ink); border-radius: var(--radius-panel); box-shadow: var(--shadow-panel); overflow: hidden; }

/* At a glance (staff Overview): a few big numbers, each with a link to the page behind it */
.stat-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr)); gap: 16px; }
.stat { display: flex; flex-direction: column; gap: 4px; padding: 18px 20px; border: 1px solid var(--line-light); border-radius: var(--radius-card); background: var(--paper); }
.stat .t-figure { color: var(--navy-800); }
.stat__label { font-size: var(--fs-small); color: var(--ink-muted); }
.stat--alert { border-color: var(--danger); background: var(--danger-bg); }   /* a number that needs a look (crashed games, failed jobs) */
.stat--alert .stat__label, .stat--alert .t-figure { color: var(--danger-text); }

/* Overview: one card per game server, with a meter per resource. A card with something in
   the red gets a red edge, and the same thing is listed under "Needs a look" above the cards. */
/* Each card and the stat grid carry theme-light too (white or paper on the dark page). */
.server-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(420px, 100%), 1fr)); gap: 24px; }
.server-card { display: flex; flex-direction: column; gap: 18px; padding: 24px 28px; background: var(--white); color: var(--ink); border-radius: var(--radius-panel); box-shadow: var(--shadow-panel); }
.server-card--attention { box-shadow: var(--shadow-panel), inset 0 0 0 3px var(--danger); }
.server-card__head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-start; gap: 12px; }
.server-card__name { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.server-card__meta { font-size: var(--fs-small); color: var(--ink-muted); }
.server-card__foot { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px 16px; padding-top: 14px; border-top: 1px solid var(--paper-2); font-size: var(--fs-small); }

/* Meter: one value against its limit (CPU, memory, disk). The bar fills a Paper 2 track; every
   fill has 3:1 or more against it (green 3.8, amber 3.3, red 5.9). The pill beside it says the
   state in a word (OK / High / Nearly full), so color is never the only sign. The fill is an SVG
   line whose x2 is the percent (no inline styles):
   <li class="meter meter--warn" role="meter" aria-label="Memory" aria-valuenow="78" aria-valuemin="0" aria-valuemax="100">
     <span class="meter__label">Memory</span>
     <span class="meter__track"><svg class="meter__bar" viewBox="0 0 100 1" preserveAspectRatio="none" aria-hidden="true" focusable="false"><line class="meter__fill" x1="0" y1="0.5" x2="78" y2="0.5"/></svg></span>
     <span class="meter__value">78% of 32 GB</span>
     <p class="status-pill status-pill--starting">…High</p></li>
   Yellow and red start at the same points as the alert emails (CPU and memory: 75% / 90%;
   disks: 70% / 80%). */
.meters { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.meter { display: grid; grid-template-columns: 104px minmax(0, 1fr) minmax(140px, max-content) minmax(112px, max-content); align-items: center; gap: 12px; font-size: var(--fs-small); }
.meter__label { font-weight: 700; color: var(--navy-800); }
.meter__track { display: block; height: 12px; border-radius: var(--radius-pill); background: var(--paper-2); overflow: hidden; }
.meter__bar { display: block; width: 100%; height: 100%; }
.meter__fill { stroke: var(--success); stroke-width: 1; }
.meter--warn .meter__fill { stroke: var(--warning); }
.meter--danger .meter__fill { stroke: var(--danger); }
.meter__value { font-variant-numeric: tabular-nums; color: var(--ink); white-space: nowrap; }
.meter .status-pill { justify-self: start; }
@media (max-width: 560px) {
  .meter { grid-template-columns: minmax(0, 1fr) auto; }
  .meter__track { grid-column: 1 / -1; }
}

/* A table of many rows (servers, games, jobs). Wide tables scroll sideways inside
   .table-scroll on narrow screens; the page itself never does.
   Use: <div class="table-scroll" tabindex="0" role="region" aria-label="Servers"><table class="data-table">… */
.table-scroll { overflow-x: auto; }
.table-scroll:focus-visible { outline: 3px solid var(--ember-on-light); outline-offset: 2px; }
.data-table { width: 100%; border-collapse: collapse; font-size: var(--fs-small); }
.data-table caption { caption-side: bottom; padding: 12px 0 0; text-align: left; font-size: var(--fs-caption); color: var(--ink-muted); }
.data-table th, .data-table td { padding: 12px; text-align: left; vertical-align: top; border-top: 1px solid var(--paper-2); }
.data-table thead th { border-top: 0; padding-block: 0 10px; font-size: var(--fs-label); font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-muted); white-space: nowrap; }
.data-table tbody tr:hover { background: var(--paper); }
.data-table__main { font-weight: 700; color: var(--navy-800); }   /* the row's name: a server, a game */
.data-table__sub { display: block; margin-top: 2px; font-size: var(--fs-caption); font-weight: 400; color: var(--ink-muted); }   /* a second line under a cell */
.data-table__num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.data-table__code { font-family: var(--font-mono); font-size: var(--fs-caption); }   /* ids, commands */
.data-table__actions { text-align: right; white-space: nowrap; }
.data-table__actions .btn--card + .btn--card { margin-left: 8px; }
.data-table tr.is-attention { background: var(--danger-bg); }   /* crashed, failed: the row to look at */

/* A small tag beside a name: "Test server", "Customer server", "Owner's". Facts, not states:
   states are status pills. Neutral colors only, and never the only sign of anything. */
.tag { display: inline-block; margin-left: 6px; padding: 1px 8px; border-radius: var(--radius); font-size: var(--fs-caption); font-weight: 600; line-height: 1.5; color: var(--neutral-text); background: var(--neutral-bg); vertical-align: 1px; white-space: nowrap; }

/* Facts about one thing (a job, a server): label on the left, value on the right; stacked on a phone */
.facts { display: grid; grid-template-columns: minmax(140px, max-content) minmax(0, 1fr); gap: 10px 24px; margin: 0; font-size: var(--fs-small); }
.facts dt { font-weight: 700; color: var(--ink-muted); }
.facts dd { margin: 0; color: var(--ink); overflow-wrap: anywhere; }
@media (max-width: 560px) { .facts { grid-template-columns: minmax(0, 1fr); gap: 2px; } .facts dd { margin-bottom: 10px; } }

/* What a server printed for a job: monospace, scrolls inside its box, keyboard-scrollable */
.log-box { max-height: 420px; overflow: auto; margin: 0; padding: 16px 18px; border-radius: var(--radius-card); background: var(--navy-950); color: var(--text-on-dark); font-family: var(--font-mono); font-size: var(--fs-caption); line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere; }
.log-box:focus-visible { outline: 3px solid var(--ember-on-light); outline-offset: 2px; }

/* Filters above a table: a checkbox or two, a search box. Wraps on a phone. */
.filter-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 24px; }
.filter-bar .check-field { min-height: 44px; padding-block: 0; align-items: center; }
/* The search box leads the bar and takes the room the checkboxes leave; the count of what's
   shown sits at the end. Use: <div class="field filter-bar__search"><label class="field__label"
   for="find">Find a game</label><input class="field__input" id="find" type="search" …></div> */
.filter-bar__search { flex: 1 1 320px; margin: 0; }
.filter-bar__count { margin: 0 0 0 auto; font-size: var(--fs-caption); color: var(--ink-muted); }

/* Staff > Games: one card per game (2026-10-17: the table got too wide and long to use). Three
   across on a wide screen (four was tight), two, then one on a phone. Everything about the game
   in one place: name and state on top, the facts, the last job in one line (what, how it went,
   when: the job's page has the rest), then its buttons along the bottom.
   A crashed game or a failed last job gets the red edge, like the server card. */
.staff-game-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; }
@media (max-width: 1040px) { .staff-game-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 680px) { .staff-game-grid { grid-template-columns: minmax(0, 1fr); } }
.staff-game-card { display: flex; flex-direction: column; gap: 14px; min-width: 0; padding: 20px; background: var(--white); color: var(--ink); border-radius: var(--radius-panel); box-shadow: var(--shadow-panel); }
.staff-game-card--attention { box-shadow: var(--shadow-panel), inset 0 0 0 3px var(--danger); }
.staff-game-card__head { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; min-width: 0; }
.staff-game-card__name { margin: 0; font-size: var(--fs-h4); font-weight: 700; line-height: 1.2; color: var(--navy-800); overflow-wrap: anywhere; }
.staff-game-card__meta { margin: 0; font-size: var(--fs-caption); color: var(--ink-muted); overflow-wrap: anywhere; }
.staff-game-card .facts { grid-template-columns: minmax(64px, max-content) minmax(0, 1fr); gap: 6px 12px; font-size: var(--fs-caption); }
.staff-game-card__job { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; font-size: var(--fs-caption); color: var(--ink-muted); }
.staff-game-card__links { display: flex; flex-wrap: wrap; gap: 4px 16px; font-size: var(--fs-small); }
.staff-game-card__actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: auto; padding-top: 14px; border-top: 1px solid var(--paper-2); }
.staff-game-card__actions .link-button { margin-left: 4px; }

/* ---- Your modules (2026-10-17): one list for the whole account, laid out like Sqyre's ----
   Tabs: "Your modules" / "Find modules". A tablist of real buttons (aria-selected); the
   current tab is navy with an ember underline, the others muted. Wraps on a phone. */
.tabs { display: flex; flex-wrap: wrap; gap: 4px 28px; border-bottom: 1px solid var(--line-light); }
.tabs__tab { min-height: 44px; padding: 10px 0; border: 0; border-bottom: 3px solid transparent; background: none; font: inherit; font-size: var(--fs-small); font-weight: 700; color: var(--ink-muted); cursor: pointer; }
.tabs__tab[aria-selected="true"] { color: var(--navy-800); border-bottom-color: var(--ember-on-light); }
.tabs__tab:focus-visible { outline: 3px solid var(--ember-on-light); outline-offset: 2px; border-radius: var(--radius-control); }

/* Module cards: three across, two on a tablet, one on a phone. Title, Foundry | repository
   links, author, a three-line summary, which of your games have it, then the button and the
   version along the bottom edge. Premium is a tag; a module with no version for a game's
   Foundry build says so in words, never by color alone. */
.module-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; }
@media (max-width: 1040px) { .module-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 680px) { .module-grid { grid-template-columns: minmax(0, 1fr); } }
.module-card { display: flex; flex-direction: column; gap: 8px; min-width: 0; padding: 20px 22px; background: var(--white); border: 1px solid var(--line-light); border-radius: var(--radius-card); }
.module-card__title { margin: 0; font-size: var(--fs-h4); font-weight: 700; line-height: 1.25; color: var(--navy-800); overflow-wrap: anywhere; }
.module-card__links { display: flex; flex-wrap: wrap; gap: 2px 14px; margin: 0; font-size: var(--fs-caption); }
.module-card__by { margin: 0; font-size: var(--fs-caption); font-weight: 700; color: var(--ink); }
.module-card__summary { margin: 0; font-size: var(--fs-small); line-height: 1.5; color: var(--ink); display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.module-card__games { margin: 0; font-size: var(--fs-caption); color: var(--ink-muted); }
.module-card__foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 12px; margin-top: auto; padding-top: 12px; border-top: 1px solid var(--paper-2); }
.module-card__version { font-size: var(--fs-caption); font-variant-numeric: tabular-nums; color: var(--ink-muted); }

/* ---- Your modules, part 2 (2026-10-18): details, updates, older versions ----
   The module's name on a card is a button: it opens the module's details dialog. It looks like
   the card title, with a quiet underline that darkens on hover, so it reads as clickable. */
.module-card__open { padding: 0; border: 0; background: none; font: inherit; color: inherit; text-align: left; cursor: pointer; text-decoration: underline; text-decoration-color: var(--line-strong); text-underline-offset: 3px; }
.module-card__open:hover { text-decoration-color: currentColor; }
.module-card__open:focus-visible { outline: 3px solid var(--ember-on-light); outline-offset: 2px; border-radius: var(--radius-control); }

/* Icon link: a small icon that opens something in a new tab, e.g. a version's changelog. Always
   an aria-label that says what it opens; the target is 44 px even though the icon is 20. */
.icon-link { display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; color: var(--ember-on-light); border-radius: var(--radius-control); vertical-align: middle; }
.icon-link:hover { color: var(--navy-800); }
.icon-link:focus-visible { outline: 3px solid var(--ember-on-light); outline-offset: 2px; }
.icon-link svg { width: 20px; height: 20px; }

/* Updates: a data table with a tick box per row (the header's tick box ticks them all), the
   version going from installed to new, and a bar underneath with how many are ticked and the
   two buttons. A too-new note goes in the row, in words. */
.data-table__check { width: 44px; }
.data-table__check input { width: 20px; height: 20px; margin: 0; accent-color: var(--navy-800); }
.data-table td.data-table__check, .data-table th.data-table__check { vertical-align: middle; }
.version-change { white-space: nowrap; font-variant-numeric: tabular-nums; }
.version-change__new { font-weight: 700; color: var(--success-text); }
.update-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px 24px; padding-top: 16px; border-top: 1px solid var(--paper-2); }
.update-bar__count { margin: 0; font-size: var(--fs-caption); color: var(--ink-muted); }
.update-bar__actions { display: flex; flex-wrap: wrap; gap: 12px; }

/* Wide dialog: for showing one thing in full (a module's details), not for a yes or no. A title
   row with a close button (the only icon-only button: aria-label "Close"), then two columns
   that stack under 820 px. */
.dialog--wide { width: min(100% - 32px, 1040px); }
.dialog__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.dialog__head .t-h2 { margin: 0; overflow-wrap: anywhere; }
.dialog__close { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; padding: 0; border: 0; border-radius: var(--radius-control); background: none; color: var(--ink-muted); cursor: pointer; }
.dialog__close:hover { color: var(--navy-800); background: var(--paper); }
.dialog__close:focus-visible { outline: 3px solid var(--ember-on-light); outline-offset: 2px; }
.dialog__close svg { width: 22px; height: 22px; }

/* Module details: the picture and the author's description on the left; what you can do, how
   it fits your games, the facts, the links and "Version to install" on the right. The
   description is the author's own text, cleaned of anything but plain formatting before it's
   shown: its headings are never bigger than body text, its pictures never wider than the column. */
.module-details { display: grid; grid-template-columns: minmax(0, 3fr) minmax(280px, 2fr); gap: 28px; }
@media (max-width: 820px) { .module-details { grid-template-columns: minmax(0, 1fr); } }
.module-details__main, .module-details__side { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.module-details__cover { display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover; border-radius: var(--radius-card); background: var(--paper-2); }
.module-details__description { font-size: var(--fs-small); line-height: 1.6; color: var(--ink); overflow-wrap: anywhere; }
.module-details__description > :first-child { margin-top: 0; }
.module-details__description p, .module-details__description ul, .module-details__description ol { margin: 0 0 0.75em; }
.module-details__description h1, .module-details__description h2, .module-details__description h3, .module-details__description h4, .module-details__description h5, .module-details__description h6 { margin: 1em 0 0.4em; font-family: var(--font-body); font-size: var(--fs-body); font-weight: 700; color: var(--navy-800); }
.module-details__description img { max-width: 100%; height: auto; }
.module-details__actions { display: flex; flex-wrap: wrap; gap: 12px; }
.module-details .facts { grid-template-columns: minmax(120px, max-content) minmax(0, 1fr); gap: 8px 16px; }
.module-details__version { display: flex; flex-direction: column; gap: 10px; padding-top: 16px; border-top: 1px solid var(--paper-2); }

/* A row of links (Foundry page, project, changelog, report a problem, license): wraps. */
.link-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 4px 18px; font-size: var(--fs-small); }

/* Files (a game's file manager, design: Files*.dc.html): the folder you're in as a path of
   buttons, file rows with a folder or file icon, and upload progress rows that reuse the
   meter's bar. Uploads use the Upload box (.upload-drop). */
.file-path { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 0; margin: 0; padding: 0; list-style: none; font-size: var(--fs-small); }
.file-path li + li::before { content: "/"; margin: 0 8px; color: var(--ink-muted); }
.file-path [aria-current="page"] { font-weight: 700; color: var(--navy-800); }
.file-name { display: inline-flex; align-items: center; gap: 10px; text-align: left; }
.file-name svg { flex: none; width: 20px; height: 20px; color: var(--navy-700); }
button.file-name { padding: 0; border: 0; background: none; font: inherit; font-weight: 700; color: var(--navy-800); cursor: pointer; text-decoration: underline; text-underline-offset: 3px; }
button.file-name:focus-visible { outline: 3px solid var(--ember-on-light); outline-offset: 2px; }
.upload-rows { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.upload-row { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 3fr) max-content; align-items: center; gap: 12px; font-size: var(--fs-small); }
.upload-row__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 700; color: var(--navy-800); }
.upload-row__value { font-variant-numeric: tabular-nums; color: var(--ink); white-space: nowrap; }
@media (max-width: 640px) { .upload-row { grid-template-columns: minmax(0, 1fr) max-content; } .upload-row .meter__track { grid-column: 1 / -1; } }
/* An upload's bar, two steps in one (2026-10-20, owner: a full green bar still saying "Uploading…"
   looked stuck): the first half is the upload from the computer (green, .meter__fill to 50), the
   second half adding it to the library on our side (navy, .meter__fill--adding from 50), filling as
   the server works through the batch. Until it's in, the rest of the bar has a slow moving stripe
   (.meter__wait), so it never looks frozen. Reduced motion: the stripe stands still. */
.meter__fill--adding { stroke: var(--navy-800); }
.meter__wait { stroke: var(--navy-800); stroke-opacity: 0.3; stroke-width: 1; stroke-dasharray: 2 2; animation: tcs-march 1s linear infinite; }
@keyframes tcs-march { to { stroke-dashoffset: -4; } }
@media (prefers-reduced-motion: reduce) { .meter__wait { animation: none; } }
/* Files: a column you can sort by is a button in its header (aria-sort on the th says which way);
   the preview frame shows one picture, video or sound at its own shape, on a dark mat. */
.sort-button { display: inline-flex; align-items: center; gap: 4px; min-height: 32px; padding: 0; border: 0; background: none; font: inherit; font-weight: 700; color: inherit; cursor: pointer; }
.sort-button:focus-visible { outline: 3px solid var(--ember-on-light); outline-offset: 2px; }
.sort-button svg { width: 14px; height: 14px; }
.file-preview { display: flex; align-items: center; justify-content: center; min-height: 320px; max-height: 60vh; border-radius: var(--radius-card); background: var(--navy-800); color: var(--text-on-dark-soft); overflow: hidden; font-size: var(--fs-small); text-align: center; padding: 16px; box-sizing: border-box; }
.file-preview img, .file-preview video { max-width: 100%; max-height: 56vh; }
/* Files: the list of folders and files. A table to screen readers (role="table", rows and cells),
   laid out with grid so rows can repeat in the canvas (a <table> can't hold its repeat tags).
   .file-table--found adds the Folder column (search results). Stacks on a phone. */
.file-table { display: flex; flex-direction: column; font-size: var(--fs-small); }
.file-table__row { display: grid; grid-template-columns: 44px minmax(0, 3fr) 96px 150px 184px; align-items: center; gap: 12px; padding: 8px 4px; border-bottom: 1px solid var(--line-light); }
.file-table--found .file-table__row { grid-template-columns: 44px minmax(0, 3fr) minmax(0, 2fr) 96px 150px 184px; }
.file-table__row--head { font-weight: 700; color: var(--navy-800); border-bottom: 2px solid var(--line-strong); }
.file-table__row:not(.file-table__row--head):hover { background: var(--paper); }
.file-table__check input { width: 20px; height: 20px; margin: 0; accent-color: var(--navy-800); }
.file-table__num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.file-table__muted { color: var(--ink-muted); }
.file-table__actions { text-align: right; white-space: nowrap; }
@media (max-width: 760px) {
    .file-table__row, .file-table--found .file-table__row { grid-template-columns: 44px minmax(0, 1fr) max-content; }
    .file-table__row > :nth-child(n+3):not(.file-table__num) { grid-column: 2 / -1; }
    .file-table__actions { text-align: left; }
}

/* Your files (2026-10-19): the file manager sits in a light outline (no shadow, no fixed height:
   the whole list always shows). View switch: Details | Thumbnails (two buttons, aria-pressed).
   Thumbnails: a grid of tiles, each a picture (or the file's icon), its name and size; the
   checkbox picks it, the tile opens it (a folder) or its preview (a file). */
.file-manager { display: flex; flex-direction: column; gap: 20px; padding: 24px; background: var(--white); border: 1px solid var(--line-light); border-radius: var(--radius-card); }
@media (max-width: 600px) { .file-manager { padding: 16px; } }
.view-switch { display: inline-flex; border: 1px solid var(--line-strong); border-radius: var(--radius-control); overflow: hidden; align-self: flex-end; }
.view-switch__option { min-height: 44px; padding: 0 16px; border: 0; background: var(--white); color: var(--navy-800); font: inherit; font-size: var(--fs-small); font-weight: 700; cursor: pointer; }
.view-switch__option + .view-switch__option { border-left: 1px solid var(--line-strong); }
.view-switch__option[aria-pressed="true"] { background: var(--navy-800); color: var(--white); }
.view-switch__option:focus-visible { outline: 3px solid var(--ember-on-light); outline-offset: -3px; }
.file-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 16px; }
.file-tile { position: relative; display: flex; flex-direction: column; gap: 4px; padding: 8px; background: var(--white); border: 1px solid var(--line-light); border-radius: var(--radius-card); }
.file-tile:hover { border-color: var(--line-strong); }
.file-tile--picked { border-color: var(--navy-800); box-shadow: 0 0 0 1px var(--navy-800); }
.file-tile__check { position: absolute; top: 14px; left: 14px; z-index: 1; width: 20px; height: 20px; margin: 0; accent-color: var(--navy-800); }
.file-tile__open { display: flex; flex-direction: column; gap: 6px; padding: 0; border: 0; background: none; color: var(--navy-800); font: inherit; text-align: left; cursor: pointer; }
.file-tile__open:focus-visible { outline: 3px solid var(--ember-on-light); outline-offset: 2px; border-radius: var(--radius-control); }
.file-tile__thumb { display: flex; align-items: center; justify-content: center; aspect-ratio: 1; overflow: hidden; border-radius: var(--radius-control); background: var(--paper); }
.file-tile__thumb img { width: 100%; height: 100%; object-fit: cover; }
.file-tile__thumb svg { width: 48px; height: 48px; color: var(--ink-muted); }
.file-tile__name { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; font-size: var(--fs-small); font-weight: 700; overflow-wrap: anywhere; }
.file-tile__meta { font-size: var(--fs-caption); color: var(--ink-muted); }
/* Drop overlay (2026-10-19, like The Forge): shown over the file manager while files are dragged
   anywhere on the page (files.js adds is-dragover); no animation. */
.file-manager { position: relative; }
.file-drop { display: none; position: absolute; inset: 0; z-index: 5; align-items: center; justify-content: center; padding: 24px; text-align: center; font-size: var(--fs-h4); font-weight: 700; color: var(--navy-800); background: color-mix(in srgb, var(--paper) 94%, transparent); border: 3px dashed var(--navy-800); border-radius: var(--radius-card); pointer-events: none; }
.file-manager.is-dragover .file-drop { display: flex; }
/* Working overlay (2026-10-20, owner: a big delete showed nothing happening): over the file
   manager while a change is being done (delete, unpack, move, rename), then while the storage is
   measured again. The waiting ring and what's happening, in words; clicks wait meanwhile. Its box
   stays in view on a long list (sticky). Reduced motion: the ring stands still, the words stay.
   Use: <div class="file-working"><div class="file-working__box" role="status">
          <div class="progress-ring progress-ring--waiting progress-ring--small" aria-hidden="true">(ring svg)</div>
          <p class="file-working__text">Deleting beneos_assets…</p><p class="file-working__hint">…</p></div></div> */
.file-working { position: absolute; inset: 0; z-index: 6; padding: 24px; background: color-mix(in srgb, var(--paper) 88%, transparent); border-radius: var(--radius-card); }
.file-working__box { position: sticky; top: 30vh; display: flex; flex-direction: column; align-items: center; gap: 12px; max-width: 520px; margin: 48px auto 0; padding: 32px; text-align: center; background: var(--white); border-radius: var(--radius-panel); box-shadow: var(--shadow-panel); }
.file-working__text { margin: 0; font-size: var(--fs-h4); font-weight: 700; color: var(--navy-800); overflow-wrap: anywhere; }
.file-working__hint { margin: 0; font-size: var(--fs-small); color: var(--ink-muted); }
.progress-ring--small { width: 72px; height: 72px; }
/* The same overlay over a form card (2026-10-20, Import: reading a dropped folder of 12,000 files
   showed nothing for a while). */
.form-card:has(> .file-working) { position: relative; }
@media (prefers-reduced-motion: reduce) { .progress-ring--waiting .progress-ring__svg { animation: none; } }
/* Long names and paths (2026-10-19: search results spilled into the next tile): a tile never grows
   past its column, its meta lines stay on one line with "…"; table cells wrap long paths. */
.file-tile { min-width: 0; }
.file-tile__meta { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-table__row > * { min-width: 0; overflow-wrap: anywhere; }
/* The meter's pill on one line (2026-10-19: "Room to spare" broke in two). */
.meter .status-pill { white-space: nowrap; }

/* Icon actions as buttons (Your files rows, 2026-10-19): the style guide's icon link, on a <button>. */
button.icon-link { padding: 0; border: 0; background: none; font: inherit; cursor: pointer; }
button.icon-link:disabled { color: var(--ink-muted); cursor: default; }
.file-table__actions { display: flex; justify-content: flex-end; gap: 2px; }
@media (max-width: 760px) { .file-table__actions { justify-content: flex-start; } }
/* Every row's actions column the same width (four 44 px icons), so the columns line up; the path and its buttons on one line. */
.file-manager .panel-head { align-items: center; }

/* Actions menu (2026-10-19, Staff > Customers): one "Actions" button that opens a short list, so a
   card shows only the actions that apply. Below the button, over what follows; Esc closes it. */
.action-menu { position: relative; margin-top: auto; padding-top: 14px; border-top: 1px solid var(--paper-2); }
.action-menu__list { position: absolute; top: calc(100% + 4px); left: 0; z-index: 10; display: flex; flex-direction: column; gap: 2px; min-width: 220px; margin: 0; padding: 6px; list-style: none; background: var(--white); border: 1px solid var(--line-light); border-radius: var(--radius-control); box-shadow: var(--shadow-panel); }
.action-menu__item { display: block; width: 100%; min-height: 44px; padding: 8px 12px; border: 0; border-radius: var(--radius-control); background: none; color: var(--navy-800); font: inherit; font-size: var(--fs-small); font-weight: 600; text-align: left; cursor: pointer; }
.action-menu__item:hover { background: var(--paper); }
.action-menu__item:focus-visible { outline: 3px solid var(--ember-on-light); outline-offset: -3px; }
.action-menu__item--danger { color: var(--ember-on-light); }