/* Lazer Snippets — developer-first terminal theme.
   Light-mode only. Monospace everywhere. One loud purple accent. */

/* -------------------------------------------------------------------------- */
/* Design tokens                                                              */
/* -------------------------------------------------------------------------- */

:root {
	/* Surfaces */
	--color-bg: #fafafa;
	--color-surface: #ffffff;
	--color-surface-subtle: #f5f5f5;

	/* Text */
	--color-text: #111827;
	--color-text-secondary: #6b7280;
	--color-text-muted: #9ca3af;
	--color-text-inverse: #ffffff;

	/* Borders */
	--color-border: #e5e7eb;
	--color-border-strong: #d1d5db;

	/* Accent — electric purple, derived from the Lazer logo gradient */
	--color-accent: #a010c0;
	--color-accent-hover: #8a0ea6;
	--color-accent-active: #720c89;
	--color-accent-subtle: #f5ecf9;

	/* Semantic */
	--color-success: #059669;
	--color-success-bg: #d1fae5;
	--color-error: #dc2626;
	--color-error-bg: #fee2e2;
	--color-warning: #d97706;
	--color-warning-bg: #fef3c7;

	/* Typography */
	--font-mono:
		"JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco,
		Consolas, "Liberation Mono", "Courier New", monospace;
	--text-xs: 0.75rem; /* 12px */
	--text-sm: 0.8125rem; /* 13px */
	--text-base: 0.875rem; /* 14px */
	--text-md: 1rem; /* 16px */
	--text-lg: 1.125rem; /* 18px */
	--text-xl: 1.25rem; /* 20px */
	--text-2xl: 1.5rem; /* 24px */

	/* Spacing */
	--space-xs: 0.25rem;
	--space-sm: 0.5rem;
	--space-md: 0.75rem;
	--space-lg: 1rem;
	--space-xl: 1.5rem;
	--space-2xl: 2rem;
	--space-3xl: 3rem;

	/* Radii */
	--radius-sm: 2px;
	--radius-md: 4px;
	--radius-lg: 6px;
	--radius-pill: 9999px;

	/* Shadow — barely there, depth comes from borders */
	--shadow-sm: 0 1px 2px 0 rgba(17, 24, 39, 0.05);

	/* Layout */
	--header-height: 64px;
	--content-max-width: 1120px;
	--content-padding-x: 2rem;
}

/* -------------------------------------------------------------------------- */
/* Reset                                                                      */
/* -------------------------------------------------------------------------- */

*,
*::before,
*::after {
	box-sizing: border-box;
}

html,
body {
	margin: 0;
	padding: 0;
}

body {
	background: var(--color-bg);
	color: var(--color-text);
	font-family: var(--font-mono);
	font-size: var(--text-base);
	line-height: 1.5;
	font-feature-settings: "ss01", "cv11";
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	text-rendering: optimizeLegibility;
}

h1,
h2,
h3,
h4,
h5,
h6,
p {
	margin: 0;
}

a {
	color: var(--color-text);
	text-decoration: none;
}

a:not(.btn):not(.app-logo):hover {
	color: var(--color-accent);
}

button,
input,
select,
textarea {
	font: inherit;
	color: inherit;
}

code,
kbd,
pre,
samp {
	font-family: var(--font-mono);
}

hr {
	border: none;
	border-top: 1px solid var(--color-border);
	margin: 0;
}

/* -------------------------------------------------------------------------- */
/* Typography                                                                 */
/* -------------------------------------------------------------------------- */

.page-title {
	font-size: var(--text-2xl);
	font-weight: 600;
	letter-spacing: -0.01em;
	color: var(--color-text);
}

.page-subtitle {
	font-size: var(--text-sm);
	color: var(--color-text-secondary);
	margin-top: var(--space-sm);
}

.section-title {
	font-size: var(--text-xs);
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--color-text-secondary);
}

/* h2-equivalent — sits between .page-title (h1) and .section-title (label). */
.section-heading {
	font-size: var(--text-lg);
	font-weight: 600;
	letter-spacing: -0.01em;
	color: var(--color-text);
}

.text-muted {
	color: var(--color-text-muted);
}
.text-secondary {
	color: var(--color-text-secondary);
}
.text-accent {
	color: var(--color-accent);
}

.inline-code {
	font-family: var(--font-mono);
	font-size: 0.8125em;
	padding: 0.125rem 0.375rem;
	background: var(--color-accent-subtle);
	color: var(--color-accent);
	border-radius: var(--radius-sm);
}

