/* ===== SHELL =====
   The sidebar / topbar / view frame, the gate screens, and the mobile tab bar.
   The shell owns all the scrolling: only .view scrolls, so the page itself
   never moves and the topbar and sidebar stay put without position:sticky. */

/* ===== GATE ===== */
.gate {
	min-height: 100dvh;
	display: grid;
	place-items: center;
	padding: var(--s-6) var(--s-4);
	/* A single wide, very soft accent wash. It reads as depth rather than as a
	   gradient, and it is the only decorative colour anywhere in the app. */
	background:
		radial-gradient(1100px 520px at 50% -10%, color-mix(in srgb, var(--accent) 9%, transparent), transparent 70%),
		var(--bg);
}

.gate-panel { width: min(100%, 400px); }

.gate-brand {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--s-2);
	margin-bottom: var(--s-6);
	font-size: var(--fs-lg);
	font-weight: 600;
	letter-spacing: -.02em;
}
.gate-mark { width: 22px; height: 22px; color: var(--accent); }

.gate-card {
	padding: var(--s-6);
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: var(--r-xl);
	box-shadow: var(--shadow-md);
}

.gate-title { font-size: var(--fs-xl); }
.gate-sub { margin-top: var(--s-1); margin-bottom: var(--s-5); color: var(--text-2); font-size: var(--fs-base); }

.gate-tabs {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 2px;
	padding: 2px;
	margin-bottom: var(--s-5);
	background: var(--surface-2);
	border: 1px solid var(--border);
	border-radius: var(--r-md);
}
.gate-tab {
	height: 28px;
	border-radius: calc(var(--r-md) - 3px);
	color: var(--text-2);
	font-size: var(--fs-sm);
	font-weight: 500;
	transition: background var(--t-fast), color var(--t-fast);
}
.gate-tab.is-active { background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm); }

.gate-error {
	margin-bottom: var(--s-4);
	padding: 9px var(--s-3);
	background: var(--danger-soft);
	border-radius: var(--r-md);
	color: var(--danger);
	font-size: var(--fs-sm);
}

/* The fingerprint alternative. A rule with a word in it rather than a second
   primary button: there is one way in that the screen is asking for, and one
   that it is offering. */
.gate-alt { margin-top: var(--s-4); }

.gate-or {
	position: relative;
	margin-bottom: var(--s-4);
	text-align: center;
}
.gate-or::before {
	content: "";
	position: absolute;
	top: 50%; left: 0; right: 0;
	border-top: 1px solid var(--border);
}
.gate-or span {
	position: relative;
	padding: 0 var(--s-3);
	background: var(--surface);
	color: var(--text-3);
	font-size: var(--fs-xs);
}

.gate-passkey svg { width: 18px; height: 18px; color: var(--accent); }

.gate-signout {
	display: block;
	width: 100%;
	margin-top: var(--s-4);
	color: var(--text-3);
	font-size: var(--fs-sm);
}
.gate-signout:hover { color: var(--text-2); text-decoration: underline; }

/* The gate is the one screen everybody meets, and on a phone it is met with a
   thumb — usually one-handed, often in a hurry. */
@media (max-width: 700px) {
	.gate { padding: var(--s-5) var(--s-4) var(--s-8); }
	.gate-card { padding: var(--s-5); }
	.gate-tabs { padding: 3px; }
	.gate-tab { height: 36px; font-size: var(--fs-base); }
	.gate-signout { padding: var(--s-3) 0; }
}

.gate-foot {
	margin-top: var(--s-5);
	color: var(--text-3);
	font-size: var(--fs-xs);
	text-align: center;
	text-wrap: balance;
	line-height: 1.6;
}

/* ===== LAYOUT ===== */
.app {
	display: grid;
	grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
	height: 100dvh;
	overflow: hidden;
}
.app.is-collapsed { grid-template-columns: var(--sidebar-w-collapsed) minmax(0, 1fr); }

/* ===== SIDEBAR ===== */
.sidebar {
	display: flex;
	flex-direction: column;
	min-height: 0;
	padding: var(--s-3);
	background: var(--surface);
	border-right: 1px solid var(--border);
}

