/*
 * The frame's layout (js/shell.js): the sidebar's three floating cards, the work area with its top bar, the avatar in
 * the top-right corner, and the phone drawer. Then a page's common parts (title, narrow column) and 设置's.
 */

.shell {
	display: flex;
	height: 100vh;
	height: 100dvh;
	overflow: hidden;
	background: var(--bg);
}

/* ---- the sidebar: three cards floating on the page ---- */

.sb {
	position: relative;
	z-index: 1; /* the cards' shadows reach over the work area */
	display: flex;
	flex: none;
	flex-direction: column;
	gap: var(--gap);
	width: var(--sb-width);
	height: 100%;
	padding: var(--gap) 0 var(--gap) var(--gap);
	background: var(--bg);
}

.sb-card {
	border: 1px solid var(--border);
	border-top-color: var(--edge-top);
	border-bottom-color: var(--edge-bottom);
	border-radius: var(--radius-card);
	background: var(--card-bg);
	box-shadow: var(--card-shadow);
}

/* two services a row, the brand across (phones first, the user 2026-10-20: the middle card gets the room; wide too
   since 2026-10-25, when 邮件 made it four) */
.sb-services {
	display: grid;
	flex: none;
	grid-template-columns: 1fr 1fr;
	gap: 6px;
	padding: 10px;
}

.sb-services > .sb-brand {
	grid-column: 1 / -1;
}

.sb-services .sb-tab {
	min-width: 0;
	gap: 8px;
	padding-inline: 10px;
}

/* 沟通 and 项目 (js/shell.js serviceGroups): what is not on the card's face now is away (the services of a group
   together, a group's 「‹ 返回」, 项目's inside); 沟通 together, the card a column, each across it. A group shown: each
   across but 日历 and 通讯录 (half each, a row at 沟通's bottom) */
.sb-services > .is-elsewhere {
	display: none;
}

.sb-services.is-together {
	grid-template-columns: 1fr;
}

.sb-services.is-open > :not(.is-half) {
	grid-column: 1 / -1;
}

/* 「‹ 返回」 (its look on its own: .text-link only darkens it under the mouse) */
.sb-fold-back {
	display: flex;
	align-items: center;
	gap: 4px;
	justify-self: start;
	padding: 2px 6px 2px 2px;
	border: 0;
	border-radius: var(--radius-sm);
	background: none;
	color: var(--muted);
	font: inherit;
	font-size: calc(13px * var(--ui-scale));
	cursor: pointer;
}

.sb-fold,
.sb-more {
	font: inherit;
	text-align: start;
	cursor: pointer;
}

/* 项目's inside: 办公 and 多媒体, each a row of small raised buttons (an icon over its name), then 最近打开的, flat rows */
.sb-proj-panel {
	display: flex;
	flex-direction: column;
	gap: 6px;
	min-width: 0;
}

.sb-proj-rows {
	display: contents;
}

.sb-proj-head {
	margin-top: 2px;
	padding-inline: 2px;
	color: var(--muted);
	font-size: calc(12px * var(--ui-scale));
}

.sb-proj-grid {
	display: grid;
	grid-template-columns: repeat(var(--sb-proj-cols, 3), minmax(0, 1fr));
	gap: 6px;
}

.sb-services .sb-proj-make {
	flex-direction: column;
	justify-content: center;
	gap: 3px;
	min-height: 52px;
	padding: 6px 2px;
	font: inherit;
	font-size: calc(12px * var(--ui-scale));
	cursor: pointer;
}