/* URL bar — shown on the new-snippet success page. The code readout
   fills remaining space on the row and the Copy/Open buttons hug the
   right edge at the same 32px height as any other .btn. Long URLs
   scroll horizontally inside the readout rather than wrapping, so the
   row height stays stable. */
.url-bar {
	display: flex;
	align-items: center;
	gap: var(--space-sm);
}

.url-bar__url {
	flex: 1 1 auto;
	/* min-width: 0 lets the flex item shrink below its intrinsic content
	   width, which is what allows overflow-x to actually engage when the
	   URL is longer than the available row. */
	min-width: 0;
	height: 32px;
	display: flex;
	align-items: center;
	padding: 0 var(--space-lg);
	background: var(--color-surface-subtle);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-md);
	font-family: var(--font-mono);
	font-size: var(--text-sm);
	color: var(--color-text);
	overflow-x: auto;
	white-space: nowrap;
	user-select: all;
}

.copyable {
	display: inline-flex;
	align-items: center;
	gap: var(--space-sm);
	font-family: var(--font-mono);
	font-size: var(--text-sm);
	padding: var(--space-xs) var(--space-sm);
	background: var(--color-surface-subtle);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-md);
	cursor: copy;
}
.copyable:hover {
	border-color: var(--color-border-strong);
}

/* -------------------------------------------------------------------------- */
/* App header                                                                 */
/* -------------------------------------------------------------------------- */

.app-header {
	height: var(--header-height);
	background: var(--color-surface);
	border-bottom: 1px solid var(--color-border);
	position: sticky;
	top: 0;
	z-index: 50;
}

/* Opt-out modifier for pages (e.g. home) where the tall content scrolls
   past the header naturally and pinning it would just waste vertical
   space above the fold. */
.app-header--static {
	position: static;
}

.app-header__inner {
	max-width: var(--content-max-width);
	margin: 0 auto;
	padding: 0 var(--content-padding-x);
	height: 100%;
	display: flex;
	align-items: center;
	gap: var(--space-2xl);
}

.app-logo {
	display: inline-flex;
	align-items: center;
	gap: var(--space-md);
	font-size: var(--text-lg);
	font-weight: 700;
	letter-spacing: -0.01em;
	color: var(--color-text);
}

.app-logo:hover {
	color: var(--color-text);
}

.app-logo__mark {
	display: inline-block;
	width: 28px;
	height: 28px;
}

.app-header__user {
	display: flex;
	align-items: center;
	gap: var(--space-lg);
	margin-left: auto;
	font-size: var(--text-xs);
	color: var(--color-text-secondary);
}

.app-header__user form {
	display: flex;
	align-items: center;
	margin: 0;
}

/* -------------------------------------------------------------------------- */
/* Page layout                                                                */
/* -------------------------------------------------------------------------- */

.page-content {
	max-width: var(--content-max-width);
	margin: 0 auto;
	padding: var(--space-2xl) var(--content-padding-x) var(--space-3xl);
}

.page-header {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--space-lg);
	margin-bottom: var(--space-2xl);
}

/* Breadcrumb / back-link row that sits above a `.page-header`. Thin
   container so the ghost-button back link reads as navigation chrome,
   not a page-level action. Margin is tight on purpose — the visual
   grouping should be "back link + title", not two disconnected rows. */
.page-breadcrumb {
	margin-bottom: var(--space-lg);
}

/* In-page section header — heading + action button row. */
.section-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-lg);
	margin-bottom: var(--space-lg);
}

.stack {
	display: flex;
	flex-direction: column;
	gap: var(--space-lg);
}
.stack-xl {
	display: flex;
	flex-direction: column;
	gap: var(--space-xl);
}

.row {
	display: flex;
	align-items: center;
	gap: var(--space-lg);
}

.grid-3 {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: var(--space-lg);
}

@media (max-width: 768px) {
	.grid-3 {
		grid-template-columns: 1fr;
	}
	.app-header__inner {
		gap: var(--space-lg);
	}
	/* Activity section header stacks on narrow viewports so the search
	   form wraps under the heading instead of overflowing the row. */
	.section-header {
		flex-wrap: wrap;
	}
	.activity-search__input {
		width: 100%;
		min-width: 0;
	}
	.activity-search {
		flex: 1 1 100%;
	}
	/* Pagination collapses its three-part row onto two lines so "Page
	   X of Y" doesn't elbow Prev/Next off-screen. */
	.pagination {
		flex-direction: column;
		align-items: stretch;
		gap: var(--space-sm);
	}
	.pagination__controls {
		justify-content: space-between;
	}
}