.sidebar-head {
	display: flex;
	align-items: center;
	gap: var(--s-2);
	height: 40px;
	padding: 0 var(--s-1) 0 var(--s-2);
	margin-bottom: var(--s-3);
}

.brand {
	display: flex;
	align-items: center;
	gap: var(--s-2);
	min-width: 0;
	color: var(--text);
	font-size: var(--fs-md);
	font-weight: 600;
	letter-spacing: -.02em;
	text-decoration: none;
}
.brand-mark { width: 20px; height: 20px; color: var(--accent); }

.sidebar-collapse { margin-left: auto; }
.sidebar-collapse svg { transition: transform var(--t-base) var(--ease); }

.sidebar-search {
	display: flex;
	align-items: center;
	gap: var(--s-2);
	width: 100%;
	height: 34px;
	padding: 0 var(--s-2) 0 10px;
	margin-bottom: var(--s-4);
	border: 1px solid var(--border);
	border-radius: var(--r-md);
	background: var(--surface-2);
	color: var(--text-3);
	font-size: var(--fs-base);
	transition: background var(--t-fast), border-color var(--t-fast);
}
.sidebar-search svg { width: 15px; height: 15px; }
.sidebar-search span { flex: 1; text-align: left; }
.sidebar-search kbd { flex: none; }
.sidebar-search:hover { background: var(--surface); border-color: var(--border-strong); color: var(--text-2); }

.sidebar-nav { display: flex; flex-direction: column; gap: 1px; overflow-y: auto; min-height: 0; flex: 1; }

.nav-item {
	position: relative;
	display: flex;
	align-items: center;
	gap: 10px;
	height: 34px;
	padding: 0 10px;
	border-radius: var(--r-md);
	color: var(--text-2);
	font-size: var(--fs-base);
	font-weight: 500;
	text-decoration: none;
	transition: background var(--t-fast), color var(--t-fast);
}
.nav-item svg { width: 17px; height: 17px; flex: none; }
.nav-item .nav-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-item .nav-count {
	margin-left: auto;
	color: var(--text-3);
	font-size: var(--fs-xs);
	font-variant-numeric: tabular-nums;
}
.nav-item:hover { background: var(--surface-2); color: var(--text); }
.nav-item.is-active { background: var(--accent-soft); color: var(--accent-text); }
.nav-item.is-active .nav-count { color: var(--accent-text); }

/* Drag-to-reorder feedback for the navigation. */
.nav-item.is-dragging { opacity: .4; }
.nav-item.is-drop-target { box-shadow: inset 0 2px 0 var(--accent); }

.sidebar-foot { padding-top: var(--s-3); margin-top: var(--s-2); border-top: 1px solid var(--border); }

.lock-meter {
	display: flex;
	align-items: center;
	gap: 6px;
	padding: 5px 10px;
	margin-bottom: var(--s-2);
	border-radius: var(--r-md);
	background: var(--surface-2);
	color: var(--text-3);
	font-size: var(--fs-xs);
	font-variant-numeric: tabular-nums;
}
.lock-meter svg { width: 13px; height: 13px; }
.lock-meter.is-soon { background: var(--warning-soft); color: var(--warning); }

.account-btn {
	display: flex;
	align-items: center;
	gap: var(--s-2);
	width: 100%;
	padding: 5px 6px;
	border-radius: var(--r-md);
	transition: background var(--t-fast);
}
.account-btn:hover { background: var(--surface-2); }

.avatar {
	display: grid;
	place-items: center;
	width: 26px; height: 26px;
	flex: none;
	border-radius: var(--r-sm);
	background: var(--accent-soft);
	color: var(--accent-text);
	font-size: var(--fs-sm);
	font-weight: 600;
	text-transform: uppercase;
}

.account-email {
	flex: 1;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	color: var(--text-2);
	font-size: var(--fs-sm);
	text-align: left;
}
.account-caret { width: 14px; height: 14px; color: var(--text-3); flex: none; }

