/* Claude update below */
:root {
  --accent: #0352a6;
  --border: #ddd;
  --danger: #b3261e;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: #f5f6f7;
  color: #1a1a1a;
}

.adminHeader {
  background: #1a1a1a;
  color: #fff;
  padding: 10px 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.adminHeader h1 { margin: 0; font-size: 17px; font-weight: 600; }

#userBox { display: flex; gap: 10px; align-items: center; font-size: 14px; }

/* Claude update below (Open Page, beside the site's name in the header:
   the page being edited is one thing every tab is about, so the way to
   look at it belongs where every tab can see it. Outlined rather than
   solid so it reads as a way out to the page, not as the primary action
   of whatever tab is open.) */
.headerLeft { display: flex; align-items: center; gap: 14px; }

.headerLink {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border: 1px solid rgba(255, 255, 255, 0.45);
  border-radius: 6px;
  color: #fff;
  text-decoration: none;
  font-size: 13px;
  white-space: nowrap;
}

.headerLink:hover { background: rgba(255, 255, 255, 0.14); }
/* Claude update end */

.panel {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 18px 20px;
  max-width: 1000px;
  margin: 18px auto;
}

/* Claude update below (a dialog carrying .panel inherited its 18px TOP margin,
   which is a fixed number rather than auto, so a dialog capped by max-height
   sat 18px from the top and put every remaining pixel underneath itself. That
   is the uneven gap: auto on both keeps them equal. #editorPanel already said
   this for itself; every other dialog needs it too.) */
dialog.panel { margin: auto; }

#loginView { max-width: 380px; }

h2 { margin: 0 0 14px; font-size: 18px; }

form label { display: block; margin-bottom: 12px; font-size: 14px; }

form label.row { display: flex; align-items: center; gap: 8px; }

input[type="text"], input[type="password"], input[type="url"],
input[type="date"], textarea {
  width: 100%;
  padding: 8px 10px;
  margin-top: 4px;
  border: 1px solid var(--border);
  border-radius: 5px;
  font-size: 14px;
  font-family: inherit;
}

label.row input { width: auto; margin: 0; }

button {
  padding: 8px 16px;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: #fff;
  font-size: 14px;
  cursor: pointer;
}

button:hover { background: #f0f0f0; }

button.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
button.primary:hover { background: #04407f; }

button.danger { color: var(--danger); border-color: var(--danger); }

.listHeader { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }

table { width: 100%; border-collapse: collapse; font-size: 14px; }

th, td { border-bottom: 1px solid var(--border); padding: 7px 8px; text-align: left; }

th { background: #fafafa; }

tr.hiddenRow td { color: #999; }

td .rowBtn { padding: 4px 10px; font-size: 13px; }

fieldset { border: 1px solid var(--border); border-radius: 6px; margin-bottom: 12px; }

.imageRow { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }

#imgPreview { max-height: 60px; max-width: 100px; border: 1px solid var(--border); }

/* Claude update below (Item 5: settings background-image preview must never
   overflow the panel/screen, however large the uploaded image is) */
#sImgPreview {
  display: block;
  /* Claude update below (fits inside the fixed frame that holds the space;
     the frame carries the border now) */
  max-width: 100%;
  max-height: 100%;
  /* Claude update end */
  width: auto;
  height: auto;
}
fieldset img { max-width: 100%; }
/* Claude update end */

.formButtons { display: flex; gap: 10px; margin-top: 6px; }

.error {
  background: #fdecea;
  border: 1px solid #f5c6c2;
  color: var(--danger);
  border-radius: 6px;
  padding: 8px 12px;
  font-size: 14px;
}

.hint { color: #777; font-size: 12px; }

code { background: #f0f0f0; padding: 1px 5px; border-radius: 3px; }

/* Claude update below (Item 2/3: users + settings panels) */
td .rowBtn.danger { color: var(--danger); border-color: var(--danger); }

.addUserForm { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px; margin-top: 14px; }

.addUserForm label { margin-bottom: 0; flex: 1 1 160px; }

.addUserForm .error { flex-basis: 100%; }
/* Claude update end */

/* Claude update below (tab bar + Buttons/Useful Links editors) */
.tabBar {
  max-width: 1000px;
  margin: 18px auto 0;
  display: flex;
  gap: 6px;
  border-bottom: 2px solid var(--border);
}

.tabBtn {
  border: 1px solid var(--border);
  border-bottom: none;
  border-radius: 8px 8px 0 0;
  background: #ececec;
  padding: 9px 18px;
  font-size: 14px;
  color: #555;
}

.tabBtn:hover { background: #e2e2e2; }

.tabBtn.active {
  background: #fff;
  color: var(--accent);
  font-weight: 600;
  border-color: var(--border);
  position: relative;
  top: 2px;
  border-bottom: 2px solid #fff;
}

.tabPage > .panel:first-child { margin-top: 12px; }

.linkList { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }

.linkRow {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px;
  background: #fafafa;
}

.linkRow input[type="text"] { margin-top: 0; }
.linkRow .lrLabel { flex: 1 1 150px; }
.linkRow .lrUrl { flex: 2 1 240px; }
.linkRow .lrNewTab { margin-bottom: 0; white-space: nowrap; font-size: 13px; }
.lrControls { display: flex; gap: 4px; margin-left: auto; }
.lrControls .rowBtn { padding: 4px 8px; }
/* Claude update end */

/* Claude update below (image display select, gradient overlay controls,
   per-row useful-link images) */
select {
  width: 100%;
  padding: 8px 10px;
  margin-top: 4px;
  border: 1px solid var(--border);
  border-radius: 5px;
  font-size: 14px;
  font-family: inherit;
  background: #fff;
}

fieldset label.stacked { margin-top: 10px; }

.overlayRow {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: flex-end;
  margin-top: 10px;
}

.overlayRow .overlayTitle { flex-basis: 100%; font-size: 13px; font-weight: 600; }

.overlayRow label { margin-bottom: 0; font-size: 13px; }

.overlayRow input[type="color"] {
  display: block;
  margin-top: 4px;
  width: 52px;
  height: 30px;
  padding: 1px;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: #fff;
}

.overlayRow input[type="number"] { width: 80px; display: block; }
/* Claude update end */

/* Claude update below (banner color pickers, same shape as the overlay row) */
.colorRow {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: flex-end;
  margin: 0 0 14px;
}

.colorRow .colorRowTitle { flex-basis: 100%; font-size: 13px; font-weight: 600; }

.colorRow label { margin-bottom: 0; font-size: 13px; }

.colorRow label.row { align-self: center; white-space: nowrap; }

.colorRow input[type="color"] {
  display: block;
  margin-top: 4px;
  width: 52px;
  height: 30px;
  padding: 1px;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: #fff;
}

.colorRow label.disabled { opacity: 0.45; }
/* Claude update end */

/* Claude update below (Sort Date column always fits on one line;
   favicon preview kept small) */
th.colSortDate,
#sessionsTable td:nth-child(3),
#pastTable td:nth-child(3) { white-space: nowrap; }

/* Keep Preview and Edit on one line instead of stacking when the title
   column takes the width. */
#sessionsTable td:last-child,
#pastTable td:last-child,
#usersTable td:last-child { white-space: nowrap; text-align: right; }
#sessionsTable td:last-child .rowBtn,
#pastTable td:last-child .rowBtn,
#usersTable td:last-child .rowBtn { margin-left: 4px; }

#sFavPreview {
  max-height: 32px;
  max-width: 32px;
  border: 1px solid var(--border);
  border-radius: 4px;
}
/* Claude update end */

/* Claude update below (Webex SSO sign-in button + users table controls) */
a.ssoBtn {
  display: inline-block;
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--accent);
  border-radius: 4px;
  background: var(--accent);
  color: #fff;
  text-decoration: none;
  font: inherit;
}
a.ssoBtn:hover { background: #04407f; }
#usersTable input[type="email"] { width: 15rem; max-width: 100%; }
#usersTable select { font: inherit; }
#usersTable button[disabled], #dashView button[disabled] { opacity: 0.5; cursor: not-allowed; }
/* Claude update end */

/* Claude update below (Add / Edit user dialog) */
#userDialog {
  border: 1px solid #ccc;
  border-radius: 8px;
  padding: 0;
  max-width: 34rem;
  width: calc(100% - 2rem);
}
#userDialog::backdrop { background: rgba(0, 0, 0, 0.35); }
#userDialog form { padding: 1.25rem; }
#userDialog h2 { margin: 0 0 0.75rem; }
#userDialog fieldset {
  border: 1px solid #ddd;
  border-radius: 6px;
  margin: 0 0 1rem;
  padding: 0.75rem 1rem 1rem;
}
/* Claude update below (the collapsible local account section, made to sit in
   the same row of boxes as the fieldsets on either side of it) */
#userDialog details.userSection {
  border: 1px solid #ddd;
  border-radius: 6px;
  margin: 0 0 1rem;
  padding: 0.75rem 1rem 1rem;
}
#userDialog details.userSection > summary {
  font-weight: 600;
  cursor: pointer;
  /* Closed, the box is only the summary line, so the padding below it would
     leave a gap under the text. */
  margin: -0.15rem 0 0;
}
#userDialog details.userSection:not([open]) { padding-bottom: 0.75rem; }
/* Claude update end */
#userDialog legend { font-weight: 600; padding: 0 0.35rem; }
#userDialog label { display: block; margin: 0.5rem 0; }
/* Claude update below (the two rules above are id selectors, so they beat the
   generic form label.row / #userDialog input and the checkbox would stack
   above its own text at full width instead of sitting beside it) */
#userDialog label.row { display: flex; align-items: center; gap: 8px; }
#userDialog label.row input[type="checkbox"] { display: inline-block; width: auto; margin: 0; }
/* Claude update end */
#userDialog input, #userDialog select {
  display: block;
  width: 100%;
  margin-top: 0.2rem;
  font: inherit;
  padding: 0.35rem;
  box-sizing: border-box;
}
#userDialog .hint { margin: 0 0 0.5rem; }
#userDialog menu {
  display: flex;
  gap: 0.5rem;
  justify-content: flex-end;
  margin: 0;
  padding: 0;
}
/* Claude update end */