/* -------------------------------------------------------------------------- */
/* Cards                                                                      */
/* -------------------------------------------------------------------------- */

.card {
	background: var(--color-surface);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-sm);
	padding: var(--space-xl);
}

.card__header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-lg);
	margin-bottom: var(--space-lg);
}

.card__title {
	font-size: var(--text-base);
	font-weight: 600;
}

.card__body {
	font-size: var(--text-sm);
}

.stat-card {
	background: var(--color-surface);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-sm);
	padding: var(--space-lg) var(--space-xl);
}

.stat-card__label {
	font-size: var(--text-xs);
	font-weight: 500;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--color-text-secondary);
	margin-bottom: var(--space-sm);
}

.stat-card__value {
	font-size: var(--text-xl);
	font-weight: 600;
	font-variant-numeric: tabular-nums;
	color: var(--color-text);
}

.stat-card__delta {
	font-size: var(--text-xs);
	color: var(--color-text-secondary);
	margin-top: var(--space-xs);
}

/* Trigger button (or form wrapping one) rendered at the bottom of a
   stat-card. Pairs the control with the value it affects — used on the
   admin page to put Backup DB / Clean Snippets directly under their
   "Last run" timestamp. Plain top margin so the button breathes away
   from the large `.stat-card__value` above it. */
.stat-card__action {
	margin-top: var(--space-lg);
}

/* Horizontal stat-card variant: label+value stacked on the left, action
   on the right. Used on the admin page's meta strip so the cards read
   as wide status tiles instead of tall portraits. The `flex: 1` pairs
   with `.row` on the parent so two cards split the available width
   evenly. Tighter vertical padding because a single line of content
   doesn't need the taller portrait padding. */
.stat-card--inline {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-lg);
	flex: 1;
	padding: var(--space-md) var(--space-xl);
}

.stat-card--inline .stat-card__label {
	margin-bottom: var(--space-xs);
}

/* Action sits on the right of an inline card, not below, so the top
   margin from the default `.stat-card__action` would push it off-center. */
.stat-card--inline .stat-card__action {
	margin-top: 0;
}

/* -------------------------------------------------------------------------- */
/* Buttons                                                                    */
/* -------------------------------------------------------------------------- */

.btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-sm);
	height: 32px;
	padding: 0 var(--space-lg);
	font-family: var(--font-mono);
	font-size: var(--text-xs);
	font-weight: 500;
	letter-spacing: 0.02em;
	border: 1px solid transparent;
	border-radius: var(--radius-md);
	background: transparent;
	color: var(--color-text);
	cursor: pointer;
	white-space: nowrap;
	transition:
		background 120ms ease,
		border-color 120ms ease,
		color 120ms ease;
}

.btn:focus-visible {
	outline: none;
	border-color: var(--color-accent);
	box-shadow: 0 0 0 3px var(--color-accent-subtle);
}

.btn:disabled {
	opacity: 0.5;
	cursor: not-allowed;
}

.btn--primary {
	background: var(--color-accent);
	color: var(--color-text-inverse);
}
.btn--primary:hover {
	background: var(--color-accent-hover);
}
.btn--primary:active {
	background: var(--color-accent-active);
}

.btn--secondary {
	background: var(--color-surface);
	border-color: var(--color-border-strong);
	color: var(--color-text);
}
.btn--secondary:hover {
	background: var(--color-bg);
	border-color: var(--color-text-muted);
}

.btn--ghost {
	color: var(--color-text-secondary);
}
.btn--ghost:hover {
	color: var(--color-accent);
	background: var(--color-accent-subtle);
}

.btn--danger {
	background: var(--color-surface);
	border-color: var(--color-border-strong);
	color: var(--color-error);
}
.btn--danger:hover {
	background: var(--color-error-bg);
	border-color: var(--color-error);
}

/* Compact button — used by the snippets-table row actions (Edit / Expire /
   Remove) so three buttons fit comfortably inside a single table cell
   without wrapping. Height drops to 24px and the horizontal padding shrinks
   to `--space-sm` so the label still has breathing room. */
.btn--sm {
	height: 24px;
	padding: 0 var(--space-sm);
	font-size: var(--text-xs);
}