/* Collapsed: icons only. Labels are removed from the flow rather than hidden,
   so nothing can reflow or overflow at the narrow width.

   Gated to the widths that actually have a sidebar. Below 900px the sidebar is
   an off-canvas drawer at nearly full width, and a stored "collapsed"
   preference used to leak into it — a 248px drawer rendering as a centred icon
   rail with every label missing. The preference belongs to the desktop layout,
   so it only applies there. */
@media (min-width: 901px) {

.app.is-collapsed .brand-name,
.app.is-collapsed .nav-label,
.app.is-collapsed .nav-count,
.app.is-collapsed .sidebar-search span,
.app.is-collapsed .sidebar-search kbd,
.app.is-collapsed .account-email,
.app.is-collapsed .account-caret,
.app.is-collapsed .lock-meter span { display: none; }

.app.is-collapsed .sidebar { padding-inline: 10px; }

/* Collapsed, the head stacks: the mark, and under it the control that gets the
   sidebar back. It stacks because 48px of content width will not hold two
   buttons side by side — and the alternative, hiding the toggle, is what this
   rule used to do. That left the state with no way out of itself: the only
   control that could undo it was hidden by it, and the preference persists, so
   a reload didn't help either. */
.app.is-collapsed .sidebar-head {
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--s-1);
	height: auto;
	padding: 0;
}
.app.is-collapsed .sidebar-collapse { margin-left: 0; }

.app.is-collapsed .sidebar-search,
.app.is-collapsed .nav-item,
.app.is-collapsed .account-btn,
.app.is-collapsed .lock-meter { justify-content: center; padding-inline: 0; }
.app.is-collapsed .sidebar-collapse svg { transform: rotate(180deg); }

/* Collapsed items get their label back as a hover tooltip. */
.app.is-collapsed .nav-item::after {
	content: attr(data-label);
	position: absolute;
	left: calc(100% + 10px);
	padding: 4px 9px;
	background: var(--overlay);
	border: 1px solid var(--border);
	border-radius: var(--r-sm);
	box-shadow: var(--shadow-md);
	color: var(--text);
	font-size: var(--fs-sm);
	white-space: nowrap;
	opacity: 0;
	pointer-events: none;
	transform: translateX(-4px);
	transition: opacity var(--t-fast), transform var(--t-fast);
	z-index: 20;
}
.app.is-collapsed .nav-item:hover::after { opacity: 1; transform: none; }

}

.sidebar-scrim {
	position: fixed;
	inset: 0;
	z-index: 39;
	background: var(--scrim);
	animation: fade-in var(--t-base) var(--ease) forwards;
	opacity: 0;
}

/* ===== MAIN ===== */
.main { display: flex; flex-direction: column; min-width: 0; min-height: 0; }

/* ===== SAMPLE BANNER =====
   The public demo lives on the same domain as the vault itself, so it says so
   on every screen. Tinted rather than loud: it has to be unmissable without
   being the first thing your eye lands on. */
.sample-banner {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	flex: none;
	padding: 9px var(--s-5);
	background: var(--accent-soft);
	border-bottom: 1px solid var(--border);
	color: var(--accent-text);
	font-size: var(--fs-sm);
}
.sample-banner svg { width: 16px; height: 16px; flex: none; }
.sample-banner p { min-width: 0; flex: 1; }
.sample-banner strong { font-weight: 600; }

.sample-banner-link {
	display: inline-flex;
	align-items: center;
	gap: 2px;
	flex: none;
	color: inherit;
	font-weight: 500;
	text-decoration: none;
	white-space: nowrap;
}
.sample-banner-link:hover { text-decoration: underline; }
.sample-banner-link svg { width: 14px; height: 14px; }

@media (max-width: 700px) {
	.sample-banner { padding: 8px var(--s-3); font-size: var(--fs-xs); align-items: flex-start; }
	.sample-banner svg { margin-top: 2px; }
	/* On a phone the sentence is the message; the link is a tap away in the
	   account menu, and two lines of banner is one too many. */
	.sample-banner-link { display: none; }
}