/* Claude update below (login page: Webex first, local account collapsed) */
#localLoginBox {
  margin-top: 1.25rem;
  border-top: 1px solid #e2e2e2;
  padding-top: 0.75rem;
}
#localLoginBox > summary {
  cursor: pointer;
  color: var(--accent);
  font-weight: 600;
  list-style-position: outside;
  padding: 0.25rem 0;
}
#localLoginBox > summary:hover { text-decoration: underline; }
#localLoginBox > form { margin-top: 0.75rem; }
#ssoBox { margin-bottom: 0.5rem; }
#userDialog menu .menuSpacer { flex: 1; }
#userDialog menu button.danger { margin-right: auto; }
/* Claude update end */

/* Claude update below (session editor as a modal + card preview popover) */
/* margin auto keeps the top and bottom gaps equal whether the content is
   short or the dialog is capped at max-height. */
#editorPanel {
  border: 1px solid #ccc;
  border-radius: 8px;
  padding: 1.25rem 1.25rem 0;
  max-width: 64rem;
  width: calc(100% - 3rem);
  max-height: 96vh;
  margin: auto;
  overflow: auto;
}
#editorPanel::backdrop { background: rgba(0, 0, 0, 0.35); }
.editorHead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.5rem;
}
.editorHead h2 { margin: 0; }