/* -------------------------------------------------------------------------- */
/* Inputs                                                                     */
/* -------------------------------------------------------------------------- */

.input,
.textarea,
.select {
	display: block;
	width: 100%;
	height: 32px;
	padding: 0 var(--space-md);
	font-family: var(--font-mono);
	font-size: var(--text-sm);
	color: var(--color-text);
	background: var(--color-surface);
	border: 1px solid var(--color-border-strong);
	border-radius: var(--radius-md);
	transition:
		border-color 120ms ease,
		box-shadow 120ms ease;
}

.textarea {
	height: auto;
	padding: var(--space-sm) var(--space-md);
	min-height: 80px;
	resize: vertical;
}

/* Content textarea on the new-snippet form — the body of a snippet is
   the primary thing the user types, so give it room without forcing the
   page to scroll on short screens. */
.textarea--tall {
	min-height: 240px;
}

.input:focus,
.textarea:focus,
.select:focus {
	outline: none;
	border-color: var(--color-accent);
	box-shadow: 0 0 0 3px var(--color-accent-subtle);
}

.input::placeholder,
.textarea::placeholder {
	color: var(--color-text-muted);
}

.label {
	display: block;
	font-size: var(--text-xs);
	font-weight: 500;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--color-text-secondary);
	margin-bottom: var(--space-sm);
}

/* -------------------------------------------------------------------------- */
/* Forms                                                                      */
/* -------------------------------------------------------------------------- */

/* A form is a narrow vertical stack of fields. Max-width keeps line
   lengths readable even on wide viewports; the .page-content wrapper
   handles the outer padding. */
.form {
	display: flex;
	flex-direction: column;
	gap: var(--space-xl);
	max-width: 640px;
}

.form__field {
	display: flex;
	flex-direction: column;
	gap: var(--space-sm);
}

/* The class's `display: flex` would otherwise beat the UA `[hidden]`
   rule on specificity — re-assert `display: none` so toggling the
   `hidden` attribute actually hides the field. */
.form__field[hidden] {
	display: none;
}

/* Label spacing inside .form__field is owned by the field's gap, not
   the label's own margin — otherwise the spacing is doubled. */
.form__field > .label {
	margin-bottom: 0;
}

.form__error {
	padding: var(--space-md) var(--space-lg);
	background: var(--color-error-bg);
	color: var(--color-error);
	border: 1px solid var(--color-error);
	border-radius: var(--radius-md);
	font-size: var(--text-sm);
}

.form__actions {
	display: flex;
	align-items: center;
	gap: var(--space-md);
}

/* Same specificity fix as `.form__field[hidden]` above: the class's
   `display: flex` would otherwise outweigh the UA `[hidden]` rule, so
   toggling the attribute from JS would be a no-op. Used by the
   new-snippet form's Format button row. */
.form__actions[hidden] {
	display: none;
}

/* Edit page expiry + extend section. Sits at the top of the page above
   a divider, separate from the main edit form below. Uses the same
   max-width as `.form` so the two blocks align vertically on the page.
   Each extend preset is its own <form> so clicks bypass the edit form
   entirely and never interfere with unsaved edits. */
.edit-expiry-section {
	display: flex;
	flex-direction: column;
	gap: var(--space-sm);
	max-width: 640px;
}

.edit-expiry__value {
	margin: 0;
	font-size: var(--text-sm);
	color: var(--color-text);
}

.edit-expiry__actions {
	display: flex;
	flex-wrap: nowrap;
	gap: var(--space-sm);
	margin-top: var(--space-sm);
}

/* Each extend preset <form> would otherwise default to block layout
   and push the buttons onto separate lines. Strip that so the four
   forms sit inline inside `.edit-expiry__actions`. */
.edit-expiry__form {
	display: inline-flex;
	margin: 0;
}

.edit-expiry__status {
	margin-top: var(--space-xs);
	font-size: var(--text-xs);
}

/* Divider between the expiry section and the edit form. Matches the
   `.form`'s max-width so it visually belongs to the same column. */
.edit-divider {
	max-width: 640px;
	margin: var(--space-xl) 0;
}

/* Error-color helper paired with the edit page's expiry readout (past-
   due snippets) and the status flash in edit-snippet.js on a failed
   extend. Factored out of `.form__error` so it can be used standalone
   on a <span>. */
.text-error {
	color: var(--color-error);
}