.topbar {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	height: var(--topbar-h);
	flex: none;
	padding: 0 var(--s-5);
	border-bottom: 1px solid var(--border);
	background: var(--surface);
}

.topbar-menu, .topbar-search { display: none; flex: none; }

.topbar-title { min-width: 0; flex: 1 1 auto; }
/* The title is the one thing in the bar that may be shortened, and it shortens
   by ellipsis rather than by squeezing — a heading clipped to "V" is worse than
   no heading at all. */
.topbar-title h1 {
	font-size: var(--fs-lg);
	letter-spacing: -.015em;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.topbar-title p {
	font-size: var(--fs-xs);
	color: var(--text-3);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.topbar-title p:empty { display: none; }

.topbar-actions { display: flex; align-items: center; gap: var(--s-2); min-width: 0; }
.topbar-actions:empty { display: none; }

.view {
	flex: 1;
	min-height: 0;
	overflow-y: auto;
	overscroll-behavior-y: contain;
	scroll-behavior: smooth;
}

/* The standard content column. Sections that own their full frame (the file
   manager, the editors) opt out with .view-flush instead. */
.page { max-width: 1120px; padding: var(--s-6) var(--s-5) var(--s-12); margin: 0 auto; }
.page-narrow { max-width: 760px; }

/* A section that fills the viewport and manages its own internal scrolling. */
.view.is-flush { overflow: hidden; }
.pane { display: flex; height: 100%; min-height: 0; }

/* ===== SECTION ENTER =====
   A 6px lift on section change. Just enough to signal that the content is new,
   short enough that it never delays the interaction. */
.section-enter { animation: section-in var(--t-base) var(--ease); }
@keyframes section-in {
	from { opacity: 0; transform: translateY(6px); }
	to   { opacity: 1; transform: none; }
}

/* ===== MOBILE TAB BAR ===== */
.tabbar { display: none; }

/* ===== RESPONSIVE ===== */
@media (max-width: 900px) {
	.app, .app.is-collapsed { grid-template-columns: minmax(0, 1fr); }

	/* Off-canvas drawer. It stays in the DOM and slides, so opening it costs a
	   transform rather than a re-render of the whole navigation. */
	.sidebar {
		position: fixed;
		inset: 0 auto 0 0;
		z-index: 40;
		width: min(84vw, var(--sidebar-w));
		/* A notch or a rounded corner must not eat the first item of the
		   navigation, so the drawer pads itself out of the unsafe area. */
		padding-top: max(var(--s-3), env(safe-area-inset-top, 0px));
		padding-left: max(var(--s-3), env(safe-area-inset-left, 0px));
		padding-bottom: max(var(--s-3), env(safe-area-inset-bottom, 0px));
		transform: translateX(-100%);
		transition: transform var(--t-base) var(--ease);
		box-shadow: var(--shadow-lg);
	}
	.app.is-drawer-open .sidebar { transform: none; }

	/* Drawer rows are aimed at with a thumb, not a cursor. */
	.sidebar-head { height: 44px; }
	.sidebar-search { height: 42px; margin-bottom: var(--s-3); }
	.nav-item { height: 44px; gap: var(--s-3); }
	.nav-item svg { width: 19px; height: 19px; }
	.nav-item:active { background: var(--surface-3); }
	.account-btn { padding: var(--s-2) 6px; }

	.topbar-menu, .topbar-search { display: grid; }
	.topbar {
		padding-inline:
			max(var(--s-3), env(safe-area-inset-left, 0px))
			max(var(--s-3), env(safe-area-inset-right, 0px));
	}
	.page {
		padding: var(--s-5) var(--s-4) calc(var(--tabbar-h) + var(--s-6));
		padding-left: max(var(--s-4), env(safe-area-inset-left, 0px));
		padding-right: max(var(--s-4), env(safe-area-inset-right, 0px));
	}

	.tabbar {
		position: fixed;
		inset: auto 0 0 0;
		z-index: 30;
		display: grid;
		grid-auto-flow: column;
		grid-auto-columns: 1fr;
		height: var(--tabbar-h);
		padding-bottom: env(safe-area-inset-bottom, 0px);
		padding-left: env(safe-area-inset-left, 0px);
		padding-right: env(safe-area-inset-right, 0px);
		/* Opaque by default. The frosted version below is an enhancement, and it
		   only applies where the blur behind it actually renders — without the
		   blur, a partly transparent bar is not a material, it is the list
		   scrolling legibly through the labels. */
		background: var(--surface);
		border-top: 1px solid var(--border);
	}

	@supports ((backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px))) {
		.tabbar {
			/* Barely translucent. The bar sits on top of a scrolling list, and
			   the only thing it owes that list is a hint that it continues
			   underneath — not a readable second copy of it behind the labels. */
			background: color-mix(in srgb, var(--surface) 94%, transparent);
			-webkit-backdrop-filter: blur(20px) saturate(180%);
			backdrop-filter: blur(20px) saturate(180%);
		}
	}

	.tab {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		gap: 3px;
		color: var(--text-3);
		font-size: 10px;
		font-weight: 500;
		text-decoration: none;
		transition: color var(--t-fast), opacity var(--t-fast);
	}
	.tab svg { width: 22px; height: 22px; }
	.tab.is-active { color: var(--accent); }
	/* Nothing hovers on a phone, so the only feedback a tap can get is the press
	   itself. */
	.tab:active { opacity: .55; }

	/* The section fills what's left between the topbar and the tab bar. */
	.view.is-flush { padding-bottom: var(--tabbar-h); }
}

/* ===== NARROW HEADER =====
   One row will not hold a title and a section's whole toolbar at these widths —
   trying made the menu button slide off the left edge and the page title
   collapse to a single letter. So the bar becomes two rows: navigation and
   identity on top, the section's own controls on a full-width line beneath.
   A section with no controls keeps its single row, because .topbar-actions
   hides itself when empty.

   The breakpoint is the same 900px the rest of the shell uses. A search field,
   a switcher and two buttons come to about 560px of controls, which leaves a
   768px tablet roughly seventy pixels of title — so "the width where the
   sidebar becomes a drawer" and "the width where the bar needs two rows" are,
   in practice, the same width. */
@media (max-width: 900px) {
	.topbar {
		flex-wrap: wrap;
		height: auto;
		min-height: 56px;
		gap: var(--s-2);
		padding-block: var(--s-2);
	}

	.topbar-menu   { order: 1; }
	.topbar-title  { order: 2; }
	.topbar-search { order: 3; }

	.topbar-actions {
		order: 4;
		flex-basis: 100%;
		flex-wrap: wrap;
		gap: var(--s-2);
	}

	/* A section's own search filters the list below it, so it reads as part of
	   that list: full width, and above the controls that switch between views. */
	.topbar-actions > .search { order: -1; flex: 1 0 100%; max-width: none; }

	.topbar-actions > .btn, .topbar-actions > .icon-btn { flex: none; }

	/* The primary action sits on the far edge, under the thumb, and away from
	   everything that only switches the view. */
	.topbar-actions > .btn-primary:last-child { margin-left: auto; }

	/* The write / split / read switch belongs to a note, and at these widths the
	   list and the note are separate screens — so it only appears on the one it
	   can act on. */
	.main:has(.notes-pane:not(.is-detail)) .topbar-actions .mode-switch { display: none; }
}

/* On a phone a switcher of words spreads across whatever the buttons leave,
   which turns a cramped little control into a proper set of targets. It stops
   at the phone breakpoint: a tablet has room to spare, and stretching two words
   across six hundred pixels of it is not a control, it is a banner. One of
   icons never stretches at all — three glyphs spread over 250px read as three
   empty boxes, and an icon is no easier to hit for the field of white
   around it. */
@media (max-width: 700px) {
	.topbar-actions > .segmented:not(:has(svg)) { flex: 1 1 auto; min-width: 0; }
	.topbar-actions > .segmented:not(:has(svg)) button { flex: 1 1 auto; min-width: 0; padding-inline: var(--s-2); }
}

@media (max-width: 560px) {
	.topbar-title p { display: none; }
}