button.previewBtn { cursor: help; }

/* Hovering lifts the card off the page with a shadow only. The dimmed,
   blurred backdrop is reserved for a pinned preview, so a passing hover does
   not black out the whole screen. Matching the card's radius keeps the shadow
   off the square corners of the host box. */
#cardPreviewHost {
  position: fixed;
  z-index: 99999;
  display: none;
  overflow: auto;
  pointer-events: none;
  border-radius: 10px;
  box-shadow: 0px 0px 54px -4px rgba(0, 0, 0, 0.6);
}
#cardPreviewHost.visible { display: block; }
#cardPreviewHost.pinned { pointer-events: auto; }

#cardPreviewScrim {
  position: fixed;
  inset: 0;
  z-index: 99998;
  display: none;
  background: rgba(0, 0, 0, 0.3);
  backdrop-filter: blur(2px);
  pointer-events: none;
}
#cardPreviewScrim.visible { display: block; }

/* Save / Cancel / Delete stay on screen instead of scrolling out of reach.
   Sticky needs the scroll container to have no bottom padding, otherwise the
   bar parks above it. */
.formButtons.stickyButtons {
  position: sticky;
  bottom: 0;
  gap: 10px;
  background: #fff;
  border-top: 1px solid var(--border);
  padding: 12px 0 16px;
  margin-top: 14px;
}
.formButtons .menuSpacer { flex: 1; }
/* Claude update end */

/* Claude update below (Top Cards / Useful Links: read-only rows + modal) */
.cardListTable { margin-bottom: 10px; table-layout: fixed; }
.cardListTable th, .cardListTable td { font-size: 13px; vertical-align: middle; }
/* Percentages must total 100 and there must be one per column: with
   table-layout fixed, a column with no width of its own is left to absorb
   whatever remains, and when the others already add up to 100 that is
   nothing. Adding the Visible column without a width here is exactly what
   squashed it under the buttons. */
.cardListTable th:nth-child(1) { width: 26%; }
.cardListTable th:nth-child(2) { width: 16%; }
.cardListTable th:nth-child(3) { width: 17%; }
.cardListTable th:nth-child(4) { width: 8%; }
.cardListTable th:nth-child(5) { width: 7%; }
/* Preview, Edit and the two arrows come to ~195px, so this column has to stay
   wide enough for them at the narrowest the panel gets, or they spill over the
   edge of their own cell. */
.cardListTable th:last-child { width: 26%; }

/* The address is a link to the thing it points at, opened in a new tab so the
   admin is never navigated away from. */
td.cardCellClip a { color: var(--accent); }

/* Long descriptions and URLs would otherwise stretch the row out of the
   panel. The full value is on the cell's title attribute. */
td.cardCellClip {
  max-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Claude update below (the row that was just reordered, so it is obvious
   which entry moved and where it ended up. On the td rather than the tr,
   because a tr background is painted under its cells and would not show.) */
tr.cardRowMoved > td { background: aliceblue; }
/* Claude update end */
td.cardRowControls { white-space: nowrap; text-align: right; }
td.cardRowControls .rowBtn { margin-left: 4px; }
td.cardRowControls .rowBtn[disabled] { opacity: 0.4; cursor: not-allowed; }

/* Wider than it was (34rem) so the four buttons in the Image row have space
   to sit across, rather than the row wrapping. */
#cardDialog {
  border: 1px solid #ccc;
  border-radius: 8px;
  padding: 1.25rem 1.25rem 0;
  max-width: 42rem;
  width: calc(100% - 2rem);
  max-height: 92vh;
  overflow: auto;
}
#cardDialog::backdrop { background: rgba(0, 0, 0, 0.35); }
#cardDialog label { display: block; margin-bottom: 12px; font-size: 14px; }
#cardDialog label.row { display: flex; align-items: center; gap: 8px; }
#cImgPreview { max-height: 100px; max-width: 160px; border: 1px solid var(--border); }
/* Claude update end */