/* Key/value list — used by the custom-headers editor on the new-snippet
   form. Each row is a three-column grid so the key and value inputs share
   space evenly and the remove button hugs the right edge. */
.kv-list {
	display: flex;
	flex-direction: column;
	gap: var(--space-sm);
}

.kv-row {
	display: grid;
	grid-template-columns: 1fr 1fr auto;
	gap: var(--space-sm);
	align-items: center;
}

/* -------------------------------------------------------------------------- */
/* Tables — the workhorse                                                     */
/* -------------------------------------------------------------------------- */

.table {
	width: 100%;
	border-collapse: collapse;
	background: var(--color-surface);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-lg);
	overflow: hidden;
	font-size: var(--text-sm);
}

/* Horizontal-scroll wrapper for tables whose intrinsic width may
   exceed the content column — used by the admin console, where the
   Owner column + standard columns push past `--content-max-width`.
   The inner `.table` keeps `width: 100%` so it fills the wrapper when
   the content fits (activity table, narrower), and `min-width: max-
   content` forces it wider — triggering horizontal scroll on the
   wrapper — when it doesn't (snippets table, wider). Header cells
   already use `white-space: nowrap`, so `max-content` reflects the
   real minimum the table needs to render without column squish. */
.table-scroll {
	width: 100%;
	overflow-x: auto;
}

.table-scroll > .table {
	width: 100%;
	min-width: max-content;
}

.table thead th {
	text-align: left;
	padding: var(--space-md) var(--space-lg);
	font-size: var(--text-xs);
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--color-text-secondary);
	background: var(--color-surface-subtle);
	border-bottom: 1px solid var(--color-border);
	white-space: nowrap;
}

.table thead th.is-sortable {
	cursor: pointer;
	user-select: none;
}
.table thead th.is-sortable:hover {
	color: var(--color-text);
}

.table thead th .sort-caret {
	display: inline-block;
	margin-left: var(--space-xs);
	color: var(--color-accent);
	font-size: var(--text-xs);
}

.table tbody td {
	padding: var(--space-md) var(--space-lg);
	border-bottom: 1px solid var(--color-border);
	color: var(--color-text);
	font-variant-numeric: tabular-nums;
	vertical-align: middle;
}

.table tbody tr:last-child td {
	border-bottom: none;
}

.table tbody tr:hover td {
	background: var(--color-bg);
}

/* Expired snippets on the home-page table — strike through and mute the
   data cells so the row reads as "no longer serving" at a glance. The
   last cell (Actions) is left alone so Edit / Extend / Remove stay
   legible and the extend buttons visually pop. */
.table tbody tr.is-expired td:not(:last-child) {
	text-decoration: line-through;
	color: var(--color-text-muted);
}

/* URL cell on the snippets table — path readout plus two inline action
   icons (copy + open-in-new-tab). Flex row so the icons sit right next
   to the code span without inheriting the cell's block flow. */
.url-cell {
	display: inline-flex;
	align-items: center;
	gap: var(--space-xs);
}

/* Row actions cell on the snippets table — holds the Edit / Expire /
   Remove buttons for a single snippet row. Each Expire/Remove button is
   wrapped in its own POST <form>, so we target direct form children to
   strip their default block layout and let them sit inline next to the
   standalone Edit button. */
.row-actions {
	display: inline-flex;
	align-items: center;
	gap: var(--space-xs);
}

.row-actions > form {
	display: inline-flex;
	margin: 0;
}

/* Icon button — square, borderless, scaled to sit comfortably inside a
   32px-row table cell. SVG children inherit color via currentColor so
   hover/focus recolors the glyph without swapping the node. `is-copied`
   is toggled by public/home.js for ~1.5s after a successful copy. */
.icon-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 14px;
	height: 14px;
	padding: 0;
	border: 1px solid transparent;
	border-radius: var(--radius-sm);
	background: transparent;
	color: var(--color-text-secondary);
	cursor: pointer;
	transition:
		background 120ms ease,
		border-color 120ms ease,
		color 120ms ease;
}

.icon-btn:hover {
	color: var(--color-accent);
	background: var(--color-accent-subtle);
}

.icon-btn:focus-visible {
	outline: none;
	border-color: var(--color-accent);
	box-shadow: 0 0 0 3px var(--color-accent-subtle);
}

.icon-btn svg {
	width: 10px;
	height: 10px;
}

.icon-btn.is-copied {
	color: var(--color-success);
	background: var(--color-success-bg);
}

