/** * OpenStation — Native Posts window styles. * * Scoped to `.desktop-mode-posts` so the rules can't leak into other * windows. Layout: a toolbar across the top, a flex-1 body that hosts * a ``, and a footer pager at the bottom. The table is * told to size to the body via `--os-ui-table-max-height: 100%` so * sticky-header engages without an outer scrollbar. * * Cell-content styles are NOT here — they're inlined in * `src/posts-window/index.ts` because `` renders into its * own shadow DOM and document stylesheets do not cross that boundary. * * @since 0.8.0 */ .desktop-mode-posts { display: flex; flex-direction: column; height: 100%; width: 100%; background: var( --os-ui-surface, #fff ); color: var( --os-ui-fg, #1d2327 ); } /* --- Toolbar --------------------------------------------------------- */ .os-posts__toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; padding: 10px 14px; border-bottom: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) ); background: var( --os-ui-surface-elevated, #f6f7f7 ); } .os-posts__toolbar-left, .os-posts__toolbar-right, .os-posts__toolbar-trailing { display: flex; align-items: center; gap: 8px; } /* * Honour the `hidden` attribute. UA stylesheet sets * `[hidden] { display: none }` but our explicit `display: flex` * above wins on specificity, so we restore the hide behaviour * for `bulkBar.hidden = true` from JS. */ .os-posts__toolbar-right[hidden] { display: none; } .os-posts__toolbar-trailing { margin-inline-start: auto; } .os-posts__count { font-size: 12px; font-weight: 600; color: var( --os-ui-fg-muted, #50575e ); font-variant-numeric: tabular-nums; padding-inline-end: 4px; } /* --- Body ------------------------------------------------------------ */ .os-posts__body { flex: 1 1 auto; /* * `min-width: 0` is the load-bearing piece — without it, the * flex item's intrinsic width (the table's column-sum) becomes * its hard minimum, which pushes the table past the window's * right edge and turns the WHOLE WINDOW into a horizontal * scroller. With `min-width: 0`, the flex item shrinks to the * available space and the table's own `.scroll` container * picks up the horizontal overflow internally. */ min-width: 0; min-height: 0; display: flex; padding: 8px 12px; } .os-posts__body os-table { flex: 1 1 auto; min-width: 0; --os-ui-table-max-height: 100%; } /* --- Empty state ----------------------------------------------------- */ .os-posts__empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; padding: 48px 24px; color: var( --os-ui-fg-muted, #50575e ); text-align: center; } .os-posts__empty .dashicons { font-size: 40px; width: 40px; height: 40px; color: var( --os-ui-fg-muted, #a7aaad ); } .os-posts__empty p { margin: 0; } .os-posts__empty-hint { font-size: 13px; color: var( --os-ui-fg-muted, #646970 ); } /* --- Footer / pager -------------------------------------------------- */ .os-posts__pager { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 14px; border-top: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) ); background: var( --os-ui-surface-elevated, #f6f7f7 ); font-size: 13px; } .os-posts__pager-meta { color: var( --os-ui-fg-muted, #50575e ); font-variant-numeric: tabular-nums; } .os-posts__pager-nav { display: flex; align-items: center; gap: 8px; } .os-posts__pager-perpage { display: inline-flex; align-items: center; gap: 6px; color: var( --os-ui-fg-muted, #50575e ); font-size: 12px; margin-inline-start: 8px; } .os-posts__pager-perpage select { padding: 4px 6px; border: 1px solid var( --os-ui-border, #c3c4c7 ); border-radius: 4px; background: var( --os-ui-surface, #fff ); color: var( --os-ui-fg, #1d2327 ); font: inherit; } /* Title-bar ⋯ menu — "Show columns" sub-section. * Section label is a small caps separator above the column toggles. * The toggles are real nodes, * so they pick up the menu's existing styling automatically. */ .os-posts-window__menu-columns { margin-top: 4px; padding: 6px 12px 2px; font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var( --os-ui-fg-muted, #50575e ); border-top: 1px solid var( --os-ui-border, #dcdcde ); pointer-events: none; } /* Filter cell uses the framework's `` — no * post-specific styles needed here. The component lives inside the * and renders its popover into so the dropdown escapes * the table's overflow chain. */ /* --------------------------------------------------------------- * Categories + Tags tabs. * * Reimagined replacement for legacy `edit-tags.php`. Layout (top to * bottom): horizontal stats strip → search + add-row toolbar → * `` → pager. The stats strip is the value-add over the * legacy view — at-a-glance counts the user previously had to hunt * for. */ /* Tabs strip — auto-height; only the active panel below grows. */ .os-posts__tabs { flex: 0 0 auto; } /* Sibling panels under the same flex column. The hidden ones are * UA-display:none via the `[hidden]` attribute; the visible one * stretches to fill the remaining height. */ .os-posts__panel { flex: 1 1 auto; min-height: 0; min-width: 0; display: flex; flex-direction: column; } .os-posts__panel[ hidden ] { display: none; } .os-posts__terms-host, .os-posts__terms { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; min-width: 0; } .os-posts__stats { display: grid; grid-template-columns: repeat( auto-fit, minmax( 140px, 1fr ) ); gap: 8px; padding: 12px 14px 0; } .os-posts__stat { display: flex; flex-direction: column; gap: 2px; padding: 10px 14px; background: var( --os-ui-surface-elevated, #f6f7f7 ); border: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) ); border-radius: 8px; } .os-posts__stat[ data-tone='accent' ] { border-color: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 45%, transparent ); } .os-posts__stat[ data-tone='warning' ] { border-color: color-mix( in srgb, var( --os-ui-danger, #d63638 ) 45%, transparent ); } .os-posts__stat-value { font-size: 18px; font-weight: 700; color: var( --os-ui-fg, #1d2327 ); line-height: 1.2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .os-posts__stat[ data-tone='accent' ] .os-posts__stat-value { color: var( --wp-admin-theme-color, #2271b1 ); } .os-posts__stat[ data-tone='warning' ] .os-posts__stat-value { color: var( --os-ui-danger, #d63638 ); } .os-posts__stat-label { font-size: 11px; font-weight: 500; letter-spacing: 0.04em; text-transform: uppercase; color: var( --os-ui-fg-muted, #50575e ); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .os-posts__terms-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; padding: 12px 14px; border-bottom: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) ); } .os-posts__terms-search { flex: 1 1 220px; min-width: 0; } .os-posts__terms-search os-text-field { width: 100%; } .os-posts__terms-add { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; min-width: 0; justify-content: flex-end; } .os-posts__terms-add os-text-field { flex: 1 1 200px; min-width: 0; } .os-posts__terms-parent { padding: 6px 8px; border: 1px solid var( --os-ui-border, #c3c4c7 ); border-radius: 4px; background: var( --os-ui-surface, #fff ); color: var( --os-ui-fg, #1d2327 ); font: inherit; font-size: 13px; max-width: 180px; } .os-posts__terms os-table { flex: 1 1 auto; min-width: 0; min-height: 0; margin: 0 14px; --os-ui-table-max-height: 100%; } .os-posts__term-name { display: inline-flex; align-items: center; gap: 6px; } .os-posts__term-indent { display: inline-block; flex-shrink: 0; } .os-posts__term-tee { color: var( --os-ui-fg-muted, #8c8f94 ); font-size: 13px; flex-shrink: 0; } .os-posts__term-label { font-weight: 500; color: var( --os-ui-fg, #1d2327 ); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .os-posts__term-slug { display: inline-block; padding: 2px 6px; border-radius: 4px; background: var( --os-ui-hover, rgba( 0, 0, 0, 0.05 ) ); color: var( --os-ui-fg-muted, #50575e ); font-family: var( --os-ui-font-mono, ui-monospace, "SF Mono", Menlo, monospace ); font-size: 11px; } .os-posts__term-count { display: inline-flex; align-items: center; gap: 8px; min-width: 0; } .os-posts__term-bar { flex: 1 1 auto; height: 4px; min-width: 24px; background: var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) ); border-radius: 999px; position: relative; overflow: hidden; } .os-posts__term-bar::before { content: ''; position: absolute; inset-inline-start: 0; top: 0; bottom: 0; width: var( --os-ui-term-bar, 0% ); background: var( --wp-admin-theme-color, #2271b1 ); border-radius: 999px; transition: width 0.2s ease; } .os-posts__term-count-num { font-variant-numeric: tabular-nums; font-weight: 600; color: var( --os-ui-fg, #1d2327 ); min-width: 2ch; text-align: end; } .os-posts__term-desc { color: var( --os-ui-fg-muted, #50575e ); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-block; max-width: 100%; } .os-posts__term-desc.is-empty { color: var( --os-ui-fg-faint, #c3c4c7 ); font-style: italic; } .os-posts__term-actions { display: inline-flex; gap: 4px; } .os-posts__term-action { appearance: none; padding: 3px 8px; font: inherit; font-size: 11px; font-weight: 500; border: 1px solid transparent; border-radius: 4px; background: transparent; color: var( --wp-admin-theme-color, #2271b1 ); cursor: pointer; transition: background-color 0.12s ease, border-color 0.12s ease; } .os-posts__term-action:hover { background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 10%, transparent ); border-color: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 35%, transparent ); } .os-posts__term-action--danger { color: var( --os-ui-danger, #d63638 ); } .os-posts__term-action--danger:hover { background: color-mix( in srgb, var( --os-ui-danger, #d63638 ) 10%, transparent ); border-color: color-mix( in srgb, var( --os-ui-danger, #d63638 ) 35%, transparent ); } /* --------------------------------------------------------------- * Categories tab — Pixi mindmap (the only Categories view). * * Hybrid render: Pixi canvas paints circles + edges, an HTML overlay * paints labels + count badges + the inline editor + post mini-cards. * The overlay keeps text crisp at any zoom; the canvas keeps the * graph hardware-accelerated. */ .os-mindmap { flex: 1 1 auto; min-height: 0; min-width: 0; display: flex; flex-direction: column; /* Mindmap canvas — dot grid + theme-tinted vignettes. The dot * grid is the canonical "infinite thinking surface" texture * (Miro / Obsidian Canvas / Excalidraw all use it); pairs nicely * with radial trees because the dots give the user a sense of * pan/zoom motion without dominating the node colors. The two * offset vignettes add light, atmospheric depth — a touch of * ambient lighting in the admin theme hue. */ background: radial-gradient( circle, rgba( 0, 0, 0, 0.06 ) 1px, transparent 1.5px ) 0 0 / 24px 24px, radial-gradient( ellipse at 28% 22%, color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 8%, transparent ), transparent 60% ), radial-gradient( ellipse at 78% 80%, color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 5%, transparent ), transparent 58% ), var( --os-ui-surface, #fff ); } .os-mindmap__toolbar { display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-bottom: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) ); background: rgba( 255, 255, 255, 0.6 ); backdrop-filter: blur( 8px ); /* New stacking context so the search dropdown (z-index inside) * paints above the pixi canvas in the layout sibling below. Without * this, the canvas (later in DOM, no z-index) wins the paint order * and the dropdown renders BEHIND the chips/nodes — clicks then land * on the canvas instead of the dropdown items. */ position: relative; z-index: 2; } .os-mindmap__btn { appearance: none; display: inline-flex; align-items: center; gap: 4px; padding: 6px 10px; font: inherit; font-size: 12px; font-weight: 500; border: 1px solid var( --os-ui-border, #c3c4c7 ); border-radius: 6px; background: transparent; color: var( --os-ui-fg, #1d2327 ); cursor: pointer; transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease; } .os-mindmap__btn:hover { background: var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) ); border-color: var( --wp-admin-theme-color, #2271b1 ); } .os-mindmap__btn--primary { background: var( --wp-admin-theme-color, #2271b1 ); color: var( --os-ui-fg-on-accent, #fff ); border-color: transparent; } .os-mindmap__btn--primary:hover { filter: brightness( 1.05 ); background: var( --wp-admin-theme-color, #2271b1 ); color: var( --os-ui-fg-on-accent, #fff ); } .os-mindmap__btn--secondary { background: var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) ); } .os-mindmap__btn--danger { color: var( --os-ui-danger, #d63638 ); border-color: transparent; } .os-mindmap__btn--danger:hover { background: color-mix( in srgb, var( --os-ui-danger, #d63638 ) 10%, transparent ); border-color: color-mix( in srgb, var( --os-ui-danger, #d63638 ) 35%, transparent ); } .os-mindmap__btn--danger.is-armed { background: var( --os-ui-danger, #d63638 ); color: var( --os-ui-fg-on-accent, #fff ); border-color: transparent; } .os-mindmap__hint { flex: 1 1 auto; font-size: 12px; font-style: italic; color: var( --os-ui-fg-muted, #50575e ); text-align: end; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .os-mindmap__search { position: relative; flex: 0 1 280px; min-width: 180px; } .os-mindmap__search-input { width: 100%; padding: 6px 10px; border: 1px solid var( --os-ui-border, #d8dde4 ); border-radius: 6px; background: var( --os-ui-surface, #fff ); color: inherit; font: inherit; font-size: 13px; } .os-mindmap__search-input:focus { outline: none; border-color: var( --os-ui-accent, var( --wp-admin-theme-color, #2271b1 ) ); box-shadow: 0 0 0 3px var( --os-ui-accent-soft, rgba( 34, 113, 177, 0.18 ) ); } .os-mindmap__search-results { position: absolute; top: calc( 100% + 4px ); inset-inline-start: 0; inset-inline-end: 0; margin: 0; padding: 4px; list-style: none; background: var( --os-ui-surface-elevated, #fff ); border: 1px solid var( --os-ui-border, #d8dde4 ); border-radius: 8px; box-shadow: 0 6px 18px rgba( 17, 24, 39, 0.08 ); z-index: 100; max-height: 300px; overflow-y: auto; } .os-mindmap__search-result { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; padding: 6px 10px; background: transparent; border: 0; border-radius: 6px; color: inherit; cursor: pointer; text-align: start; font: inherit; font-size: 13px; } .os-mindmap__search-result:hover, .os-mindmap__search-result.is-active { background: var( --os-ui-hover, rgba( 79, 139, 243, 0.08 ) ); } .os-mindmap__search-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .os-mindmap__search-meta { font-size: 11px; color: var( --os-ui-fg-muted, #6b7280 ); white-space: nowrap; } /* Two-column layout: Pixi stage on the left, fixed sidebar on the * right. The sidebar always renders — empty-state copy when no node * is focused, full editor form otherwise — so the editing surface * never covers the post nodes. */ .os-mindmap__layout { display: flex; flex: 1 1 auto; min-height: 0; min-width: 0; } .os-mindmap__stage { position: relative; flex: 1 1 auto; min-height: 0; min-width: 0; overflow: hidden; /* Fade in once the very first `fitToView()` has run — without * this the canvas briefly paints its tree at the unfitted * default transform on the frame between mount and the first * rAF, which the user saw as a flash. */ transition: opacity 200ms ease; } .os-mindmap__stage.is-loading { opacity: 0; } .os-mindmap__sidebar { flex: 0 0 320px; display: flex; flex-direction: column; gap: 12px; padding: 16px; border-inline-start: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) ); background: var( --os-ui-surface, #fff ); color: var( --os-ui-fg, #1d2327 ); overflow-y: auto; min-height: 0; } .os-mindmap__sidebar-empty { flex: 1 1 auto; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; color: var( --os-ui-fg-muted, #50575e ); text-align: center; padding: 24px 8px; } .os-mindmap__sidebar-empty .dashicons { font-size: 36px; width: 36px; height: 36px; opacity: 0.5; } .os-mindmap__sidebar-empty-title { font-size: 13px; font-weight: 600; color: var( --os-ui-fg, #1d2327 ); } .os-mindmap__sidebar-empty-hint { font-size: 12px; font-style: italic; max-width: 220px; } .os-mindmap__sidebar-header { display: flex; align-items: center; gap: 8px; font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var( --os-ui-fg-muted, #50575e ); } .os-mindmap__sidebar-dot { width: 10px; height: 10px; border-radius: 50%; flex: 0 0 10px; } .os-mindmap__sidebar-slug { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .os-mindmap__sidebar-label { font-size: 11px; font-weight: 500; color: var( --os-ui-fg-muted, #50575e ); margin-block-start: 4px; } .os-mindmap__sidebar-meta { font-size: 12px; color: var( --os-ui-fg-muted, #50575e ); padding-block: 4px; } .os-mindmap__canvas { display: block; position: absolute; inset: 0; width: 100%; height: 100%; touch-action: none; } /* Spotlight: dim the world container while a node is deployed so * the focused branch reads as the foreground. Implemented as a * filter on the canvas itself because category and post chips are * Pixi nodes inside the canvas — there is no longer an HTML overlay * we can selectively dim. The canvas keeps full alpha so the * focused-node + post fan stay vivid; only the discs/chips outside * the focus area look muted via the slight desaturation in JS * (drawEdges + chip-color logic dim non-focused branches). */ /* Editor lives in the sidebar — no longer a floating popover, so no * positioning, shadow or backdrop. Just a vertical stack of inputs + * actions. (Kept the class so the form's children still hit the * existing input/button styles below.) */ .os-mindmap__editor { display: contents; } .os-mindmap__editor-name { appearance: none; font: inherit; font-size: 14px; font-weight: 600; padding: 6px 8px; border: 1px solid var( --os-ui-border, #c3c4c7 ); border-radius: 6px; background: var( --os-ui-surface, #fff ); color: inherit; } .os-mindmap__editor-name:focus, .os-mindmap__editor-desc:focus { outline: none; border-color: var( --wp-admin-theme-color, #2271b1 ); box-shadow: 0 0 0 2px color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 25%, transparent ); } .os-mindmap__editor-desc { appearance: none; font: inherit; font-size: 12px; padding: 6px 8px; border: 1px solid var( --os-ui-border, #c3c4c7 ); border-radius: 6px; background: var( --os-ui-surface, #fff ); color: inherit; resize: vertical; min-height: 46px; } .os-mindmap__editor-actions { display: flex; gap: 6px; flex-wrap: wrap; } .os-mindmap__editor-actions .os-mindmap__btn { flex: 1 1 auto; justify-content: center; } .os-mindmap__empty { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; pointer-events: none; color: var( --os-ui-fg-muted, #50575e ); font-size: 13px; font-style: italic; text-align: center; padding: 0 24px; } /* --------------------------------------------------------------------- * Tag cloud (Tags tab) * * Same surface shape as the Categories mindmap — toolbar across the * top, Pixi stage on the left, fixed-width sidebar editor on the right * — but with a hashtag-pill metaphor instead of circles. The Pixi-side * code (`tags-cloud.ts`) paints chips, post satellites, and the pager * directly into the canvas; this stylesheet only owns the chrome * around the canvas (toolbar, sidebar form, empty states). * ------------------------------------------------------------------ */ .os-tagcloud { flex: 1 1 auto; min-height: 0; min-width: 0; display: flex; flex-direction: column; background: repeating-linear-gradient( 135deg, transparent 0, transparent 22px, color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 4%, transparent ) 22px, color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 4%, transparent ) 23px ), radial-gradient( circle at 30% 20%, color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 6%, transparent ), transparent 65% ), var( --os-ui-surface, #fff ); } .os-tagcloud__toolbar { display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-bottom: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) ); background: rgba( 255, 255, 255, 0.6 ); backdrop-filter: blur( 8px ); /* Stacking context for the search dropdown — see the matching * comment on `.os-mindmap__toolbar`. */ position: relative; z-index: 2; } .os-tagcloud__btn { appearance: none; display: inline-flex; align-items: center; gap: 4px; padding: 6px 10px; font: inherit; font-size: 12px; font-weight: 500; border: 1px solid var( --os-ui-border, #c3c4c7 ); border-radius: 6px; background: transparent; color: var( --os-ui-fg, #1d2327 ); cursor: pointer; transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease; } .os-tagcloud__btn:hover { background: var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) ); border-color: var( --wp-admin-theme-color, #2271b1 ); } .os-tagcloud__btn--primary { background: var( --wp-admin-theme-color, #2271b1 ); color: var( --os-ui-fg-on-accent, #fff ); border-color: transparent; } .os-tagcloud__btn--primary:hover { filter: brightness( 1.05 ); background: var( --wp-admin-theme-color, #2271b1 ); color: var( --os-ui-fg-on-accent, #fff ); } .os-tagcloud__btn--danger { color: var( --os-ui-danger, #d63638 ); border-color: transparent; } .os-tagcloud__btn--danger:hover { background: color-mix( in srgb, var( --os-ui-danger, #d63638 ) 10%, transparent ); border-color: color-mix( in srgb, var( --os-ui-danger, #d63638 ) 35%, transparent ); } .os-tagcloud__btn--danger.is-armed { background: var( --os-ui-danger, #d63638 ); color: var( --os-ui-fg-on-accent, #fff ); border-color: transparent; } .os-tagcloud__hint { flex: 1 1 auto; font-size: 12px; font-style: italic; color: var( --os-ui-fg-muted, #50575e ); text-align: end; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .os-tagcloud__search { position: relative; flex: 0 1 280px; min-width: 180px; } .os-tagcloud__search-input { width: 100%; padding: 6px 10px; border: 1px solid var( --os-ui-border, #d8dde4 ); border-radius: 6px; background: var( --os-ui-surface, #fff ); color: inherit; font: inherit; font-size: 13px; } .os-tagcloud__search-input:focus { outline: none; border-color: var( --os-ui-accent, var( --wp-admin-theme-color, #2271b1 ) ); box-shadow: 0 0 0 3px var( --os-ui-accent-soft, rgba( 34, 113, 177, 0.18 ) ); } .os-tagcloud__search-results { position: absolute; top: calc( 100% + 4px ); inset-inline-start: 0; inset-inline-end: 0; margin: 0; padding: 4px; list-style: none; background: var( --os-ui-surface-elevated, #fff ); border: 1px solid var( --os-ui-border, #d8dde4 ); border-radius: 8px; box-shadow: 0 6px 18px rgba( 17, 24, 39, 0.08 ); z-index: 100; max-height: 300px; overflow-y: auto; } .os-tagcloud__search-result { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; padding: 6px 10px; background: transparent; border: 0; border-radius: 6px; color: inherit; cursor: pointer; text-align: start; font: inherit; font-size: 13px; } .os-tagcloud__search-result:hover, .os-tagcloud__search-result.is-active { background: var( --os-ui-hover, rgba( 79, 139, 243, 0.08 ) ); } .os-tagcloud__search-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .os-tagcloud__search-meta { font-size: 11px; color: var( --os-ui-fg-muted, #6b7280 ); white-space: nowrap; } .os-tagcloud__layout { display: flex; flex: 1 1 auto; min-height: 0; min-width: 0; } .os-tagcloud__stage { position: relative; flex: 1 1 auto; min-height: 0; min-width: 0; overflow: hidden; transition: opacity 200ms ease; } .os-tagcloud__stage.is-loading { opacity: 0; } .os-tagcloud__sidebar { flex: 0 0 320px; display: flex; flex-direction: column; gap: 12px; padding: 16px; border-inline-start: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) ); background: var( --os-ui-surface, #fff ); color: var( --os-ui-fg, #1d2327 ); overflow-y: auto; min-height: 0; } .os-tagcloud__sidebar-empty { flex: 1 1 auto; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; color: var( --os-ui-fg-muted, #50575e ); text-align: center; padding: 24px 8px; } .os-tagcloud__sidebar-empty .dashicons { font-size: 36px; width: 36px; height: 36px; opacity: 0.5; } .os-tagcloud__sidebar-empty-title { font-size: 13px; font-weight: 600; color: var( --os-ui-fg, #1d2327 ); margin: 0; } .os-tagcloud__sidebar-empty-hint { font-size: 12px; font-style: italic; max-width: 240px; margin: 0; } .os-tagcloud__sidebar-header { display: flex; align-items: center; gap: 8px; font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var( --os-ui-fg-muted, #50575e ); } .os-tagcloud__sidebar-dot { width: 10px; height: 10px; border-radius: 50%; flex: 0 0 10px; } .os-tagcloud__sidebar-slug { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .os-tagcloud__sidebar-label { font-size: 11px; font-weight: 500; color: var( --os-ui-fg-muted, #50575e ); margin-block-start: 4px; } .os-tagcloud__sidebar-meta { font-size: 12px; color: var( --os-ui-fg-muted, #50575e ); padding-block: 4px; } .os-tagcloud__canvas { display: block; position: absolute; inset: 0; width: 100%; height: 100%; touch-action: none; } .os-tagcloud__editor-name { appearance: none; font: inherit; font-size: 14px; font-weight: 600; padding: 6px 8px; border: 1px solid var( --os-ui-border, #c3c4c7 ); border-radius: 6px; background: var( --os-ui-surface, #fff ); color: inherit; } .os-tagcloud__editor-name:focus, .os-tagcloud__editor-desc:focus { outline: none; border-color: var( --wp-admin-theme-color, #2271b1 ); box-shadow: 0 0 0 2px color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 25%, transparent ); } .os-tagcloud__editor-desc { appearance: none; font: inherit; font-size: 12px; padding: 6px 8px; border: 1px solid var( --os-ui-border, #c3c4c7 ); border-radius: 6px; background: var( --os-ui-surface, #fff ); color: inherit; resize: vertical; min-height: 46px; } .os-tagcloud__editor-actions { display: flex; gap: 6px; flex-wrap: wrap; } .os-tagcloud__editor-actions .os-tagcloud__btn { flex: 1 1 auto; justify-content: center; } .os-tagcloud__empty { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; pointer-events: none; color: var( --os-ui-fg-muted, #50575e ); font-size: 13px; font-style: italic; text-align: center; padding: 0 24px; } /* ---------------------------------------------------------------- */ /* Posts-window intro dialog (first-open Pixi preview) */ /* ---------------------------------------------------------------- */ .os-intro-backdrop { position: fixed; inset: 0; background: rgba( 15, 23, 42, 0.55 ); backdrop-filter: blur( 6px ); display: flex; align-items: center; justify-content: center; z-index: 100000; animation: os-intro-fade 180ms ease-out; } .os-intro { width: min( 720px, calc( 100vw - 48px ) ); max-height: calc( 100vh - 48px ); background: var( --os-ui-surface, #fff ); color: var( --os-ui-fg, #1d2327 ); border-radius: 14px; box-shadow: 0 20px 60px rgba( 0, 0, 0, 0.35 ); padding: 28px 28px 22px; display: flex; flex-direction: column; gap: 12px; outline: none; animation: os-intro-pop 220ms cubic-bezier( 0.2, 0.8, 0.2, 1.05 ); overflow: hidden; } .os-intro__title { margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -0.01em; } .os-intro__lede { margin: 0; font-size: 14px; line-height: 1.5; color: var( --os-ui-fg, #1d2327 ); } .os-intro__stage { position: relative; height: 550px; border-radius: 10px; overflow: hidden; background: radial-gradient( circle at 30% 30%, rgba( 124, 58, 237, 0.10 ), transparent 55% ), radial-gradient( circle at 70% 70%, rgba( 34, 113, 177, 0.10 ), transparent 55% ), linear-gradient( 180deg, #f8fafc, #eef2f7 ); border: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) ); } .os-intro__canvas { display: block; width: 100% !important; height: 100% !important; } .os-intro__fallback { margin: 0; padding: 40px 24px; font-size: 14px; line-height: 1.5; text-align: center; color: var( --os-ui-fg-muted, #50575e ); } .os-intro__tip { margin: 0; font-size: 12px; color: var( --os-ui-fg-muted, #50575e ); font-style: italic; } .os-intro__escape { margin: 0; font-size: 13px; line-height: 1.5; padding: 10px 12px; border-radius: 8px; background: rgba( 34, 113, 177, 0.07 ); color: var( --os-ui-fg, #1d2327 ); } .os-intro__actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; } .os-intro__btn { appearance: none; font: inherit; font-size: 13px; font-weight: 500; padding: 8px 14px; border-radius: 8px; border: 1px solid transparent; cursor: pointer; transition: background-color 0.12s ease, border-color 0.12s ease, filter 0.12s ease; } .os-intro__btn--secondary { background: var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) ); color: var( --os-ui-fg, #1d2327 ); border-color: var( --os-ui-border, rgba( 0, 0, 0, 0.10 ) ); } .os-intro__btn--secondary:hover { background: var( --os-ui-hover, rgba( 0, 0, 0, 0.07 ) ); } .os-intro__btn--primary { background: var( --wp-admin-theme-color, #2271b1 ); color: var( --os-ui-fg-on-accent, #fff ); } .os-intro__btn--primary:hover { filter: brightness( 1.05 ); } @keyframes os-intro-fade { from { opacity: 0; } to { opacity: 1; } } @keyframes os-intro-pop { from { opacity: 0; transform: translateY( 8px ) scale( 0.98 ); } to { opacity: 1; transform: translateY( 0 ) scale( 1 ); } } /* ─── Native Users window — tabs + Add User panel ────────────────── */ /* * Lives in the shared posts-window stylesheet because the Users * window enqueues `os-posts-window` (single bundle, single * style handle covers both). Scoped to `.desktop-mode-users` so it * never bleeds into the Posts/Pages markup. */ /* * Tab strip — keep it auto-height (matches the * `.os-posts__tabs` rule). Without the `flex: 0 0 auto` * the os-tabs element grows to fill the column and the panel * below collapses to zero height — exactly the "no table" * regression the first cut hit. */ .os-users__tabs { flex: 0 0 auto; padding: 0 18px; border-bottom: 1px solid var( --os-ui-border, #dcdcde ); } /* * The Add User tab. The PHP template applies this class to the * ``. Padding lives on the host so the form's own * footer separator sits flush with the panel edges. `flex: 1 1 * auto` matches the All-users panel so the active tab fills the * remaining height and the inactive one is fully `hidden`. */ .os-users__add-panel { flex: 1 1 auto; min-height: 0; display: block; padding: 32px 24px 28px; box-sizing: border-box; overflow-y: auto; } .os-users__add-panel[ hidden ] { display: none; } /* * Center the form at a comfortable reading width. The os-form * itself does the column layout — we only do the OUTER centering * here. `min(760px, 100%)` keeps it from blowing past the panel's * edges on narrow windows where the max-width would be moot. */ .os-users__add-panel > os-form { display: block; margin-inline: auto; width: min( 760px, 100% ); } .os-users__add-panel [ slot="header" ] { margin-bottom: 4px; } .os-users__add-panel [ slot="header" ] h2 { margin: 0 0 6px; font-size: 20px; font-weight: 600; letter-spacing: -0.01em; color: var( --os-ui-fg, #1d2327 ); } .os-users__form-lede { margin: 0 0 8px; color: var( --os-ui-fg-muted, #50575e ); font-size: 13px; line-height: 1.5; } .os-users__form-pwd-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: -4px; } .os-users__form-hint { margin: 0; font-size: 12px; color: var( --os-ui-fg-muted, #50575e ); } /* * Narrow-window trim — at < 540px the side padding gets in the way * more than it helps. Same breakpoint the form's own grid uses to * collapse to one column, so the visual cadence stays consistent. */ @media ( max-width: 540px ) { .os-users__add-panel { padding: 20px 14px 16px; } .os-users__tabs { padding: 0 12px; } } /* * Profile tab — third tab in the Users window. Same flex * stretch as the .os-posts__panel rule: the active * tabpanel fills the remaining height, scrolls its own content, * leaves the tab strip pinned at the top. */ .os-users__edit-panel { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow: hidden; } .os-users__edit-panel[ hidden ] { display: none; } /* * `` slotted as a tabpanel child needs to fill * the height itself so its internal scroll container takes the * remaining space. Without this the layout grid lives at its * intrinsic height and the user can't reach the bottom of the * form on shorter windows. */ .os-users__edit-panel > os-user-profile { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; } /* * Standalone `desktop-mode-user-edit` window. The native body is * a chromeless host; `` fills the whole space * and owns its own scroll. Without `min-height: 0` on the chain, * the inner `overflow-y: auto` on the layout grid never gets a * chance to clip and scroll. */ .os-user-edit-window { display: flex; flex-direction: column; height: 100%; min-height: 0; } .os-user-edit-window > os-user-profile { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; } /* * The component itself — light-DOM host of the layout. Same * flex semantics so the inner scroll container is the * .os-users__edit-layout div. */ os-user-profile { display: flex; flex-direction: column; min-height: 0; } os-user-profile > .os-users__edit-layout { flex: 1 1 auto; min-height: 0; } /* * Sidebar layout — at-a-glance summary on the left, editable * form + activity feed in the main column on the right. A * single CSS container query collapses the two columns to one * stack on narrow widths, where the aside moves above the form * (the user still sees their summary; just scrolls past it * before the form). The Profile form's own os-form container * query handles the inner 1↔2 column flip. */ .os-users__edit-layout { flex: 1 1 auto; min-height: 0; overflow-y: auto; display: grid; grid-template-columns: 280px minmax( 0, 1fr ); gap: 28px; padding: 28px 32px 32px; container-type: inline-size; container-name: desktop-mode-user-edit; } @container desktop-mode-user-edit ( max-width: 800px ) { .os-users__edit-layout { grid-template-columns: minmax( 0, 1fr ); } } .os-users__edit-aside { display: flex; flex-direction: column; gap: 8px; min-width: 0; position: sticky; top: 0; align-self: start; } @container desktop-mode-user-edit ( max-width: 800px ) { .os-users__edit-aside { position: static; } } .os-users__edit-main { min-width: 0; width: 100%; max-width: 720px; /* Center within the `minmax(0, 1fr)` grid cell so the form * stays readable on wide windows instead of leaving a large * empty band on the right edge. The 720px max preserves the * intended line length; the auto inline margin pushes the * unused width equally to both sides. */ margin-inline: auto; display: flex; flex-direction: column; } .os-users__edit-activity { min-width: 0; }