/* Claude update below (custom file pickers, image gallery, toast) */
.hiddenFileInput {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.fileRow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

/* Claude update below (Upload File / Pick from Gallery / Edit Image / Remove
   image have to sit on one line. At the shared 14px they came to more than
   the narrowest dialog could hold and wrapped onto a second row, which reads
   as two groups of buttons rather than one. Smaller here only: these four are
   a row of equals, not the form's own Save and Cancel.) */
.fileRow .rowBtn {
  font-size: 13px;
  padding: 6px 10px;
  white-space: nowrap;
}

.fileHint { margin: 6px 0 2px; }

.galleryActions { display: flex; gap: 8px; align-items: center; }

.galleryGrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 12px;
  margin-top: 10px;
}

.galleryTile {
  margin: 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px;
  background: #fafafa;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.galleryTile img {
  width: 100%;
  height: 110px;
  object-fit: contain;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 4px;
}

.galleryTile figcaption { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

.galleryName {
  font-size: 12px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.galleryFoot { display: flex; flex-direction: column; gap: 4px; margin-top: auto; }

.galleryUsed {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.galleryTile.pickable { cursor: pointer; }
.galleryTile.pickable:hover { border-color: var(--accent); background: #eef4fb; }

/* Claude update below (one picture's details, opened from a tile's Edit
   button. The destructive actions live in here rather than on the tiles, so
   the gallery reads as a wall of pictures instead of a wall of buttons.) */
#galleryFileDialog { max-width: 34rem; width: 92vw; }

.galleryFileBody {
  display: flex;
  gap: 16px;
  align-items: flex-start;
  flex-wrap: wrap;
}

#galleryFileImg {
  width: 180px;
  height: 140px;
  object-fit: contain;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 6px;
  flex: 0 0 auto;
}

.galleryFileFacts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 12px;
  margin: 0;
  font-size: 13px;
  flex: 1 1 14rem;
  min-width: 0;
}

.galleryFileFacts dt { color: #555; }

/* Claude update below (the image dialog dims what is behind it, the way the
   card preview does, so it reads as a layer over the page rather than a panel
   sitting on it) */
#galleryFileDialog::backdrop, #msgDialog::backdrop {
  background: rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(2px);
}

/* Claude update below (how much of the audit log is on screen, and the rest) */
.auditMore {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 10px;
}

.auditMore p { margin: 0; }
/* Claude update end */

/* Claude update below (alertDialog / confirmDialog) */
#msgDialog { max-width: 30rem; width: 92vw; }

/* The messages carry their own line breaks, from back when they were
   browser alerts. */
#msgDialogText { white-space: pre-line; margin: 0 0 4px; line-height: 1.5; }

.msgDialogButtons { justify-content: flex-end; }
/* Claude update end */

/* An action that cannot run right now looks dead but stays clickable, so the
   click can explain itself rather than being swallowed. */
button.btnBlocked, button.btnBlocked:hover {
  background: #e4e4e4;
  color: #767676;
  border-color: #cfcfcf;
  cursor: not-allowed;
}

.galleryFileFacts dd {
  margin: 0;
  /* A long filename or a list of six sessions must wrap rather than push the
     dialog wider than the viewport. */
  overflow-wrap: anywhere;
}
/* Claude update end */

#galleryPickDialog {
  max-width: 52rem;
  width: calc(100% - 3rem);
  max-height: 90vh;
  margin: auto;
  overflow: auto;
}
#galleryPickDialog::backdrop { background: rgba(0, 0, 0, 0.35); }

#toast {
  position: fixed;
  left: 50%;
  bottom: 28px;
  transform: translate(-50%, 12px);
  z-index: 100000;
  background: #1a1a1a;
  color: #fff;
  padding: 10px 18px;
  border-radius: 6px;
  font-size: 14px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
  cursor: pointer;
  opacity: 0;
  /* Only the slide is animated. Fading the opacity looked marginally nicer
     but left the toast stuck at 0 wherever transitions do not get frames,
     which reads as the toast never appearing at all. */
  transition: transform 0.15s;
}
#toast.visible { opacity: 1; transform: translate(-50%, 0); }

/* Claude update below (a toast parked just above the button that caused it.
   left/top are set inline from the button's own box; the translate lifts it
   clear of the button rather than covering it.) */
#toast.anchored { bottom: auto; transform: translate(-50%, calc(-100% + 8px)); }
#toast.anchored.visible { transform: translate(-50%, -100%); }
/* Claude update end */
/* Claude update end */

/* Claude update below (Audit Log / Restore, and the inherited Date text) */
#auditTable { table-layout: fixed; }
#auditTable th.colAuditWhen { width: 12rem; }
#auditTable th.colAuditUser { width: 11rem; }
/* Wide enough for Preview and Restore side by side. The table is
   table-layout: fixed, so a column that is too narrow does not grow: the
   buttons spill out of the panel instead. */
#auditTable th:last-child { width: 12.5rem; }
#auditTable td { vertical-align: top; }