.sb-proj-name {
	min-width: 0;
	max-width: 100%;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.sb-proj-recent {
	display: flex;
	flex-direction: column;
	gap: 1px;
}

.sb-proj-item {
	display: flex;
	align-items: center;
	gap: 8px;
	min-height: 30px;
	padding: 4px 8px;
	border-radius: var(--radius-sm);
	color: var(--text);
	font-size: calc(13px * var(--ui-scale));
	text-decoration: none;
	transition: background-color var(--dur-quick);
}

.sb-proj-item > svg {
	flex: none;
	color: var(--muted);
}

.sb-proj-item > .sb-proj-name {
	flex: 1;
}

.sb-proj-item:hover {
	background: var(--surface-2);
	color: var(--text-strong);
}

.sb-proj-item:focus-visible {
	outline: 2px solid var(--border-strong);
	outline-offset: -2px;
}

.sb-proj-when {
	flex: none;
	color: var(--muted);
	font-size: calc(11px * var(--ui-scale));
}

.sb-proj-none {
	margin: 0;
	padding: 4px 8px;
	color: var(--muted);
	font-size: calc(13px * var(--ui-scale));
}

/* A short window: the middle card (the drive's tree, under this one) keeps room: 最近打开的 two rows, then none; shorter
   still, the card itself scrolls rather than run over the card at the bottom */
@media (max-height: 800px) {
	.sb-proj-recent > :nth-child(n + 3) {
		display: none;
	}
}

@media (max-height: 680px) {
	.sb-proj-recent,
	.sb-proj-panel > .sb-proj-head {
		display: none;
	}
}

.sb-services.is-proj {
	flex: 0 1 auto;
	align-content: start;
	min-height: 0;
	overflow-y: auto;
}

/* (very short: room left for the middle card and 在当前工作区搜索's, which comes in under it) */
@media (max-height: 520px) {
	.sb-services.is-proj {
		max-height: 180px;
	}
}

/* 设置 › 外观 › 服务按钮: a service put in 更多 (js/shell.js moreGroup) or not shown is not in the card */
.sb-services > .sb-tab.is-away,
.sb-services > .sb-more[hidden] {
	display: none;
}

/* one of 沟通's open while folded (or of 更多's): its button stays raised, a small dot at its right (before the unread count, if any)
   in the ink a pressed button's words have — not red, not the blue of news. Always there (no shift of the count),
   only grown in and out (the search's dot's way) */
.sb-talk-dot {
	flex: none;
	width: 6px;
	height: 6px;
	border-radius: 50%;
	background: var(--text-strong);
	transform: scale(0);
	opacity: 0;
	transition:
		transform var(--dur-quick) var(--ease-grow),
		opacity var(--dur-quick);
}

.sb-fold.is-inside .sb-talk-dot,
.sb-more.is-inside .sb-talk-dot {
	transform: none;
	opacity: 1;
}

/* a service, 设置: each its own raised bar on its card, pressed in while it is open */
.sb-tab {
	display: flex;
	align-items: center;
	gap: 10px;
	min-height: 38px;
	padding: 6px 12px;
	border: 1px solid var(--border);
	border-top-color: var(--edge-top);
	border-bottom-color: var(--edge-bottom);
	border-radius: var(--radius-sm);
	background: var(--face), var(--sb-btn-bg);
	box-shadow: var(--edge-light), var(--edge-dark), var(--elev-1);
	color: var(--text);
	text-decoration: none;
	-webkit-user-drag: none;
	transition:
		translate 220ms var(--ease-grow),
		box-shadow var(--dur-quick),
		background-color var(--dur-quick),
		border-color var(--dur-quick);
}

.sb-tab svg {
	flex: none;
	color: var(--muted);
}

.sb-tab-name {
	flex: 1;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.sb-tab:hover {
	background: var(--face), var(--sb-btn-hover);
	color: var(--text-strong);
}

.sb-tab:active,
.sb-tab.is-current {
	border-top-color: var(--edge-bottom);
	border-bottom-color: var(--edge-top);
	background: var(--sb-btn-sunk);
	box-shadow: var(--btn-pressed);
	color: var(--text-strong);
}

.sb-tab.is-current {
	font-weight: 600;
}

.sb-tab.is-current svg {
	color: var(--text-strong);
}

.sb-tab:active {
	translate: 0 1px;
	transition-duration: 80ms;
}

.sb-tab:focus-visible {
	outline: 2px solid var(--border-strong);
	outline-offset: 2px;
}

/* a count beside a service (unread messages), at the button's right */
.sb-tab .sb-badge {
	min-width: 18px;
	height: 18px;
	padding: 0 6px;
	border-radius: 999px;
	background: var(--danger);
	color: var(--on-fill);
	font-size: calc(11px * var(--ui-scale));
	font-weight: 600;
	line-height: 18px;
	text-align: center;
	font-variant-numeric: tabular-nums;
}

.sb-tab .sb-badge[hidden] {
	display: none;
}

/* between the first two cards: what a service keeps showing whatever page is open (shell.dock) */
.sb-dock {
	display: contents;
}

/* ---- the middle card: the module's list; a deck of two when it says so (the one behind shows its right and bottom
   edge) ---- */
.sb-deck {
	--sb-peek: 0px;
	position: relative;
	flex: 1;
	min-height: 0;
	margin: 0 var(--sb-peek) var(--sb-peek) 0;
	transform-origin: 50% 0; /* (js/shell.js swapCard scales it) */
}

.sb-deck.is-deck {
	--sb-peek: 7px;
}

.sb-face {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	overflow: hidden;
	transition:
		transform 0.35s var(--ease),
		filter 0.35s;
}

.sb-face.is-front {
	z-index: 2;
}

.sb-face.is-back {
	z-index: 1;
	filter: brightness(0.97);
	transform: translate(var(--sb-peek), var(--sb-peek));
}

.sb-deck:not(.is-deck) .sb-face.is-back {
	visibility: hidden;
}

.sb-face-in {
	display: flex;
	flex: 1;
	flex-direction: column;
	min-height: 0;
}

/* swapping: the front card steps aside and sinks behind; the back one swings out and comes forward */
.sb-deck.is-swapping .sb-face {
	transition: none;
}

.sb-deck.is-swapping .sb-face.is-leaving {
	animation: sb-to-back 0.5s var(--ease) both;
}

.sb-deck.is-swapping .sb-face.is-coming {
	animation: sb-to-front 0.5s var(--ease) both;
}

@keyframes sb-to-back {
	0% {
		z-index: 2;
		transform: none;
	}
	45% {
		z-index: 2;
		transform: translate(-3%, -4px) scale(0.97) rotate(-1.5deg);
	}
	55% {
		z-index: 1;
	}
	100% {
		z-index: 1;
		transform: translate(var(--sb-peek), var(--sb-peek));
	}
}

@keyframes sb-to-front {
	0% {
		z-index: 1;
		transform: translate(var(--sb-peek), var(--sb-peek));
	}
	45% {
		z-index: 1;
		transform: translate(12%, 6px) rotate(2deg);
	}
	55% {
		z-index: 3;
	}
	100% {
		z-index: 3;
		transform: none;
	}
}

/* ---- the middle card's rows, a raised tile each: the chats (Rarien), the mails (Meol), the AI chats (Fadmeo) — the
   user 2026-10-26: 邮件、ai、即时聊天的左侧选择信息的标签样式不一样, all as the chats were. An avatar, a line of a name
   and a time, a line under it. Under each tile what holds it (设置 › 外观, html[data-chat-slot]: 相角 by default,
   托板; 挂钩 and 淡卡 are only where the open chat's card left, rarien.css) only in a list with .sb-tiles--slot, the
   chats', whose card flies out to the chat's top (the user 2026-10-28: 这个角是专门搭配聊天的动画用的，没有聊天那种上标签
   不要这个角): the games, the work orders and the mails have plain tiles, as close as ever ---- */
.sb-tiles {
	display: flex;
	flex-direction: column;
	gap: 6px;
	padding: 2px 0 4px;
}

html[data-chat-slot="shelf"] .sb-tiles--slot {
	gap: 12px;
}

.sb-tile-row {
	position: relative;
}

.sb-tiles--slot .sb-tile-row {
	--slot-face: color-mix(in srgb, var(--muted) 40%, var(--sb-btn-bg));
	--slot-lit: color-mix(in srgb, var(--muted) 15%, var(--sb-btn-bg));
	--slot-shade: rgb(0 0 0 / 0.3);
	--slot-soft: rgb(0 0 0 / 0.16); /* (相角's: what is left of the shade under a 1 px blur) */
}

.sb-tile {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 7px 8px;
	border: 1px solid var(--border);
	border-top-color: var(--edge-top);
	border-bottom-color: var(--edge-bottom);
	border-radius: var(--radius);
	background: var(--face), var(--sb-btn-bg);
	box-shadow: var(--edge-light), var(--edge-dark), var(--elev-1);
	color: var(--text);
	text-decoration: none;
	-webkit-user-drag: none;
	transition:
		box-shadow var(--dur-quick),
		background-color var(--dur-quick);
}

.sb-tile:hover {
	background: var(--face), var(--sb-btn-hover);
}

/* the open one, where it stays in its place (a chat's flies out, rarien.css .is-slot): pressed in, as a service's
   button is */
.sb-tile.is-current:not(.is-slot) {
	border-top-color: var(--edge-bottom);
	border-bottom-color: var(--edge-top);
	background: var(--sb-btn-sunk);
	box-shadow: var(--btn-pressed);
	color: var(--text-strong);
}

.sb-tiles--slot .sb-tile-row::after {
	position: absolute;
	content: "";
	pointer-events: none;
}

/* 相角: two photo corners holding each tile at its bottom, over it (the user 2026-10-25: 上面两个三角形不要了，只留下
   下面的，然后让相角放在聊天卡上面). Their shadow is the same corners under them, half a pixel right and one down, soft
   along the long edge — not a drop-shadow filter: one on every row took WebKit 150 ms to draw the list again with 40
   chats (2026-10-26); the box reaches the row's right and bottom edges for it */
html:not([data-chat-slot]) .sb-tiles--slot .sb-tile-row::after {
	inset: 1px 0 0 1px;
	background:
		linear-gradient(45deg, var(--slot-lit), var(--slot-face) 50%, transparent 50%) left 0 bottom 1px / 15px 15px no-repeat,
		linear-gradient(315deg, var(--slot-face) 50%, transparent 50%) right 1px bottom 1px / 15px 15px no-repeat,
		linear-gradient(45deg, var(--slot-soft) calc(50% - 1px), transparent calc(50% + 1.5px)) left 0.5px bottom 0 / 15px 15px no-repeat,
		linear-gradient(315deg, var(--slot-soft) calc(50% - 1px), transparent calc(50% + 1.5px)) right 0.5px bottom 0 / 15px 15px no-repeat;
}

/* 托板: a little shelf under each tile, which it stands on (the user 2026-10-25: 展示出托板) */
html[data-chat-slot="shelf"] .sb-tiles--slot .sb-tile-row {
	isolation: isolate; /* (the shelf under the tile, not under the sidebar's card) */
	padding-bottom: 6px;
}

html[data-chat-slot="shelf"] .sb-tiles--slot .sb-tile-row::after {
	right: 4px;
	bottom: 0;
	left: 4px;
	z-index: -1;
	height: 7px;
	border-radius: 3px;
	background: linear-gradient(var(--slot-lit), var(--slot-face));
	box-shadow: inset 0 1px 0 rgb(var(--shine) / 0.35), 0.5px 2px 3px var(--slot-shade);
}

.sb-tile > .sb-tile-ava {
	flex: none;
}

.sb-tile-text {
	display: flex;
	flex: 1;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
}

.sb-tile-line {
	display: flex;
	align-items: center;
	gap: 6px;
	min-width: 0;
}

.sb-tile-name,
.sb-tile-sub {
	flex: 1;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.sb-tile-name {
	font-weight: 500;
}

.sb-tile.has-unread .sb-tile-name {
	font-weight: 650;
	color: var(--text-strong);
}

.sb-tile-sub {
	color: var(--muted);
	font-size: 0.86em;
}

.sb-tile-time {
	flex: none;
	color: var(--faint);
	font-size: 0.78em;
}

/* how many not read; a dot when it is only whether */
.sb-tile-badge {
	flex: none;
	min-width: 18px;
	padding: 0 5px;
	border-radius: 9px;
	background: var(--unread);
	box-shadow: inset 0 1px 0 rgb(var(--shine) / 0.3), 0 1px 2px rgb(var(--depth-ink) / 0.2);
	color: var(--on-fill);
	font-size: 0.72em;
	font-weight: 650;
	line-height: 18px;
	text-align: center;
}

.sb-tile-badge:empty {
	min-width: 8px;
	height: 8px;
	padding: 0;
}

/* a muted one's: grey, it is not asking for attention */
.sb-tile.is-muted .sb-tile-badge {
	background: var(--faint);
}

/* ---- the bottom card: 搜索 and 设置 ---- */
.sb-pages {
	display: flex;
	flex: none;
	flex-direction: column;
	gap: 8px;
	padding: 10px;
}

.sb-search {
	display: flex;
	align-items: center;
	gap: 8px;
	margin: 0;
}

/* the box: a well of its own; the round button stands outside it, at its right (the local card's boxes too) */
.sb-search-well,
.sb-lf-well {
	display: flex;
	flex: 1;
	align-items: center;
	min-width: 0;
	padding: 3px 12px;
	border: 1px solid var(--border);
	border-top-color: var(--edge-bottom);
	border-radius: 999px;
	background: var(--surface);
	box-shadow: var(--sunken);
}

.sb-search-field,
.sb-lf-field {
	flex: 1;
	min-width: 0;
	height: 30px;
	padding: 0;
	border: 0;
	outline: 0;
	background: none;
	color: var(--text);
	font: inherit;
}

.sb-search-field::-webkit-search-cancel-button,
.sb-lf-field::-webkit-search-cancel-button {
	display: none;
}

.sb-search-well:focus-within,
.sb-lf-well:focus-within {
	border-color: var(--border-strong);
}

/* the "⋯" in the box: 统一搜索's conditions (components/search-filters.js); colored, with a dot, when not as they start */
.sb-search-more {
	position: relative;
	width: 26px;
	height: 26px;
	margin-right: -8px;
}

.sb-search-dot {
	position: absolute;
	top: 3px;
	right: 3px;
	width: 7px;
	height: 7px;
	border: 1.5px solid var(--surface);
	border-radius: 50%;
	background: var(--mark);
	transform: scale(0);
	transition: transform var(--dur-quick) var(--ease-grow);
}

.sb-search-more.is-set {
	color: var(--mark);
}

.sb-search-more.is-set .sb-search-dot {
	transform: none;
}

/* its window: rows of chips */
.sf-win {
	--win-max-w: 340px;
}

.sf {
	display: flex;
	flex-direction: column;
	gap: 12px;
	padding: 6px 6px 4px;
}

.sf-label {
	margin: 0 2px 6px;
	font-size: calc(12px * var(--ui-scale));
	color: var(--muted);
}

.sf-chips {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
}

.sf-chip {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	height: 30px;
	padding: 0 12px;
	border: 0;
	border-radius: 999px;
	background: var(--seg-groove);
	box-shadow: var(--seg-groove-shadow);
	color: var(--seg-text);
	font: inherit;
	font-size: calc(13px * var(--ui-scale));
	cursor: pointer;
	transition: background-color var(--dur-quick), box-shadow var(--dur-quick), color var(--dur-quick);
}

.sf-chip[aria-pressed="true"] {
	background: var(--face), var(--seg-thumb);
	box-shadow: var(--seg-thumb-shadow);
	color: var(--seg-text-on);
}

.sf-hint {
	margin: 0 2px;
	font-size: calc(12px * var(--ui-scale));
	color: var(--muted);
}

.sf-foot {
	display: flex;
	justify-content: flex-end;
}

/* the round button beside it: raised, sinks while pressed */
.sb-search-go {
	display: inline-flex;
	flex: none;
	align-items: center;
	justify-content: center;
	width: 38px;
	height: 38px;
	padding: 0;
	border: 1px solid var(--border);
	border-top-color: var(--edge-top);
	border-bottom-color: var(--edge-bottom);
	border-radius: 50%;
	background: var(--face), var(--sb-btn-bg);
	box-shadow: var(--edge-light), var(--edge-dark), var(--elev-1);
	color: var(--muted);
	cursor: pointer;
	transition:
		translate 220ms var(--ease-grow),
		box-shadow var(--dur-quick),
		background-color var(--dur-quick);
}

.sb-search-go:hover {
	background: var(--face), var(--sb-btn-hover);
	color: var(--text-strong);
}

.sb-search-go:active {
	background: var(--sb-btn-sunk);
	box-shadow: var(--btn-pressed);
	translate: 0 1px;
	transition-duration: 80ms;
}

/* Ctrl+F with no page finding in itself (js/shell.js searchCard): the box and its button light up blue for a moment
   (the page's own card's glow is amber: .sb-lf.is-glowing) */
.sb-search.is-glowing .sb-search-well {
	--glow-under: var(--sunken);
	animation: sb-search-glow 1.2s var(--ease-out);
}

.sb-search.is-glowing .sb-search-go {
	--glow-under: var(--edge-light), var(--edge-dark), var(--elev-1);
	animation: sb-search-glow 1.2s var(--ease-out);
}

@keyframes sb-search-glow {
	from {
		box-shadow:
			var(--glow-under),
			0 0 0 0 transparent,
			0 0 0 0 transparent;
	}
	25% {
		box-shadow:
			var(--glow-under),
			0 0 0 3px color-mix(in srgb, var(--mark) 70%, transparent),
			0 0 22px 6px color-mix(in srgb, var(--mark) 45%, transparent);
	}
	to {
		box-shadow:
			var(--glow-under),
			0 0 0 0 transparent,
			0 0 0 0 transparent;
	}
}

/* ---- 在当前工作区搜索: the card split off the bottom one (components/local-find.js) ---- */
.sb-pages {
	transform-origin: 50% 100%; /* it stretches up as the card splits off */
}

.sb-lf {
	position: relative;
	display: flex;
	flex: none;
	flex-direction: column;
	gap: 8px;
	padding: 10px;
}

/* splitting off or merging back: out of the flow where it lands (left, bottom, width: set by the script) */
.sb-lf.is-lifting {
	position: absolute;
	z-index: 1; /* under the middle card's front face (2), whose shadow falls on it as when it is in the flow */
}

/* the bottom card's search row, split off into it (form.is-local): no 统一搜索 "⋯" or round button; the magnifier,
   the count and 上一个 / 下一个 in the box */
/* the "⋯" and the round button out of the flow where they were (components/local-find.js moves them as it goes: the
   "⋯" fading with the box's end, the button up to the card's corner as its ×) */
.sb-search {
	position: relative;
}

.sb-search.is-local .sb-search-more,
.sb-search.is-local .sb-search-go {
	position: absolute;
	opacity: 0;
	visibility: hidden;
	pointer-events: none;
}

.sb-search.is-local .sb-search-more {
	right: 12px;
}

.sb-search.is-local .sb-search-go {
	right: 0;
}

.sb-search.is-local .sb-search-well {
	gap: 6px;
	color: var(--faint); /* its magnifier */
}

.sb-search-well {
	position: relative;
}

.sb-lf-steps {
	display: inline-flex;
	flex: none;
	align-items: center;
	gap: 6px;
}

/* the round button's × as it flies (over its magnifier) */
.sb-search-go {
	position: relative;
}

.sb-lf-x {
	position: absolute;
	inset: 0;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	opacity: 0;
}

/* the box's round end while it grows into the button's place (the box clipped short of it): its edge, a ring */
.sb-lf-cap {
	position: absolute;
	border: 1px solid var(--border);
	border-top-color: var(--edge-bottom);
	border-radius: 999px;
	pointer-events: none;
}

.sb-search-well:focus-within ~ .sb-lf-cap {
	border-color: var(--border-strong);
}

.sb-lf-mag {
	display: inline-flex;
	flex: none;
}

/* splitting off or merging back (components/local-find.js split): the card and the bottom card draw only what is in
   them, the two drawn as one shape behind them (.sb-lf-cell) */
.sb-card.is-cell {
	border-color: transparent;
	background: transparent;
	box-shadow: none;
}

/* the two as one cell, drawn as a card is (.sb-card): its face and shadow, the lit and dark lines inside its edge, its
   edge lighter on top and darker below */
.sb-lf-cell {
	position: absolute;
	inset: 0;
	z-index: 0;
	width: 100%;
	height: 100%;
	overflow: visible;
	pointer-events: none;
}

.sb-lf-cell .is-face {
	fill: var(--card-bg);
	filter: var(--elev-2-drop);
}

.sb-lf-cell .is-lit,
.sb-lf-cell .is-dim {
	fill-rule: evenodd;
}

.sb-lf-cell .is-lit {
	fill: var(--lit-line);
}

.sb-lf-cell .is-dim {
	fill: var(--dim-line);
}

.sb-lf-cell .is-edge,
.sb-lf-cell .is-edge-up,
.sb-lf-cell .is-edge-down {
	fill: none;
	stroke: var(--border);
	stroke-width: 1;
}

.sb-lf-cell .is-edge-up {
	stroke: var(--edge-top);
}

.sb-lf-cell .is-edge-down {
	stroke: var(--edge-bottom);
}

/* the drop from where they part, falling into the bottom card */
.sb-lf-drop {
	position: absolute;
	border-radius: 50%;
	background: var(--card-bg);
	box-shadow: var(--elev-1);
	pointer-events: none;
}

.sb-pages.is-parting {
	position: relative; /* over the card's shape (.sb-lf-cell) */
	z-index: 1;
}

/* a card's frame behind it while an edge of it moves (components/local-find.js: the middle card's bottom) */
.sb-lf-ghost {
	position: absolute;
	margin: 0;
	pointer-events: none;
}

/* where the row was in the bottom card while it moves */
.sb-lf-spacer {
	flex: none;
}

.sb-lf-row {
	display: flex;
	align-items: center;
	gap: 4px;
}

.sb-lf-well {
	gap: 6px;
	color: var(--faint); /* its magnifier */
}

.sb-lf-row > .btn {
	flex: none;
}

/* 上一个 / 下一个: flat, in the box */
.sb-lf-step {
	display: inline-flex;
	flex: none;
	align-items: center;
	justify-content: center;
	width: 24px;
	height: 24px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: none;
	color: var(--muted);
	cursor: pointer;
}

.sb-lf-step:last-child {
	margin-right: -8px;
}

.sb-lf-step:hover:not(:disabled) {
	background: var(--surface-2);
	color: var(--text-strong);
}

.sb-lf-step:disabled {
	opacity: 0.4;
	cursor: default;
}

/* ×: a small raised bubble on the card's top right corner */
.sb-lf-close {
	position: absolute;
	top: -7px;
	right: -7px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 22px;
	height: 22px;
	padding: 0;
	border: 1px solid var(--border);
	border-top-color: var(--edge-top);
	border-bottom-color: var(--edge-bottom);
	border-radius: 50%;
	background: var(--face), var(--sb-btn-bg);
	box-shadow: var(--edge-light), var(--edge-dark), var(--elev-1);
	color: var(--muted);
	cursor: pointer;
}

.sb-lf-close:hover {
	background: var(--face), var(--sb-btn-hover);
	color: var(--text-strong);
}

.sb-lf-count {
	flex: none;
	padding-left: 6px;
	color: var(--faint);
	font-size: 0.8em;
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}

.sb-lf-scope {
	margin: 0;
	padding: 0 4px;
	color: var(--muted);
	font-size: 0.8em;
}

.sb-lf-list {
	display: flex;
	flex-direction: column;
	gap: 2px;
	max-height: min(240px, 35vh);
	margin: 0 -4px;
	overflow-y: auto;
}

.sb-lf-hit {
	display: flex;
	flex: none;
	flex-direction: column;
	gap: 2px;
	padding: 6px 8px;
	border: 0;
	border-radius: 10px;
	background: none;
	color: var(--text);
	font: inherit;
	text-align: left;
	cursor: pointer;
}

.sb-lf-hit:hover {
	background: var(--surface-2);
}

.sb-lf-hit[aria-selected="true"] {
	background: color-mix(in srgb, var(--hit) 24%, transparent);
}

.sb-lf-hit-text {
	display: -webkit-box;
	overflow: hidden;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 2;
	overflow-wrap: anywhere;
}

.sb-lf-hit-where {
	color: var(--faint);
	font-size: 0.78em;
}

.sb-lf-more {
	align-items: center;
	color: var(--muted);
}

/* Ctrl+F once more while it is open: it lights up for a moment */
.sb-lf.is-glowing {
	animation: sb-lf-glow 1.2s var(--ease-out);
}

@keyframes sb-lf-glow {
	from {
		box-shadow:
			var(--card-shadow),
			0 0 0 0 transparent,
			0 0 0 0 transparent;
	}
	25% {
		box-shadow:
			var(--card-shadow),
			0 0 0 3px color-mix(in srgb, var(--hit) 75%, transparent),
			0 0 26px 8px color-mix(in srgb, var(--hit) 45%, transparent);
	}
	to {
		box-shadow:
			var(--card-shadow),
			0 0 0 0 transparent,
			0 0 0 0 transparent;
	}
}

/* folded, or a phone: it floats at the bottom left (with the bottom card, 设置 › 外观 › 局部搜索框), over a phone's
   keyboard (--lf-kb) */
.sb-lf-float {
	position: fixed;
	bottom: calc(var(--gap) + var(--lf-kb, 0px));
	left: var(--gap);
	z-index: 30;
	display: none;
	flex-direction: column;
	gap: var(--gap);
	width: calc(var(--sb-width) - var(--gap));
}

.sb-lf-float.is-on {
	display: flex;
}

@media (max-width: 767px) {
	.sb-lf-float {
		right: var(--gap);
		width: auto;
	}
}

/* what is found in the page (lib/pagefind.js textFinder) */
::highlight(find-hit) {
	background-color: color-mix(in srgb, var(--hit) 30%, transparent);
}

::highlight(find-now) {
	background-color: color-mix(in srgb, var(--hit) 80%, transparent);
}

.sb-list-head {
	display: flex;
	flex: none;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	min-height: 46px;
	padding: 8px 10px 4px 16px;
}

.sb-list-title {
	overflow: hidden;
	font-size: calc(13px * var(--ui-scale));
	font-weight: 600;
	color: var(--text-strong);
	text-overflow: ellipsis;
	white-space: nowrap;
}

.sb-list-actions {
	display: flex;
	gap: 6px;
}

/* the deck's button that turns the cards (shell.flipList): the other card's name, a dot when something is new there
   (AI 助手's single / group chats, 文档's pages / files; moved here from Fadmeo 2026-10-24 when Eolite came to use it) */
.sb-flip {
	position: relative;
	display: inline-flex;
	flex: none;
	align-items: center;
	gap: 5px;
	max-width: 150px;
	padding: 3px 10px;
	border: 1px solid var(--border);
	border-radius: 999px;
	background: none;
	color: var(--muted);
	font: inherit;
	font-size: calc(12px * var(--ui-scale));
	cursor: pointer;
	transition: background-color 0.15s, color 0.15s;
}

.sb-flip span:not(.sb-flip-dot) {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.sb-flip:hover {
	background: var(--surface-2);
	color: var(--text-strong);
}

.sb-flip svg {
	flex: none;
}

.sb-flip-dot {
	position: absolute;
	top: -2px;
	right: -2px;
	width: 8px;
	height: 8px;
	border: 2px solid var(--sb-card-bg);
	border-radius: 50%;
	background: var(--mark);
	box-sizing: content-box;
}

.sb-flip:active {
	background-color: var(--press-bg);
}

.sb-list-body {
	flex: 1;
	min-height: 0;
	overflow: auto;
	padding: 2px 6px 8px;
}

/* what is held at the list's bottom (setList's floor: 邮件's envelope), still while the list scrolls: in from the
   card's edges as the list's rows are, one part behind the list, one over it; the list ends --floor-rise above the
   floor's bottom, inside the part over it, so nothing it scrolls shows under that (the user 2026-10-27: 信封底下还是
   有缝 — three times: a cover under it, then the list ending at it, then the rows under it as it glided) */
.sb-list-body.has-floor {
	position: relative;
	z-index: 1;
	margin-bottom: calc(8px + var(--floor-rise));
	padding-bottom: 0;
}

.sb-list-floor {
	position: absolute;
	inset-inline: 8px;
	bottom: 8px;
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	pointer-events: none;
}

.sb-list-floor:empty {
	display: none;
}

.sb-list-floor.is-behind {
	z-index: 0;
}

.sb-list-floor.is-over {
	z-index: 2;
}

/* the card's own small search (shell.js, lib/find.js): a small well under the title row */
.sb-find {
	display: flex;
	flex: none;
	align-items: center;
	gap: 6px;
	margin: 2px 10px 6px;
	padding: 0 6px 0 10px;
	border: 1px solid var(--border);
	border-top-color: var(--edge-bottom);
	border-radius: 999px;
	background: var(--surface);
	box-shadow: var(--sunken);
	color: var(--faint);
	cursor: text;
}

.sb-find[hidden] {
	display: none;
}

.sb-find:focus-within {
	border-color: var(--border-strong);
	color: var(--muted);
}

.sb-find-field {
	flex: 1;
	min-width: 0;
	height: 26px;
	padding: 0;
	border: 0;
	outline: 0;
	background: none;
	color: var(--text);
	font: inherit;
	font-size: calc(13px * var(--ui-scale));
}

.sb-find-field::-webkit-search-cancel-button {
	display: none;
}

.sb-find-clear {
	display: inline-flex;
	flex: none;
	align-items: center;
	justify-content: center;
	width: 20px;
	height: 20px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: var(--surface-2);
	color: var(--muted);
	cursor: pointer;
}

.sb-find-clear[hidden] {
	display: none;
}

/* 侧栏列表空的时候那一句（可带一句小字）。全项目这一份（UI-RULES 6，用户 2026-11-02「这个还没有对话的位置奇怪」）：
   在列表里左右居中，离上面的搜索框、卡头一个行距；程序只给字，不写自己的 …-sb-empty */
.sb-empty {
	margin: 0;
	padding: 12px 10px;
	color: var(--muted);
	font-size: calc(13px * var(--ui-scale));
	text-align: center;
	text-wrap: balance;
}

.sb-empty p {
	margin: 0;
}

.sb-empty .sb-empty-hint {
	margin-top: 4px;
	font-size: calc(12px * var(--ui-scale));
	color: var(--faint);
}

/* what is not found goes; what is folded opens meanwhile */
.sb-list-body .is-not-found {
	display: none !important;
}

.sb-list-body.is-finding .set-nav-parts {
	grid-template-rows: 1fr;
}

.sb-list-body.is-finding .set-nav-chevron svg {
	transform: rotate(90deg);
}

/* the old list's card on its way out to the left (shell.js leaveCard) */
.sb-ghost {
	position: absolute;
	z-index: 2;
	flex: none;
	margin: 0;
	pointer-events: none;
}

/* ---- wide screens: the bookmark on the middle card's right edge folds the sidebar away and back ---- */
/* the sidebar and the bookmark travel the same way at the same pace (the bookmark is never left hanging); folded, the
   cards' shadows would still show at the window's edge: hidden once they are there (.sb-gone, put on by shell.js when
   the sidebar has gone: a delayed visibility in a transition hid the home card's pictures at the start in GNOME Web,
   the card going out empty, 2026-10-19).
   Only moved, never laid out again (transform): moving it by its margin had the cards and their many-layered shadows
   painted again every frame, and GNOME Web (WebKitGTK, drawing on the CPU) dropped frames all the way (2026-10-20).
   The work area takes its new width at once and what is in it slides there, the same pace (shell.js sidebarFold: by
   transform too, 2026-10-24: its padding moving had it laid out again every frame). */
@media (min-width: 768px) {
	.shell {
		position: relative;
	}

	/* (floating over every page, see-through: only its cards take the pointer — common/web/docs/float-layout.md) */
	.sb {
		position: absolute;
		top: 0;
		left: 0;
		z-index: 3;
		background: transparent;
		pointer-events: none;
		transition: transform var(--dur) var(--ease);
	}

	.sb > :not(.sb-ghost) {
		pointer-events: auto;
	}

	:root.sb-collapsed .sb {
		transform: translateX(calc(-1 * var(--sb-width)));
	}
}

:root.sb-gone .sb {
	visibility: hidden;
}

/* the work area laid out anew (the sidebar folded, the side card opened: lib/motion.js relayout): only its parts are
   pictured and moved; the rest of the page is the live one, not faded, and takes the clicks (folded again half way) */
:root[data-relayout]::view-transition,
:root[data-relayout]::view-transition-group(*),
:root[data-relayout]::view-transition-image-pair(*),
:root[data-relayout]::view-transition-old(*),
:root[data-relayout]::view-transition-new(*) {
	pointer-events: none;
}

/* (moved by lib/motion.js's own animations, not the browser's; the two pictures of a part crossfading add up, as the
   browser's do. Not display: none: WebKit crashed on the next view transition) */
:root[data-relayout]::view-transition-group(*),
:root[data-relayout]::view-transition-old(*),
:root[data-relayout]::view-transition-new(*) {
	animation: none;
}

:root[data-relayout]::view-transition-old(*),
:root[data-relayout]::view-transition-new(*) {
	mix-blend-mode: plus-lighter;
}

:root[data-relayout]::view-transition-old(root) {
	opacity: 0;
}

.sb-edge {
	position: fixed;
	top: 50%;
	left: var(--sb-width);
	z-index: 6;
	display: flex;
	align-items: center;
	justify-content: center;
	width: var(--sb-edge-room, 22px); /* (the work area keeps as much clear of it: below) */
	height: 72px;
	padding: 0;
	border: 1px solid var(--border);
	border-top-color: var(--edge-top);
	border-bottom-color: var(--edge-bottom);
	border-left: 0;
	border-radius: 0 10px 10px 0;
	background: var(--card-bg);
	box-shadow: var(--edge-light), var(--elev-1);
	color: var(--muted);
	cursor: pointer;
	transform: translateY(-50%);
	transform-origin: left center;
	transition:
		transform var(--dur) var(--ease),
		color var(--dur-quick);
}

.sb-edge:hover {
	color: var(--text-strong);
}

.sb-edge:focus-visible {
	outline: 2px solid var(--border-strong);
	outline-offset: 2px;
}

.sb-edge svg {
	transition: transform var(--dur) var(--ease);
}

:root.sb-collapsed .sb-edge {
	transform: translate(calc(-1 * var(--sb-width)), -50%);
}

:root.sb-collapsed .sb-edge svg {
	transform: rotate(180deg);
}

/* 设置 › 外观 › 侧边栏边缘开关: a thin handle instead of the bookmark */
:root[data-sidebar-toggle="handle"] .sb-edge {
	width: 14px; /* (and its margin: --sb-edge-room 16px) */
	margin-left: 2px;
	border: 0;
	background: none;
	box-shadow: none;
}

:root[data-sidebar-toggle="handle"] .sb-edge::before {
	content: "";
	width: 4px;
	height: 44px;
	border-radius: 999px;
	background: var(--border-strong);
	transition:
		transform var(--dur-quick),
		background-color var(--dur-quick);
}

/* longer under the pointer: 44 → 60px, stretched (its round ends 4px wide: no one sees them squeezed) */
:root[data-sidebar-toggle="handle"] .sb-edge:hover::before {
	background: var(--muted);
	transform: scaleY(1.3636);
}

:root[data-sidebar-toggle="handle"] .sb-edge svg {
	display: none;
}

/* …or none: a button left of the module's in the middle card's head, and, folded, a round one in the corner */
.sb-list-tools {
	display: flex;
	align-items: center;
	gap: 6px;
}

/* a page's own buttons (setPageTools: 文档's 搜索 and 共享), small and round like the module's "+" */
.sb-page-tools {
	display: flex;
	gap: 6px;
}

/* none (the module's buttons too: the home page's frame has only the fold button, and an empty one took a gap beside
   it — the user 2026-10-27: 那个按钮距离上边和右边的距离不一样) */
.sb-page-tools:empty,
.sb-list-actions:empty {
	display: none;
}

.sb-page-tools .round {
	width: 26px;
	height: 26px;
}

.sb-page-tools .round svg {
	width: 15px;
	height: 15px;
}

/* a picker as the card's title (文档's space): its shadow is not cut off at the title's edge, and it is as wide as
   its name while there is room (narrower only when the buttons need it) */
.sb-list-title:has(.picker) {
	display: flex;
	flex: 0 1 auto;
	min-width: 0;
	overflow: visible;
}

.sb-collapse,
.sb-expand,
:root[data-sidebar-toggle="none"] .sb-edge {
	display: none;
}

/* ---- lifted (shell.js sidebarLift, a page offering it: Postige's editor): the middle card up in the top-left corner,
   the cards above and below it out of the way, the work area under it to the window's left (--lift-room: how much of
   its left the card takes) ---- */
.sb-lift[hidden] {
	display: none;
}

@media (max-width: 767px) {
	.sb-lift {
		display: none;
	}
}

@media (min-width: 768px) {
	:root.sb-lifted:not(.sb-collapsed) .shell {
		--lift-room: var(--sb-width);
	}

	:root.sb-lifted .sb {
		padding-bottom: var(--sb-lift-below, var(--gap));
	}

	:root.sb-lifted :is(.sb-services, .sb-pages, .sb-dock > *):not(.sb-ghost) {
		display: none;
	}

	/* (the bookmark and the fold button: this card's lift button brings the cards back) */
	:root.sb-lifted .sb-edge {
		opacity: 0;
		pointer-events: none;
	}

	:root.sb-lifted .sb-list-tools .sb-collapse {
		display: none;
	}

	/* (no bookmark to keep clear of) */
	:root.sb-lifted:not(.sb-collapsed) {
		--sb-edge-room: 0px;
	}

	/* the top bar's buttons over what is beside the card (a toolbar in the middle: in the middle of that) */
	:root.sb-lifted:not(.sb-collapsed) .work-top {
		left: var(--lift-room);
	}
}

/* folded: the top-left corner's round buttons, 新建 over 展开 (shell.js; the avatar's look, in the other corner) */
.sb-corners {
	position: fixed;
	top: calc(var(--gap) + 3px);
	left: calc(var(--gap) + 6px);
	z-index: 20;
	display: none;
	flex-direction: column;
	gap: 10px;
}

.sb-corners .round,
.sb-fly.sb-corner-new,
.sb-fly.sb-corner-tool,
.sb-fly.sb-expand {
	width: 36px;
	height: 36px;
	border: 1px solid var(--border);
	background: var(--surface);
	box-shadow: var(--edge-light), var(--elev-2);
	color: var(--text);
}

.sb-corner-tools {
	display: contents;
}

.sb-corner-tool svg {
	width: 16px;
	height: 16px;
}

.sb-corner-new[hidden] {
	display: none !important;
}

@media (min-width: 768px) {
	:root[data-sidebar-toggle="none"] .sb-collapse {
		display: inline-flex;
	}

	:root.sb-collapsed .sb-corners {
		display: flex;
	}

	:root[data-sidebar-toggle="none"].sb-collapsed .sb-expand {
		display: inline-flex;
	}

	/* the top bar's own buttons keep clear of them, gliding over as the sidebar goes (shell.js: by transform) */
	:root.sb-collapsed .work-top {
		padding-left: calc(var(--gap) + 56px);
	}

	/* and the page under it (the column of them reaches down over it: 文档's 「我的空间」 under 分享, 2026-10-24), as
	   much on the right, so what is in it is as far from either edge of the window (the user 2026-10-25) */
	:root.sb-collapsed .work-main {
		padding-inline: calc(var(--gap) + 30px);
	}

	/* the edge's bookmark (or handle) stands over the work area's left edge, folded or not: the work area keeps clear
	   of it, so what a page places itself at the work area's left (.work's left and its padding: 音频's track heads
	   reached under it, their first letter hidden, 2026-10-30) is beside it, not under it. The top bar and the page's
	   box stay where they were (what is in them keeps clear already: the page's padding, the corner's buttons) */
	:root {
		--sb-edge-room: 22px;
	}

	:root[data-sidebar-toggle="handle"] {
		--sb-edge-room: 16px;
	}

	:root[data-sidebar-toggle="none"] {
		--sb-edge-room: 0px;
	}

	.work {
		padding-left: calc(var(--room-left) + var(--sb-edge-room));
	}

	.work-main {
		margin-left: calc(-1 * var(--sb-edge-room));
	}
}

/* folding and unfolding: the stand-ins flying between the list and the corner; the real ones wait out of sight */
.sb-fly {
	position: fixed;
	z-index: 21;
	display: inline-flex !important;
	align-items: center;
	justify-content: center;
	margin: 0;
	padding: 0;
	pointer-events: none;
}

.sb-waiting {
	visibility: hidden;
}

/* ---- the work area ---- */

/* One way for every page (common/web/docs/float-layout.md, 2026-11-05): the work area is the whole window; the
   sidebar, the top bar and the side card float over it, and these say how much of it each stands over — what a page
   keeps clear of, or not (its background, fixed to the window, under all of them):
     --room-left   the sidebar (its width; 0 folded, lifted or on a phone)
     --room-right  the side card (components/pop.js sets it on the root: the card's width and its gap; 0 shut)
     --room-top    the top bar's height
   The page's box (.page) is the work area less the left and right; it starts under the top bar unless the page
   reaches up under it (.page-full on its root). Folding the sidebar, opening the side card: only these change, and
   what is in the page goes to its new place (lib/motion.js relayout); a background fixed to the window stays. */
:root {
	--room-left: 0px;
	--room-right: 0px;
	--room-top: 56px;
}

@media (min-width: 768px) {
	:root {
		--room-left: var(--sb-width);
	}

	:root:is(.sb-collapsed, .sb-lifted) {
		--room-left: 0px;
	}
}

@media (max-width: 767px) {
	:root {
		--room-top: 52px;
	}
}

.work {
	position: relative;
	display: flex;
	flex: 1;
	flex-direction: column;
	min-width: 0;
	padding-right: var(--room-right); /* the side card stands there: at once, what is in the work area sliding over (components/pop.js) */
}

/* (floating over the page, see-through: only its buttons take the pointer) */
.work-top {
	position: absolute;
	top: 0;
	right: var(--room-right);
	left: var(--room-left);
	z-index: 5; /* over a page that fills the window under it (Eolite's map) */
	pointer-events: none;
	display: flex;
	flex: none;
	align-items: center;
	gap: 10px;
	height: 56px;
	/* right: room for the avatar (6 + 36 px from the edge) and as much again as between two buttons, a little more (it
	   is not one of them): 14 px. Was 44 (2 px from it: the user 2026-10-25, 为什么每次这里有按钮都距离头像这么近) */
	padding: var(--gap) calc(var(--gap) + 56px) 0 calc(var(--gap) + 6px);
}

/* the side card covers the avatar's corner: the avatar moves to the left of it, and nothing else has to */
:root.has-side .work-top {
	padding-right: calc(var(--gap) + 56px);
}

.work-menu {
	display: none;
}

.work-top :is(.work-menu, .work-tools > *) {
	pointer-events: auto;
}

.work-tools {
	display: flex;
	flex: 1;
	align-items: center;
	min-width: 0; /* (what is in it gives way, never past the avatar's room: where a file is, long, at its left) */
	justify-content: flex-end;
	gap: 8px;
}

.work-main {
	position: relative; /* the leaving page sits on top while it goes (lib/motion.js swapPage) */
	flex: 1;
	min-height: 0;
	overflow: hidden;
}

.page {
	height: calc(100% - var(--room-top));
	margin-top: var(--room-top);
	overflow: auto;
	padding: 12px 28px 40px;
}

.page--narrow > * {
	max-width: 760px;
	margin-left: auto;
	margin-right: auto;
}

.page-title {
	margin: 8px auto 16px;
	font-size: calc(22px * var(--ui-scale));
	font-weight: 650;
	color: var(--text-strong);
}

.page-title:has(+ .page-subtitle) {
	margin-bottom: 4px;
}

.page-subtitle {
	margin: 0 auto 20px;
	color: var(--muted);
}

.boot-error {
	padding: 48px 16px;
	text-align: center;
	color: var(--muted);
}

/* ---- offline (js/lib/offline.js) ---- */

.boot-offline {
	display: grid;
	place-items: center;
	align-content: center;
	gap: 8px;
	min-height: 100dvh;
	padding: 24px 16px;
	text-align: center;
}
.boot-offline img {
	width: 72px;
	height: 72px;
}
.boot-offline h1 {
	margin: 8px 0 0;
	font-size: 20px;
}
.boot-offline p {
	max-width: 22em;
	margin: 0 0 12px;
	color: var(--muted);
}

/* who sees it knows the pages are as they were kept: top centre, over the work area */
.offline-pill {
	position: fixed;
	top: calc(var(--gap) + 8px);
	left: 50%;
	z-index: 30;
	transform: translateX(-50%);
	padding: 4px 12px;
	border-radius: 999px;
	background: var(--surface);
	color: var(--muted);
	font-size: 13px;
	box-shadow: var(--edge-light), var(--elev-2);
	pointer-events: none;
}

/* a page that needs the network: where to go instead (app.js needsNetwork) */
.offline-go {
	display: flex;
	flex-wrap: wrap;
	gap: 8px 20px;
	margin: 0;
}

/* ---- the avatar, alone in the top-right corner ---- */

.shell-avatar {
	position: fixed;
	top: calc(var(--gap) + 3px);
	right: calc(var(--gap) + 6px);
	z-index: 20;
	width: 36px;
	height: 36px;
	box-shadow: var(--edge-light), var(--elev-2);
	/* aside for the side card (--room-right, components/pop.js) by translate, not by right: not laid out again each frame.
	   Not by transform: the browser draws translate, then scale, then transform, so scale below grew the way aside too
	   (1.1 × the card's 390 px: 39 px to the left, 52 at the spring's top; the user 2026-11-04: 头像往左边跑) */
	translate: calc(-1 * var(--room-right, 0px)) 0;
	transition:
		translate var(--dur) var(--ease),
		scale var(--dur-grow) var(--ease-bounce),
		box-shadow var(--dur-quick);
}

/* pointed at: a little bigger, springing (the user 2026-10-27: 略微扩大一点，一个弹弹的动画); scale, apart from the
   translate that keeps it clear of the side card. A transition both ways, not an animation: left half way, it goes
   back from where it is (an animation taken away ended at once: 移开鼠标不是慢慢变小而是一瞬间变小) */
@media (hover: hover) {
	.shell-avatar:hover {
		scale: 1.1;
		transition:
			translate var(--dur) var(--ease),
			scale calc(var(--dur-grow) * 1.8) var(--ease-spring),
			box-shadow var(--dur-quick);
	}

	.shell-avatar:active {
		scale: 1.02;
	}
}

.shell-avatar .avatar {
	width: 30px !important;
	height: 30px !important;
}

/* its window: who one is on top */
.acct-win .acct-card {
	min-height: 56px;
}

/* takes the room between the face and the arrow, so the arrow is at the right (a classed span does not get .flat's) */
.acct-who {
	display: flex;
	flex: 1;
	min-width: 0;
	flex-direction: column;
	line-height: 1.3;
}

.acct-who b {
	color: var(--text-strong);
	font-weight: 600;
}

.acct-who small {
	overflow: hidden;
	color: var(--muted);
	font-size: calc(12px * var(--ui-scale));
	text-overflow: ellipsis;
}

/* a short note grown out of a "?" */
.win--note .win-body {
	padding: 12px 14px 14px;
}

.help-pop-title {
	font-weight: 600;
	color: var(--text-strong);
}

.help-pop-text {
	margin: 6px 0 0;
	font-size: calc(13px * var(--ui-scale));
	color: var(--muted);
}

/* ---- phones: the sidebar is a drawer ---- */

.sb-shade {
	display: none;
}

@media (max-width: 767px) {
	.sb {
		position: fixed;
		top: 0;
		bottom: 0;
		left: 0;
		z-index: 40;
		width: min(320px, 86vw);
		padding-right: var(--gap);
		background: transparent;
		transform: translateX(-105%);
		transition: transform var(--dur) var(--ease);
	}

	:root.sb-open .sb {
		transform: none;
	}

	/* phones: the drawer, never folded */
	:root.sb-gone .sb {
		visibility: visible;
	}

	.sb-edge {
		display: none;
	}

	.sb-shade {
		position: fixed;
		inset: 0;
		z-index: 39;
		display: block;
		background: var(--veil-dim);
		opacity: 0;
		pointer-events: none;
		transition: opacity var(--dur) var(--ease);
	}

	:root.sb-open .sb-shade {
		opacity: 1;
		pointer-events: auto;
	}

	:root[data-backdrop="none"] .sb-shade {
		background: transparent;
	}

	:root[data-backdrop="blur"] .sb-shade {
		background: rgb(var(--depth-ink) / 0.08);
		backdrop-filter: blur(var(--veil-blur));
		-webkit-backdrop-filter: blur(var(--veil-blur));
	}

	.work {
		padding-right: 0;
	}

	.work-top,
	:root.has-side .work-top {
		height: 52px;
		padding: var(--gap) calc(var(--gap) + 56px) 0 var(--gap);
	}

	.work-menu {
		display: inline-flex;
		width: 36px;
		height: 36px;
	}

	.shell-avatar {
		right: var(--gap);
	}

	.page {
		padding: 8px 14px 32px;
	}

	.page-title {
		font-size: calc(19px * var(--ui-scale));
	}
}

/* a list scrolled a row at a time (setList's snap; 邮件, the user 2026-10-27: 一格一格地上划和下滑，仅适用于电脑): with a
   mouse only; the rows stop where the first one starts (the list's padding and its own 2 px above it) */
@media (hover: hover) and (pointer: fine) {
	.sb-list-body.is-snap {
		scroll-snap-type: y mandatory;
		scroll-padding-top: 4px;
	}

	.sb-list-body.is-snap.is-gliding {
		scroll-snap-type: none;
	}
}

/* ---- 设置 ---- */

/* a group of rows that may be split in two (modules/settings.js talkGroups): laid out as if it were not there */
.set-nav-box {
	display: contents;
}

.set-nav-group {
	padding: 12px 10px 4px;
	font-size: calc(12px * var(--ui-scale));
	color: var(--muted);
}

.set-nav > .set-nav-group:first-child {
	padding-top: 2px;
}

/* a section with parts (AI 助手: 常规, 显示…): an arrow in its icon's place, and the parts under the row open and close
   like a workspace of AI 助手's (0fr ↔ 1fr animates the height without measuring it); the line hangs from the arrow's
   middle (the row's padding 10px + half the 16px icon) */
.set-nav-chevron {
	display: inline-flex;
	flex: none;
	align-items: center;
	justify-content: center;
	width: 16px;
	height: 16px;
	margin: -4px 0;
	padding: 4px 0;
	box-sizing: content-box;
	color: var(--muted);
}

.set-nav-chevron svg {
	transition: transform var(--dur) var(--ease);
}

.set-nav-sec.is-open > .flat .set-nav-chevron svg {
	transform: rotate(90deg);
}

/* 0fr shut, 1fr open; modules/settings.js slides it by transform (lib/motion.js slideBody) */
.set-nav-parts {
	display: grid;
	grid-template-rows: 0fr;
}

.set-nav-sec.is-open > .set-nav-parts {
	grid-template-rows: 1fr;
}

.set-nav-inner {
	min-height: 0;
	overflow: hidden;
}

/* a section's parts as a tree (the user, 2026-10-19): a line down from the section, a short branch to each part at
   its middle, the line ending at the last one's middle with a rounded corner (└), not past it */
.set-nav-tree {
	display: flex;
	flex-direction: column;
	gap: 1px;
	margin: 1px 0 4px 17.5px;
	padding-left: 10px;
}

.set-nav-tree > .set-nav-part {
	position: relative;
	overflow: visible;
}

/* the line past this part (up through the gap above it, down its whole height) */
.set-nav-tree > .set-nav-part::after {
	position: absolute;
	top: -1px;
	bottom: 0;
	left: -10px;
	border-left: 1px solid var(--border-strong);
	content: "";
	pointer-events: none;
}

/* the branch to it */
.set-nav-tree > .set-nav-part::before {
	position: absolute;
	top: 50%;
	left: -10px;
	width: 15px; /* up to just before its words */
	border-top: 1px solid var(--border-strong);
	content: "";
	pointer-events: none;
}

/* the last: the line turns into its branch */
.set-nav-tree > .set-nav-part:last-child::after {
	bottom: auto;
	width: 15px;
	height: calc(50% + 1px);
	border-bottom: 1px solid var(--border-strong);
	border-bottom-left-radius: 6px;
}

.set-nav-tree > .set-nav-part:last-child::before {
	content: none;
}

/* 全局设置's button in the bottom card (shell.js): who one is for it on its right, in red */
.sb-tab-tag {
	flex: none;
	margin-left: auto;
	padding: 1px 8px;
	border-radius: 999px;
	background: var(--danger-bg);
	color: var(--danger);
	font-size: calc(11px * var(--ui-scale));
	font-weight: 600;
	line-height: 18px;
}

.set-nav-part {
	min-height: 32px;
	font-size: calc(13px * var(--ui-scale));
}



/* ---- arriving under water (lib/jump.js: signing in, another machine): the water over everything ---- */
:root {
	--jump-water: rgb(7 55 99); /* lib/jump.js draws the same */
	--jump-words: #fff;
}

html.jump-in body::after {
	position: fixed;
	inset: 0;
	z-index: 2147483000;
	background: var(--jump-water);
	content: "";
}

.jump-stage {
	position: fixed;
	inset: 0;
	z-index: 2147483000;
	pointer-events: none;
}

.jump-stage canvas {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
}

.jump-words {
	position: absolute;
	top: 50%;
	right: 0;
	left: 0;
	padding: 0 16px;
	color: var(--jump-words);
	font-size: clamp(22px, 5.5vw, 40px);
	font-weight: 700;
	line-height: 1.45;
	text-align: center;
	text-shadow: 0 2px 12px rgb(var(--depth-ink) / 0.25);
	white-space: pre-line;
	translate: 0 -50%;
}

/* ---- the admin's logo or words (lib/branding.js, 设置 › 管理 › 系统 › 品牌) at the top of the services card; a picture
   shows the one for the current theme ---- */
.sb-brand {
	display: flex;
	justify-content: center;
	min-width: 0;
	padding: 8px 10px 10px;
	border-radius: var(--radius);
	color: var(--text-strong);
	text-decoration: none;
	transition: transform var(--dur-quick) var(--ease-out);
	-webkit-user-drag: none;
}

.sb-brand[hidden] {
	display: none;
}

.sb-brand:active {
	transform: scale(0.97);
}

.sb-brand:focus-visible {
	outline: 2px solid var(--border-strong);
	outline-offset: 2px;
}

.sb-brand-mark {
	min-width: 0;
	font-size: calc(20px * var(--ui-scale));
	line-height: 1.25;
}

.sb-brand-mark.brand-text {
	text-align: center;
	white-space: normal;
	overflow-wrap: anywhere;
	text-wrap: balance;
}

.sb-brand-mark.brand-logo {
	justify-content: center;
	height: 44px;
}

.sb-brand-mark.brand-logo img {
	object-position: center;
}

.brand-text {
	overflow: hidden;
	font-weight: 500;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.brand-text.is-bold {
	font-weight: 700;
}

.brand-text.is-italic {
	font-style: italic;
}

.brand-text.is-underline {
	text-decoration: underline;
	text-decoration-thickness: 0.08em;
	text-underline-offset: 0.18em;
}

.brand-logo {
	display: inline-flex;
	align-items: center;
	min-width: 0;
	max-width: 100%;
}

.brand-logo img {
	display: block;
	max-width: 100%;
	height: 100%;
	object-fit: contain;
	object-position: left center;
}

.brand-logo-dark {
	display: none !important;
}

@media (prefers-color-scheme: dark) {
	:root:not([data-theme="light"]) .brand-logo-light {
		display: none !important;
	}

	:root:not([data-theme="light"]) .brand-logo-dark {
		display: block !important;
	}
}

:root[data-theme="dark"] .brand-logo-light {
	display: none !important;
}

:root[data-theme="dark"] .brand-logo-dark {
	display: block !important;
}



/* ---- the home page's middle card: a pixel picture (components/scenery.js) and a clock over it (components/clock.js) ---- */
.scenery {
	position: absolute;
	top: 0;
	left: 0;
	image-rendering: pixelated;
	image-rendering: crisp-edges;
}

/* the clock is drawn over the picture: its colours are the picture's, the same in both themes */
.clock {
	--clock-ink: #fff;
	--clock-shade: 10 20 40; /* rgb(var(--clock-shade) / a): the shadows under it */
	--clock-second: #ffb05a;
	--clock-minute: #9fd4ff;
	--clock-face: #fbfaf6; /* 经典: a dial of paper and ink, a red second hand */
	--clock-face-line: #2b2b2b;
	--clock-face-ink: #1d1d1d;
	--clock-face-red: #d8432f;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 6px;
	color: var(--clock-ink); /* over the picture; in the card's own room, the text's (.home-clock) */
	text-shadow: 0 1px 2px rgb(var(--clock-shade) / 0.45);
	pointer-events: none;
	user-select: none;
}

.clock-date {
	font-size: calc(13px * var(--ui-scale));
	font-weight: 500;
	letter-spacing: 0.02em;
}

.clock-pixels {
	display: block;
	image-rendering: pixelated;
}

.clock--thin .clock-time {
	font-size: calc(52px * var(--ui-scale));
	font-weight: 200;
	font-variant-numeric: tabular-nums;
	line-height: 1;
	letter-spacing: 0.02em;
}

.clock--glass .clock-dial {
	width: 112px;
	height: 112px;
	border-radius: 50%;
	background: rgb(var(--shine) / 0.16);
	box-shadow: inset 0 1px 0 rgb(var(--shine) / 0.5), 0 6px 18px rgb(var(--clock-shade) / 0.25);
	-webkit-backdrop-filter: blur(6px);
	backdrop-filter: blur(6px);
}

.clock-rim {
	fill: none;
	stroke: currentColor;
	stroke-opacity: 0.55;
	stroke-width: 1;
}

.clock-mark {
	stroke: currentColor;
	stroke-opacity: 0.8;
	stroke-width: 1.4;
	stroke-linecap: round;
}

.clock-mark.is-big {
	stroke-opacity: 1;
	stroke-width: 2.4;
}

.clock-hand {
	stroke: currentColor;
	stroke-linecap: round;
}

.clock-hand.is-hour {
	stroke-width: 3.6;
}

.clock-hand.is-minute {
	stroke-width: 2.4;
}

.clock-hand.is-second {
	stroke: var(--clock-second);
	stroke-width: 1.2;
}

.clock-pin {
	fill: var(--clock-second);
}

.clock--classic .clock-dial {
	width: 112px;
	height: 112px;
	border-radius: 50%;
	background: var(--clock-face);
	box-shadow: inset 0 -2px 4px rgb(var(--clock-shade) / 0.12), 0 6px 18px rgb(var(--clock-shade) / 0.3);
}

.clock--classic .clock-rim {
	stroke: var(--clock-face-line);
	stroke-width: 2.5;
}

.clock--classic .clock-mark {
	stroke: var(--clock-face-line);
	stroke-width: 1.4;
}

.clock--classic .clock-num {
	fill: var(--clock-face-ink);
	font-size: 12px;
	font-weight: 600;
	text-shadow: none;
}

.clock--classic .clock-hand {
	stroke: var(--clock-face-ink);
}

.clock--classic .clock-hand.is-second,
.clock--classic .clock-pin {
	stroke: var(--clock-face-red);
	fill: var(--clock-face-red);
}

.clock--minimal .clock-dial,
.clock--numbers .clock-dial,
.clock--rings .clock-dial {
	width: 116px;
	height: 116px;
	filter: drop-shadow(0 1px 2px rgb(var(--clock-shade) / 0.45));
}

.clock-dot {
	fill: currentColor;
}

.clock--minimal .clock-hand.is-hour {
	stroke-width: 2.6;
}

.clock--minimal .clock-hand.is-minute {
	stroke-width: 1.6;
}

.clock--numbers .clock-rim {
	stroke-opacity: 0.7;
}

.clock--numbers .clock-num {
	fill: currentColor;
	font-size: 8px;
	font-weight: 500;
}

.clock-ring-track {
	fill: none;
	stroke: currentColor;
	stroke-opacity: 0.18;
	stroke-width: 5;
}

.clock-ring {
	fill: none;
	stroke-width: 5;
	stroke-linecap: round;
	transition: stroke-dashoffset 0.4s ease;
}

.clock-ring.is-hour {
	stroke: currentColor;
}

.clock-ring.is-minute {
	stroke: var(--clock-minute);
}

.clock-ring.is-second {
	stroke: var(--clock-second);
}

.clock-ring-time {
	fill: currentColor;
	font-size: 15px;
	font-weight: 300;
}

.clock-segments {
	width: 178px;
	height: 63px;
	filter: drop-shadow(0 0 6px rgb(var(--shine) / 0.35)) drop-shadow(0 1px 2px rgb(var(--clock-shade) / 0.4));
}

.clock-seg {
	fill: currentColor;
	fill-opacity: 0.12;
}

.clock-seg.is-on {
	fill-opacity: 1;
}

/* a row of them among words (components/segdigits.js segFigures: Reassurance's time signature): as tall as the words' line */
.seg-figures {
	flex: none;
	width: calc(var(--seg-size, calc(14px * var(--ui-scale))) * var(--seg-ratio, 1));
	height: var(--seg-size, calc(14px * var(--ui-scale)));
}

/* the middle card as a picture frame (setList's frame: the home page): no title row or search, the body to the edges;
   the button that folds the sidebar (边缘开关 "不显示") floats in its corner */
.sb-deck.is-frame .sb-list-head {
	position: absolute;
	top: 0;
	right: 0;
	z-index: 2;
	min-height: 0;
	padding: 8px;
}

/* the picture over all of it (8px in from the card): the button inside it, as far in from its edge again, not over
   its round corner (the user, 2026-10-24) */
.sb-deck.is-frame:has(.home-frame:not(.is-most)) .sb-list-head {
	padding: 16px;
}

.sb-deck.is-frame .sb-list-body {
	position: relative;
	padding: 0;
	overflow: hidden;
}

.home-frame {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
}

.home-picture {
	position: relative;
	flex: 1;
	min-height: 0;
	overflow: hidden;
}

/* the picture inset like a photo, the card round it as its frame (the user, 2026-10-19); over most of it, the clock
   in the room above */
.home-picture {
	margin: 8px;
	border-radius: calc(var(--radius-card) - 6px);
}

.home-frame.is-most .home-picture {
	margin-top: 0;
}

.home-clock {
	display: flex;
	flex: none;
	justify-content: center;
	padding: 20px 12px 16px;
}

.home-clock .clock {
	color: var(--text-strong);
	text-shadow: none;
}

.home-clock .clock-date {
	color: var(--muted);
}

.home-clock .clock--glass .clock-dial {
	background: var(--surface);
	box-shadow: var(--sunken);
	-webkit-backdrop-filter: none;
	backdrop-filter: none;
}

.home-clock .clock-segments,
.home-clock .clock--minimal .clock-dial,
.home-clock .clock--numbers .clock-dial,
.home-clock .clock--rings .clock-dial {
	filter: none;
}

/* over all of it: the pixel clock on the picture's sky */
.home-clock.is-over {
	position: absolute;
	top: 36px;
	right: 0;
	left: 0;
	padding: 0;
}

.home-clock.is-over .clock {
	color: var(--clock-ink);
	text-shadow: 0 1px 2px rgb(var(--clock-shade) / 0.45);
}

.home-clock.is-over .clock-date {
	color: var(--clock-ink);
}

/* the work area: the logo in the middle, the admins' words under it */
.home-page {
	display: grid;
	place-items: center;
}

.home-center {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 18px;
	max-width: min(560px, 100%);
	margin-top: -6vh;
	text-align: center;
}

.home-logo img {
	display: block;
	width: auto;
	height: auto;
	max-width: min(320px, 70vw);
	max-height: 140px;
	object-fit: contain;
	object-position: center;
}

.home-logo-text {
	color: var(--text-strong);
	font-size: calc(40px * var(--ui-scale));
	line-height: 1.15;
}

.home-words {
	margin: 0;
	color: var(--muted);
	font-size: calc(15px * var(--ui-scale));
	line-height: 1.7;
	white-space: pre-wrap;
}

/* ---- an editor's backdrop over the whole window (components/backdrop.js; UI-RULES 第 6 条): the canvas's colour and
   its pattern (data-pattern: dots, crosses, a grid; plain: none), a mask in --canvas-dot's colour, its cell (--bd-cell)
   and where it starts (--bd-at) as the page moves and zooms what is on it. Fixed: a layer of its own, which fades when
   the page comes or goes while the rest moves with the page, and stays as it is from one page with one to the next
   (lib/motion.js swapPage, data-backdrop) ---- */
.page-dots {
	position: fixed;
	inset: 0;
	background-color: var(--canvas-bg);
}

.page-dots::before {
	content: "";
	position: absolute;
	inset: 0;
	background: var(--canvas-dot);
	mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22'%3E%3Ccircle cx='11' cy='11' r='1.1' fill='white'/%3E%3C/svg%3E");
	mask-position: var(--bd-at, 0 0);
	mask-size: var(--bd-cell, 22px) var(--bd-cell, 22px);
}

.page-dots[data-pattern="cross"]::before {
	mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22'%3E%3Cpath d='M7 11h8M11 7v8' stroke='white' stroke-width='1.1'/%3E%3C/svg%3E");
}

.page-dots[data-pattern="grid"]::before {
	mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22'%3E%3Cpath d='M0 .5h22M.5 0v22' stroke='white' stroke-width='1'/%3E%3C/svg%3E");
}

.page-dots[data-pattern="plain"]::before {
	display: none;
}

/* ---- switches: what a page asks of the frame (UI-RULES.md 1). A program puts the class on an element of its own page;
   the look is the frame's, and it goes with the page. ---- */

/* A page that reaches up under the top bar (an editor: its toolbar beside the avatar, its background to the window's
   top — the user 2026-10-25: 我不希望图二这类地方成为单独的区域): .page-full on its root (the page's own element; it goes with
   the page). Its box is then the whole height; what it puts up there is centred on the avatar's middle (--top-mid)
   and keeps clear of the bar's right (--top-right, shell.js); what it shows below the bar starts at --room-top */
.shell {
	--top-mid: calc(var(--gap) + 21px);
}

.page:has(.page-full) {
	height: 100%;
	margin-top: 0;
}

/* frame-card-bottom: on a portrait window (as tall as wide or taller: phones, iPads, computers alike) the page's side
   card is a drawer at the bottom of the work area, half or all of it high, over the page without a backdrop, above what
   the page keeps at the window's bottom (--frame-card-below on that element: its height and gap). Placed by
   js/components/pop.js (it sets nothing here: the drawer's look is css/pop.css .win--card.is-bottom). Postige's
   editor; Reassurance's 底部抽屉 is to be the same */

/* frame-own-avatar: phones: the page shows one's avatar itself (AI 助手, on one's messages), so the corner's goes and
   the top bar takes its room */
@media (max-width: 767px) {
	.shell:has(.frame-own-avatar) .shell-avatar {
		visibility: hidden;
		pointer-events: none;
	}

	.shell:has(.frame-own-avatar) .work-top {
		padding-right: var(--gap);
	}
}

/* something going as water bursting (js/components/burst.js): the drops, on a layer over the page */
.burst-layer {
	position: fixed;
	inset: 0;
	z-index: 70;
	overflow: hidden;
	pointer-events: none;
}

.burst-drop {
	position: absolute;
	border-radius: 50%;
	background: radial-gradient(circle at 32% 30%, rgb(var(--shine) / 0.95), color-mix(in srgb, var(--mark) 85%, transparent) 55%, color-mix(in srgb, var(--mark) 60%, transparent));
}