/* User-agent cell — UAs run 100+ chars and would blow out column widths.
   Cap the width and break long tokens in place so the rest of the row stays
   legible without forcing JS-side truncation. */
.table td.cell-ua {
	max-width: 320px;
	font-size: var(--text-xs);
	color: var(--color-text-secondary);
	word-break: break-all;
}

/* Activity section search — lives inside .section-header on the right
   side opposite the heading. The input owns the flexible width, the
   submit/clear buttons hug the right edge. No max-width on the form
   itself so it collapses gracefully on narrow viewports. */
.activity-search {
	display: flex;
	align-items: center;
	gap: var(--space-sm);
	margin: 0;
}

.activity-search__input {
	width: 240px;
}

/* Screen-reader-only label. Visually hidden but discoverable to AT so
   the activity search input still has an accessible name even though
   its placeholder carries the intent visually. */
.visually-hidden {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* Pagination footer — row under a table with a left-aligned "X–Y of Z"
   range, prev/next buttons, and a "Page N of M" readout between them.
   Buttons are real anchors so middle-click opens in a new tab; the
   disabled edge renders as a .btn--secondary span with .is-disabled so
   the row height stays stable without a fake affordance. */
.pagination {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-lg);
	margin-top: var(--space-lg);
	font-size: var(--text-xs);
	color: var(--color-text-secondary);
}

.pagination__summary {
	font-variant-numeric: tabular-nums;
}

.pagination__controls {
	display: flex;
	align-items: center;
	gap: var(--space-sm);
}

.pagination__page {
	font-variant-numeric: tabular-nums;
	color: var(--color-text-secondary);
	padding: 0 var(--space-sm);
}

/* Disabled pagination edge — rendered as a <span> with button styling
   but no interactivity. Matches the .btn:disabled look so prev/next
   visually agree with other disabled buttons in the app. */
.btn.is-disabled {
	opacity: 0.5;
	cursor: not-allowed;
	pointer-events: none;
}

/* Empty state — placeholder shown when a table has no rows. Dashed border
   distinguishes it from a real surface so users read it as "nothing here yet". */
.empty-state {
	background: var(--color-surface);
	border: 1px dashed var(--color-border-strong);
	border-radius: var(--radius-lg);
	padding: var(--space-3xl) var(--space-xl);
	text-align: center;
	color: var(--color-text-secondary);
}

.empty-state p {
	margin-top: var(--space-xs);
}

.empty-state p:first-child {
	margin-top: 0;
	color: var(--color-text);
}

/* -------------------------------------------------------------------------- */
/* Badges                                                                     */
/* -------------------------------------------------------------------------- */

.badge {
	display: inline-flex;
	align-items: center;
	gap: var(--space-xs);
	padding: 0.125rem var(--space-sm);
	font-size: var(--text-xs);
	font-weight: 500;
	border-radius: var(--radius-pill);
	background: var(--color-border);
	color: var(--color-text-secondary);
	line-height: 1.4;
}

.badge--accent {
	background: var(--color-accent-subtle);
	color: var(--color-accent);
}
.badge--success {
	background: var(--color-success-bg);
	color: var(--color-success);
}
.badge--error {
	background: var(--color-error-bg);
	color: var(--color-error);
}
.badge--warning {
	background: var(--color-warning-bg);
	color: var(--color-warning);
}

/* -------------------------------------------------------------------------- */
/* Budget bar (progress)                                                      */
/* -------------------------------------------------------------------------- */

.budget-bar {
	width: 100%;
	height: 8px;
	background: var(--color-border);
	border-radius: var(--radius-pill);
	overflow: hidden;
}

.budget-bar__fill {
	height: 100%;
	background: var(--color-accent);
	border-radius: var(--radius-pill);
	transition: width 240ms ease;
}

/* -------------------------------------------------------------------------- */
/* Landing (sign-in)                                                          */
/* -------------------------------------------------------------------------- */

.landing {
	min-height: 100vh;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	padding: var(--space-2xl);
}

.landing__logo {
	width: 60px;
	height: 60px;
	margin-bottom: var(--space-xl);
}

.landing__title {
	font-size: var(--text-lg);
	font-weight: 500;
	color: var(--color-text);
	margin-bottom: var(--space-xl);
}

.landing__button {
	height: 40px;
	padding: 0 var(--space-2xl);
	font-size: var(--text-base);
}