/* The action can be several changes joined together. Wrap it rather than
   pushing the Restore button off the panel. */
td.auditAction { overflow-wrap: anywhere; }

td.auditControls { text-align: right; white-space: nowrap; }

/* The Preview control is a link so the browser opens the tab itself and the
   address can be copied, but the button look comes from the `button` element
   selector, so it has to be restated here. */
a.rowBtn {
  display: inline-block;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: #fff;
  color: #1a1a1a;
  text-decoration: none;
  cursor: pointer;
}

a.rowBtn:hover { background: #f0f0f0; }

td.auditControls a.rowBtn { margin-right: 6px; }
/* Page and Preview are the same width, so the Restore button beside them
   sits in the same place whatever a row happens to be about. */
a.auditPreviewBtn {
  min-width: 96px;
  text-align: center;
}

/* The icon comes from cardRender.js with no size of its own; public.css sizes
   it for the public page and the admin has to do the same. */
.newTabIcon {
  width: 0.85em;
  height: 0.85em;
  margin-left: 5px;
  vertical-align: -0.08em;
}

#auditRetention { margin-top: 0; }

/* A Date text that was not typed in but taken from the Sort Date. */
td.inheritedValue { font-style: italic; color: #777; }
/* Claude update end */

/* Claude update below (Admin Settings tab + the idle timeout warning) */
input[type="number"].adminNumber {
  width: 90px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 5px;
  font-size: 14px;
  font-family: inherit;
}

/* One grid per row, all with the same columns, so every box lines up down
   the tab no matter how long the wording to its left is. */
.adminRow {
  display: grid;
  grid-template-columns: 14rem auto 1fr;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
  font-size: 14px;
}

.adminRow > label { margin: 0; }

.adminRow input[type="checkbox"] {
  width: auto;
  margin: 0;
  justify-self: start;
}

.adminUnit { color: inherit; }

.fieldRange { color: #777; font-size: 12px; }

/* What the site is using on disk. Reads as a note rather than a setting, so
   it is boxed off from the form below it. */
.storageLine {
  margin: 0 0 18px;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: #fafafa;
  font-size: 13px;
  color: #444;
}

.storageLine .storageLow {
  color: #b00020;
  font-weight: 600;
}

#adminSettingsForm .hint { margin: 0 0 18px; }

@media (max-width: 640px) {
  .adminRow { grid-template-columns: 1fr auto; }
  .adminRow .adminUnit { grid-column: 1 / -1; }
}

#idleDialog { max-width: 420px; }

/* The timed refresh, in the top right under the header. Quieter than the
   main toast on purpose: it reports something the page did on its own, not
   an answer to anything the user asked for. */
#refreshToast {
  position: fixed;
  top: 68px;
  right: 20px;
  z-index: 99999;
  background: #fff;
  color: #444;
  border: 1px solid var(--border);
  padding: 7px 13px;
  border-radius: 6px;
  font-size: 13px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.12);
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity 0.2s, transform 0.2s;
  pointer-events: none;
}

#refreshToast.visible { opacity: 1; transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  #refreshToast { transition: none; }
}
/* Claude update end */

/* Claude update below (background image: the picture beside a scaled preview
   of the real public page, and the full size version of the same thing) */
.bgPreviewCols {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-bottom: 10px;
}

.bgPreviewCol {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.bgPreviewTitle { font-size: 12px; color: #777; }

/* The picture sits in a frame the same size as the preview beside it, kept
   even when there is no picture, so the preview never moves about depending
   on whether one is set. */
.bgImageFrame {
  width: 380px;
  height: 285px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: #fafafa;
  overflow: hidden;
}

#sImgEmpty { color: #999; font-size: 13px; }

/* An icon button: square, and the glyph carries the meaning, so the label is
   on title/aria-label instead. */
button.iconBtn {
  padding: 4px 0;
  width: 30px;
  font-size: 17px;
  line-height: 1;
}

button.iconBtn:disabled { opacity: 0.4; cursor: default; }
button.iconBtn:disabled:hover { background: #fff; }

.bgPreviewCol #sImgName { font-size: 13px; word-break: break-all; }

/* A 1024x768 screen shown at 380px wide. The iframe is laid out at the real
   size and scaled, so the image crop, the gradient and the text all land
   exactly where they would on that screen rather than being approximated. */
.heroPreviewViewport {
  width: 380px;
  height: 285px;
  overflow: hidden;
  position: relative;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: #fff;
}

.heroPreviewViewport iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 1024px;
  height: 768px;
  border: 0;
  /* 380 / 1024 */
  transform: scale(0.37109375);
  transform-origin: top left;
  /* A picture of the page, not something to interact with. */
  pointer-events: none;
}

/* Sits at the right hand end of the preview it opens, and smaller than the
   form's own buttons so it does not read as one of them. */
#btnHeroPreviewFull {
  align-self: flex-end;
  padding: 4px 10px;
  font-size: 12px;
}

.pendingNote {
  margin: 6px 0 0;
  padding: 7px 10px;
  border: 1px solid #e6d9a8;
  border-radius: 5px;
  background: #fdf8e6;
  font-size: 13px;
  color: #6b5a17;
}

/* Never a bare display on the dialog itself: that would override the UA's
   display:none for a closed one and leave it on screen. */
#heroPreviewDialog[open] {
  display: flex;
  flex-direction: column;
  width: 90vw;
  height: 90vh;
  max-width: 90vw;
  max-height: 90vh;
  padding: 14px 16px;
  overflow: hidden;
}

.heroPreviewHead {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: start;
  gap: 10px;
  margin-bottom: 10px;
}

.heroPreviewHead h2 { margin: 0; grid-column: 1; }
.heroPreviewHead .hint { margin: 4px 0 0; grid-column: 1; }
#heroPreviewClose { grid-column: 2; grid-row: 1 / span 2; width: auto; padding: 0 14px; }

.heroPreviewStage {
  flex: 1;
  position: relative;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: 4px;
  /* A 4:3 screen in a wider dialog leaves space at the sides. Grey rather
     than white, so it reads as being outside the simulated screen instead of
     as part of the page. */
  background: #e9eaec;
}

#heroPreviewBigFrame {
  position: absolute;
  top: 0;
  left: 0;
  width: 1024px;
  height: 768px;
  border: 0;
  background: #fff;
  transform-origin: top left;
}
/* Claude update end */

/* Claude update below (Pages tab + editor) */
/* Five columns instead of the card lists' six, and the controls cell holds
   Edit, Copy Markdown Link and Delete, so it needs the widest share. */
/* table-layout is fixed, so every column needs a width and they must total
   100. The last one holds five buttons and is sized for them. */
.pagesTable th:nth-child(1) { width: 19%; }
.pagesTable th:nth-child(2) { width: 17%; }
.pagesTable th:nth-child(3) { width: 9%; }
.pagesTable th:nth-child(4) { width: 6%; }
.pagesTable th:last-child { width: 49%; }

.slugWarn { color: #a15c00; }

#pageUsedByDialog { width: min(46rem, 94vw); max-height: 90vh; overflow: auto; }

/* Filling the screen with the page. The meta fields and the head buttons are
   about the page's address, not its text, so they go; the editing surface
   takes every pixel they leave. .panel's own max-width has to be undone. */
.wysiwygGrow { flex: 1; }

#pageDialog.pageFocus {
  width: 98vw;
  max-width: none;
  height: 96vh;
  max-height: 96vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
#pageDialog.pageFocus .editorHead,
#pageDialog.pageFocus .pageMetaCols,
#pageDialog.pageFocus > .hint { display: none; }
/* Filling the screen is also the chance to see the page's own measure: the
   text sits in a column the width the public page gives it, centred, rather
   than running the whole way across a wide monitor. --pageEditW is what the
   Width control sets; 100% is the Full option. */
#pageDialog.pageFocus .pageBodyEdit {
  flex: 1;
  max-height: none;
  min-height: 0;
  width: 100%;
  max-width: var(--pageEditW, 860px);
  margin: 0 auto;
}

#pageWidthWrap {
  display: none;
  align-items: center;
  gap: 6px;
  margin: 0;
  font-size: 13px;
  color: #555;
  white-space: nowrap;
}
#pageWidthWrap select { width: auto; margin: 0; padding: 4px 6px; font-size: 13px; }
/* The size box is typed into as well as picked from. The list beside it is
   built rather than a datalist, which the browser filters down to what has
   been typed: a box reading 8 would offer only 8, 18 and 28. */
.sizeField { position: relative; display: inline-flex; align-items: stretch; }
.wysiwygField input#pageFontSize {
  width: 3.4rem;
  margin: 0;
  padding: 4px 6px;
  font-size: 13px;
  /* Said outright rather than left to the generic input rule, which is keyed
     on [type="text"] and so used to leave the browser's own dark border. */
  border: 1px solid var(--border);
  border-right: 0;
  border-radius: 5px 0 0 5px;
  background: #fff;
}
.sizeField > button {
  border: 1px solid var(--border);
  padding: 0 6px;
  font-size: 11px;
  border-radius: 0 5px 5px 0;
  line-height: 1;
}
.sizeMenu {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 50;
  margin-top: 2px;
  max-height: 260px;
  overflow-y: auto;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
  display: flex;
  flex-direction: column;
  min-width: 4.4rem;
}
.sizeMenu button {
  border: 0;
  border-radius: 0;
  text-align: left;
  padding: 5px 12px;
  font-size: 13px;
  background: #fff;
}
.sizeMenu button:hover { background: #eef3fa; }
#pageDialog.pageFocus #pageWidthWrap { display: inline-flex; }
#pageDialog.pageFocus .formButtons.stickyButtons { margin-top: 8px; }

#pageImageSourceDialog { width: min(30rem, 94vw); }
.pageImageSourceRow { flex-wrap: wrap; margin-top: 14px; }

#pageTableDialog { width: min(26rem, 94vw); }
.tableSizeRow { display: flex; gap: 16px; align-items: flex-end; }
.tableSizeRow label { margin-bottom: 12px; }
/* The checkbox sits on the button row, at the far end from Insert. */
.tableHeadRow {
  margin: 0;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  white-space: nowrap;
}
#pageTableDialog input[type="number"] {
  width: 6rem;
  padding: 8px 10px;
  margin-top: 4px;
  border: 1px solid var(--border);
  border-radius: 5px;
  font-size: 14px;
}
.usedByTable th:nth-child(1) { width: 22%; }
.usedByTable th:nth-child(2) { width: 48%; }
.usedByTable th:last-child { width: 30%; }

/* Claude update below (Clone, and the site to copy onto) */
#cloneDialog { width: min(34rem, 94vw); }
#cloneDialog p { margin: 0 0 4px; line-height: 1.45; }
/* Three buttons rather than two, so Cancel sits away from the two that act. */
.cloneButtons { justify-content: flex-end; flex-wrap: wrap; }
.cloneButtons #cloneCancel { margin-right: auto; }

#cloneSiteDialog { width: min(38rem, 94vw); }
.cloneFind { display: block; margin: 4px 0 10px; font-size: 13px; }
.cloneFind input { width: 100%; }
/* The list is the point of this dialog, so it gets the height and scrolls
   inside itself: a hundred subsites must not push the Copy button off the
   bottom of the screen. */
.cloneSiteScroll {
  max-height: 46vh;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 6px;
}
.cloneSiteTable { margin: 0; }
.cloneSiteTable th { width: 50%; position: sticky; top: 0; background: #fff; }
.cloneSiteTable td { cursor: pointer; }
.cloneSiteTable tr.cloneChosen td { background: #e7f1fb; font-weight: 600; }
.cloneSiteTable tr:hover td { background: #f3f6fa; }
.cloneSiteTable tr.cloneChosen:hover td { background: #e7f1fb; }
.cloneSitePath { font-family: ui-monospace, Menlo, Consolas, monospace; }
.cloneSiteOff { color: var(--muted); font-style: italic; }
/* A header that sorts, drawn as the header text rather than as a button. */
.sortHead {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.sortHead::after { content: ''; }
.sortHead.sortAsc::after { content: ' \25B2'; font-size: 10px; }
.sortHead.sortDesc::after { content: ' \25BC'; font-size: 10px; }
/* Claude update end */

#pageDialog {
  width: min(60rem, 96vw);
  max-height: 94vh;
  overflow: auto;
  padding-bottom: 0;
}
#pageDialog::backdrop { background: rgba(0, 0, 0, 0.35); }
#pageDialog .editorHead { gap: 8px; }
#pageDialog .editorHead .rowBtn { font-size: 13px; padding: 4px 10px; }

.pageMetaCols { display: flex; gap: 16px; flex-wrap: wrap; }
.pageMetaCols > label { flex: 1 1 240px; }
/* The Visible line carries the way into full screen at its right hand end:
   the button is about the whole editor, not about one toolbar group, so it
   sits above the toolbar and is a full sized button rather than an icon. */
.pageVisibleRow {
  align-self: end;
  margin-bottom: 14px;
  display: flex;
  align-items: center;
  gap: 14px;
}
.pageVisibleRow label { margin: 0; }
.pageVisibleRow #pageExpandBtn {
  margin-left: auto;
  font-size: 14px;
  padding: 7px 14px;
  white-space: nowrap;
}
/* Minimize only means anything while the editor fills the screen, and that
   is also the only time the button above is out of reach. */
#pageMinimizeBtn { display: none; margin-right: auto; }
#pageDialog.pageFocus #pageMinimizeBtn { display: inline-block; }

.slugRow { display: flex; align-items: center; gap: 2px; }
.slugRow .slugFixed { color: #777; font-size: 14px; }
.slugRow input { flex: 1; min-width: 0; }

.wysiwygBar {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  align-items: center;
  border: 1px solid var(--border);
  border-bottom: 0;
  border-radius: 6px 6px 0 0;
  background: #f7f8fa;
  padding: 6px 8px;
  margin-top: 10px;
}
.wysiwygBar button {
  padding: 4px 9px;
  font-size: 13px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 4px;
  cursor: pointer;
}
.wysiwygBar button:hover { background: #eef3fa; border-color: var(--accent); }
.wysiwygSep { width: 1px; height: 20px; background: var(--border); margin: 0 4px; }

/* The editing surface previews the page's own typography closely enough to
   trust: headings, lists, quotes, tables and pictures read as they will. */
.pageBodyEdit {
  border: 1px solid var(--border);
  border-radius: 0 0 6px 6px;
  min-height: 320px;
  max-height: 55vh;
  overflow-y: auto;
  padding: 14px 16px;
  background: #fff;
  line-height: 1.6;
  overflow-wrap: break-word;
}
.pageBodyEdit:focus { outline: 2px solid #cfd4da; outline-offset: -1px; }
.pageBodyEdit h1 { font-size: 26px; margin: 0.9em 0 0.4em; }
.pageBodyEdit h2 { font-size: 22px; margin: 0.9em 0 0.4em; }
.pageBodyEdit h3 { font-size: 18px; margin: 0.9em 0 0.4em; }
.pageBodyEdit p { margin: 0.6em 0; }
.pageBodyEdit ul, .pageBodyEdit ol { margin: 0.6em 0; padding-left: 28px; }
.pageBodyEdit img { max-width: 100%; height: auto; }
.pageBodyEdit blockquote {
  margin: 0.8em 0;
  padding: 2px 14px;
  border-left: 4px solid var(--accent);
  background: #f6f8fb;
}
.pageBodyEdit table { border-collapse: collapse; margin: 0.8em 0; }
/* A table with its lines turned off still needs to be workable, so the
   editor draws a faint guide where the page will draw nothing. */
.pageBodyEdit table.pgTblNoBorder th,
.pageBodyEdit table.pgTblNoBorder td { border: 1px dashed #e4e4e4; }
.pageBodyEdit th, .pageBodyEdit td {
  border: 1px solid #ccc;
  padding: 4px 8px;
}
.pageBodyEdit a { color: var(--accent); }

/* The same five rules page.css uses, so a picture sits where the editor shows
   it sitting. The clear keeps a float inside the editing surface. */
.pageBodyEdit::after { content: ''; display: block; clear: both; }
.pageBodyEdit img.pgImgLeft { display: block; margin: 0.8em auto 0.8em 0; }
.pageBodyEdit img.pgImgCenter { display: block; margin: 0.8em auto; }
.pageBodyEdit img.pgImgRight { display: block; margin: 0.8em 0 0.8em auto; }
.pageBodyEdit img.pgImgWrapLeft { float: left; margin: 6px 20px 12px 0; }
.pageBodyEdit img.pgImgWrapRight { float: right; margin: 6px 0 12px 20px; }

/* The buttons that appear over the picture the pointer is on. position:fixed
   so they need no offset-parent arithmetic inside a dialog the browser draws
   in its top layer, and a z-index above the dialog's own content. */
.pageImgTools {
  position: fixed;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 3px 4px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.96);
  border: 1px solid var(--border);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.22);
}
.pageImgTools button {
  font-size: 12px;
  padding: 3px 7px;
  border: 1px solid transparent;
  border-radius: 4px;
  background: none;
  cursor: pointer;
  white-space: nowrap;
}
.pageImgTools button:hover { background: #eef3fa; border-color: var(--accent); }
.pageImgTools button.active { background: var(--accent); color: #fff; }
.pageImgTools .wysiwygSep { height: 16px; margin: 0 2px; }

.pageImgHandle {
  position: fixed;
  z-index: 40;
  width: 14px;
  height: 14px;
  border-radius: 3px;
  background: #fff;
  border: 2px solid var(--accent);
  cursor: nwse-resize;
  touch-action: none;
}

/* The toolbar button for whatever the caret is already inside, so the bar
   reads as the state of the text rather than only as a set of actions. */
.wysiwygBar button.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.wysiwygBar button.armed {
  background: #fff3cd;
  border-color: #d39e00;
}

.wysiwygField {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  color: #555;
}
.wysiwygField select {
  font-size: 12px;
  padding: 3px 4px;
  max-width: 8.5rem;
}

/* A color button says which color it will apply, under the letter it
   applies it to, the way every word processor draws it. */
.wysiwygSwatchBtn {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  line-height: 1;
  padding: 3px 7px 4px;
}
.wysiwygSwatchBtn span { font-size: 13px; }
/* The highlight button wears what it does: an A on highlighter yellow. */
.wysiwygSwatchBtn .hiliteA {
  background: #fff176;
  border-radius: 2px;
  padding: 0 3px;
  color: #222;
}
.wysiwygSwatchBtn i {
  display: block;
  width: 16px;
  height: 4px;
  border-radius: 1px;
  background: #222;
  /* A checkerboard shows through when the color is none at all. */
  background-image: linear-gradient(45deg, #ccc 25%, transparent 25%),
                    linear-gradient(-45deg, #ccc 25%, transparent 25%);
  background-size: 6px 6px;
}

.pageBodyEdit pre {
  background: #f4f4f4;
  border: 1px solid #e2e2e2;
  border-radius: 6px;
  padding: 10px 12px;
  overflow-x: auto;
  font-family: "Courier New", Courier, monospace;
  white-space: pre-wrap;
}
.pageBodyEdit code {
  background: #f4f4f4;
  border-radius: 3px;
  padding: 1px 4px;
  font-family: "Courier New", Courier, monospace;
}
.pageBodyEdit pre code { background: none; padding: 0; }
/* While the painter is armed, so the pointer says what the next drag does. */
.pageBodyEdit.painting { cursor: copy; }

#pageSourceDialog { width: min(60rem, 96vw); }
#pageSourceDialog::backdrop { background: rgba(0, 0, 0, 0.35); }
.pageSourceText {
  width: 100%;
  min-height: 55vh;
  font-family: "Courier New", Courier, monospace;
  font-size: 13px;
  line-height: 1.45;
  white-space: pre;
  overflow-wrap: normal;
  overflow-x: auto;
}

/* A class setting display beats the browser's own [hidden] rule, the same
   trap the image editor hit, so say it once for both. */
#pageDialog [hidden] { display: none !important; }

#pageLinkDialog { width: min(28rem, 94vw); }
/* Claude update end */
