/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css, which the CSS linter cannot resolve */
.sd-state {
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	gap: var(--space-2);
	padding: var(--space-8) var(--space-4);
	/* 420px design width at the default text size, in rem so it widens with the text size. */
	max-width: 26.25rem;
	margin: 0 auto;
}

.sd-state-icon {
	/* 48px design size at the default text size, in rem so it follows the text size. */
	width: 3rem;
	height: 3rem;
	border-radius: 50%;
	display: grid;
	place-items: center;
	background: var(--surface-sunken);
	color: var(--ink-muted);
	margin-bottom: var(--space-2);
}

.sd-state-error .sd-state-icon {
	background: var(--danger-soft);
	color: var(--danger);
}

.sd-state-offline .sd-state-icon {
	background: var(--sub-soft);
	color: var(--sub);
}

.sd-state-kind {
	font-size: 0.875rem;
	font-weight: 700;
	color: var(--danger);
}

/* Its size comes from the shared title classes (theme/titles.css); the container's gap spaces it. */
.sd-state .sd-state-title {
	margin: 0;
}

.sd-state .sd-caption {
	font-size: 0.875rem;
	line-height: 1.25rem;
	color: var(--ink-muted);
	margin: 0;
}

.sd-state .ant-btn {
	margin-top: var(--space-2);
}

/* The spinner glyph, in rem so it follows the text size like the text around it: 16px, 32px and 48px at the default
   size. Qualified by Ant Design's `.ant-spin .ant-spin-dot` (which sets the dot's font size, 26px) so these win. */
.ant-spin .ant-spin-dot.sd-app-spin-small {
	font-size: 1rem;
}

.ant-spin .ant-spin-dot.sd-app-spin-default {
	font-size: 2rem;
}

.ant-spin .ant-spin-dot.sd-app-spin-large {
	font-size: 3rem;
}

/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css, which the CSS linter cannot resolve */
.sd-icon {
	flex: none;
	display: inline-block;
	vertical-align: middle;
}

.sd-powered {
	display: inline-flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: center;
	gap: var(--space-2);
	font-size: var(--type-caption-size);
	line-height: var(--type-caption-line);
	color: var(--ink-muted);
}

/* The mark follows the caption's text size (1rem at the default); Mark's width/height attributes are only its fallback. */
.sd-powered .sd-mark {
	width: 1rem;
	height: 1rem;
}

.sd-powered b {
	color: var(--ink);
	font-weight: 700;
}

/* One green per logo: the wordmark is brand-pine, never the theme's pine, which turns mint in the dark theme. */
.sd-wordmark {
	display: block;
	fill: var(--brand-pine);
	flex: none;
}

[data-theme='dark'] .sd-wordmark {
	fill: var(--ink);
}

.sd-wordmark-ink {
	fill: var(--ink);
}

/* The gap between the mark and the wordmark is 0.3x the wordmark height, rounded to the nearest spacing token:
   20px (6, half-way between --space-1 and --space-2, rounded half up), 24px (7.2) and 32px (9.6) take --space-2 (8),
   48px (14.4) takes --space-4 (16). At 20px that is 0.4x, which the rounding rule accepts. */
.sd-lockup {
	display: inline-flex;
	align-items: center;
}

.sd-lockup-md {
	gap: var(--space-2);
}

.sd-lockup-lg {
	gap: var(--space-2);
}

.sd-lockup-xl {
	gap: var(--space-2);
}

.sd-lockup-xxl {
	gap: var(--space-4);
}

/* The mark is the same in both themes: a brand-pine tile, white ink and a harvest tin. `--on-pine` cannot be the ink,
   because it turns dark in the dark theme where pine turns mint; the white is the one deliberate fixed colour. */
.sd-mark-tile {
	fill: var(--brand-pine);
}

.sd-mark-ink {
	fill: #fff;
}

.sd-mark-tin {
	fill: var(--harvest);
}

/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css, which the CSS linter cannot resolve */
/* Caption size (the design's 13px has no token; caption is the nearest, 14px), ink-muted, an underlined bold link. */
.sd-legal {
	margin: 0;
	font-size: var(--type-caption-size);
	line-height: var(--type-caption-line);
	color: var(--ink-muted);
	text-align: center;
}

.sd-legal a {
	color: inherit;
	font-weight: 700;
	text-decoration: underline;
}

/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css, which the CSS linter cannot resolve */
/* Off-screen until focused, then top-left in ink on a raised plate with the standard focus ring (global.css). */
.sd-skip-link {
	position: absolute;
	inset-inline-start: var(--space-2);
	top: var(--space-2);
	z-index: 1100;
	box-sizing: border-box;
	padding: var(--space-2) var(--space-3);
	border-radius: var(--radius-md);
	background: var(--surface-raised);
	color: var(--ink);
	font-weight: 700;
	transform: translateY(-200%);
}

.sd-skip-link:focus {
	transform: none;
}

/* The skip link's target takes focus without drawing a ring around the whole column: one rule for every layout
 * (public pages, shopper shell, staff portal), so none of them carries its own copy. */
main[id='main']:focus {
	outline: none;
}

/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css, which the CSS linter cannot resolve */

/* The three-way theme choice: a segmented control whose options are touch targets. The chosen option is raised
   (surface, a 1px line, shadow and bold), never told by colour alone; the line also keeps it apart from the track in
   Dark, where the raised and sunken surfaces are close. */
.sd-theme-choice {
	display: flex;
	flex-direction: column;
	gap: var(--space-1);
	min-width: 0;
}

.sd-theme-choice-label {
	font-size: var(--type-label-size);
	line-height: var(--type-label-line);
	font-weight: 700;
	color: var(--ink);
}

.sd-theme-choice-options {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-1);
	box-sizing: border-box;
	padding: var(--space-1);
	border-radius: var(--radius-md);
	background: var(--surface-sunken);
}

.sd-theme-choice-option {
	flex: 1 1 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-2);
	box-sizing: border-box;
	min-width: var(--touch-min);
	min-height: var(--touch-min);
	padding: 0 var(--space-3);
	/* Transparent until chosen, so the chosen option's line does not move anything. */
	border: var(--sd-line-width) solid transparent;
	border-radius: var(--radius-sm);
	background: transparent;
	color: var(--ink);
	font: inherit;
	cursor: pointer;
}

.sd-theme-choice-option:hover {
	background: var(--surface);
}

.sd-theme-choice-option[aria-checked='true'] {
	background: var(--surface-raised);
	border-color: var(--line);
	box-shadow: var(--shadow-float);
	font-weight: 700;
}

.sd-theme-choice-compact .sd-theme-choice-option {
	flex: 0 0 auto;
	padding: 0;
}

/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css and Ant Design's theme variables (--sd-*), which the CSS linter cannot resolve */

/* The public pages (PublicChrome and its parts). Colours are the design tokens (tokens.css); sizes are Ant Design's
   theme variables (--sd-*), which remTokens.ts writes in rem, so every space and type size follows the person's text
   size. The column widths are in rem for the same reason. */

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

.sd-pub {
	min-height: 100dvh;
	display: flex;
	flex-direction: column;
	background: var(--surface);
	color: var(--ink);
	font-size: var(--sd-font-size);
	line-height: var(--sd-line-height);
}

.sd-pub-bar {
	border-color: var(--line);
	border-style: solid;
	border-width: 0;
}

.sd-pub-header {
	border-bottom-width: var(--sd-line-width);
}

.sd-pub-footer {
	border-top-width: var(--sd-line-width);
	color: var(--ink-muted);
	font-size: var(--sd-font-size-sm);
	line-height: var(--sd-line-height-sm);
}

.sd-pub-bar-inner {
	/* 1120px at the default text size: the design's widest column. */
	max-width: 70rem;
	margin: 0 auto;
	padding: var(--sd-padding-sm) var(--sd-padding-lg);
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--sd-padding-xs);
}

/* The legal line takes a row of its own under the © line and the theme choice, so at 375px and 200% text neither
   crowds it. */
.sd-pub-legal {
	flex-basis: 100%;
}

.sd-pub-home {
	display: inline-flex;
	align-items: center;
	min-height: var(--sd-control-height);
}

/* An address the person typed is one long word: at 375px and 200% text it ran past the screen. Any word may break
   rather than widen the page (WCAG 1.4.10). */
.sd-pub-main {
	overflow-wrap: anywhere;
	flex-grow: 1;
	width: 100%;
	margin: 0 auto;
	padding: var(--sd-padding-xl) var(--sd-padding-lg);
	display: flex;
	flex-direction: column;
	gap: var(--sd-padding-lg);
}

/* One column of reading: 720px at the default text size. */
.sd-pub-main-narrow {
	max-width: 45rem;
}

.sd-pub-main-wide {
	max-width: 70rem;
	flex-direction: row;
	flex-wrap: wrap;
	align-items: center;
	column-gap: var(--sd-margin-xxl);
	row-gap: var(--sd-margin-xxl);
}

@media (min-width: 48em) {
	.sd-pub-main {
		padding-block: var(--sd-margin-xxl);
	}
}

/* A phone gets the 16px gutter the rest of the app uses, so 200% text still leaves room for a word. */
@media (max-width: 30em) {
	.sd-pub-main,
	.sd-pub-bar-inner {
		padding-inline: var(--sd-padding);
	}
}

/* Columns of the wide layout. The basis is in em so the columns stack sooner as the text grows. */
.sd-pub-column {
	flex: 1 1 26em;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: var(--sd-padding-lg);
}

.sd-pub-column-aside {
	flex-basis: 20em;
	gap: var(--sd-padding);
}

.sd-pub-stack {
	display: flex;
	flex-direction: column;
	gap: var(--sd-padding);
}

/* Type */
.sd-pub-h1 {
	margin: 0;
	font-size: var(--sd-font-size-heading-1);
	line-height: var(--sd-line-height-heading-1);
	font-weight: 700;
	letter-spacing: -0.01em;
	color: var(--ink);
}

.sd-pub-h2 {
	margin: 0;
	font-size: var(--sd-font-size-heading-3);
	line-height: var(--sd-line-height-heading-3);
	font-weight: 700;
}

.sd-pub-lead {
	margin: 0;
	max-width: 34em;
	font-size: var(--sd-font-size-lg);
	line-height: var(--sd-line-height-lg);
}

.sd-pub-p {
	margin: 0;
}

.sd-pub-caption {
	margin: 0;
	color: var(--ink-muted);
	font-size: var(--sd-font-size-sm);
	line-height: var(--sd-line-height-sm);
}

.sd-pub-fine {
	margin: 0;
	padding-top: var(--sd-padding-lg);
	border-top: var(--sd-line-width) solid var(--line);
}

.sd-pub a:not(.ant-btn) {
	color: var(--pine);
	font-weight: 700;
}

.sd-pub a:not(.ant-btn):hover {
	color: var(--pine-hover);
}

/* A link that stands alone gets the touch target, not only its line of text. */
.sd-pub-link {
	align-self: flex-start;
	min-height: var(--sd-control-height);
	display: inline-flex;
	align-items: center;
	gap: var(--sd-padding-xxs);
	text-decoration: none;
}

/* Status banner */
.sd-pub-banner {
	display: flex;
	align-items: center;
	gap: var(--sd-padding-xs);
	padding: var(--sd-padding-sm) var(--sd-padding);
	border-radius: var(--sd-border-radius);
	color: var(--ink);
	font-size: var(--sd-font-size-sm);
	line-height: var(--sd-line-height-sm);
	font-weight: 700;
}

.sd-pub-banner-compact {
	align-self: flex-start;
	padding: var(--sd-padding-xxs) var(--sd-padding-sm);
	border-radius: var(--sd-border-radius-sm);
}

.sd-pub-banner-pine {
	background: var(--pine-soft);
}

.sd-pub-banner-pine .sd-pub-banner-icon {
	color: var(--pine);
}

.sd-pub-banner-sub {
	background: var(--sub-soft);
}

.sd-pub-banner-sub .sd-pub-banner-icon {
	color: var(--sub);
}

.sd-pub-banner-danger {
	background: var(--danger-soft);
}

.sd-pub-banner-danger .sd-pub-banner-icon {
	color: var(--danger);
}

.sd-pub-banner-icon {
	flex: none;
}

/* Numbered steps */
.sd-pub-steps {
	margin: 0;
	padding: 0;
	list-style: none;
	display: flex;
	flex-direction: column;
	gap: var(--sd-padding);
}

.sd-pub-steps li {
	display: flex;
	gap: var(--sd-padding-sm);
}

.sd-pub-step-number {
	flex: none;
	width: var(--sd-control-height-sm);
	height: var(--sd-control-height-sm);
	border-radius: var(--sd-border-radius);
	background: var(--pine-soft);
	color: var(--pine);
	display: flex;
	align-items: center;
	justify-content: center;
	font-family: var(--font-mono);
	font-weight: 700;
}

/* Check list */
.sd-pub-checks {
	margin: 0;
	padding: 0;
	list-style: none;
	display: flex;
	flex-direction: column;
	gap: var(--sd-padding-xs);
}

.sd-pub-checks li {
	display: flex;
	gap: var(--sd-padding-sm);
}

.sd-pub-check-icon {
	flex: none;
	margin-top: var(--sd-padding-xxs);
	color: var(--pine);
}

.sd-pub-checks-boxed {
	gap: 0;
	padding: var(--sd-padding-xs) var(--sd-padding-lg);
	border: var(--sd-line-width) solid var(--line);
	border-radius: var(--sd-border-radius-lg);
	background: var(--surface-raised);
}

.sd-pub-checks-boxed li {
	padding: var(--sd-padding) 0;
}

.sd-pub-checks-boxed li + li {
	border-top: var(--sd-line-width) solid var(--line);
}

.sd-pub-checks-boxed .sd-pub-check-icon {
	color: var(--ink-muted);
}

/* Card section */
.sd-pub-card {
	padding: var(--sd-padding-lg);
	border: var(--sd-line-width) solid var(--line);
	border-radius: var(--sd-border-radius-lg);
	display: flex;
	flex-direction: column;
	gap: var(--sd-padding-xs);
}

.sd-pub-card-raised {
	background: var(--surface-raised);
}

.sd-pub-card-section {
	gap: var(--sd-padding);
}

.sd-pub-card-title {
	margin: 0;
	font-size: var(--sd-font-size-heading-4);
	line-height: var(--sd-line-height-heading-4);
	font-weight: 700;
}

.sd-pub-card-section .sd-pub-card-title {
	font-size: var(--sd-font-size-heading-3);
	line-height: var(--sd-line-height-heading-3);
}

.sd-pub-card p {
	margin: 0;
}

.sd-pub-card-link {
	align-self: flex-start;
	min-height: var(--sd-control-height);
	display: inline-flex;
	align-items: center;
}

/* Two cards side by side where they fit (Closed beta). */
.sd-pub-cards {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 17.5em), 1fr));
	gap: var(--sd-padding);
}

/* Forms: fields side by side where they fit, the action and its note on one line where they fit. */
.sd-pub-fields {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 15em), 1fr));
	gap: var(--sd-padding);
	align-items: start;
}

.sd-pub-fields-wide {
	grid-column: 1 / -1;
}

.sd-pub-actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--sd-padding);
}

/* The waitlist's one-line form: the field grows, the button keeps its words on one line where it can. */
.sd-pub-inline-form {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	gap: var(--sd-padding-xs);
	max-width: 35em;
}

.sd-pub-inline-form > .ant-form-item {
	flex: 1 1 16em;
}

.sd-pub-inline-form > .ant-btn {
	flex: 0 1 auto;
}

/* Errors under a field come from antd; a form-level one sits in the flow like this. */
.sd-pub-error {
	margin: 0;
	color: var(--danger);
}

/* Override: Ant Design keeps a button's label on one line at a fixed height. On a phone at 200% text a public page's
   buttons ("Send to Sundries", "Back to sundries.ca") are wider than the screen, so they wrap like the block buttons in
   global.css do, keeping each size's height as the minimum (WCAG 1.4.4 and 1.4.10). */
.sd-pub .ant-btn {
	max-width: 100%;
	white-space: normal;
	height: auto;
	min-height: var(--sd-control-height);
	padding-block: var(--sd-padding-xxs);
}

.sd-pub .ant-btn.ant-btn-lg {
	min-height: var(--sd-control-height-lg);
}

/* A phone: cards and boxed lists take the 16px gutter inside too (after their own rules, so this wins). */
@media (max-width: 30em) {
	.sd-pub-card,
	.sd-pub-checks-boxed {
		padding-inline: var(--sd-padding);
	}
}

/* Out-ranks Ant Design's bottom margin on a form item: the screen's own gap spaces the fields. */
.ant-form-item.sd-field {
	margin: 0;
}

/* eslint-disable css/no-invalid-properties -- the custom properties are Ant Design's theme variables (--sd-*), which the CSS linter cannot resolve */

/* The widget sizes itself (Cloudflare's flexible size: the container's width, at least 300px); the message below it
   keeps the page's rhythm. */
.sd-turnstile {
	display: grid;
	gap: var(--sd-padding-xs);
	max-width: 100%;
}

/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css and Ant Design's theme variables (--sd-*), which the CSS linter cannot resolve */

.sd-auth-frame {
	gap: var(--space-6);
	padding: var(--space-12) var(--space-4);
}

/* 420px at the default 16px root, and wider as the person's text size grows, so labels are never cut off. */
.sd-auth-card.ant-card {
	width: 100%;
	max-width: 26.25rem;
}

.sd-auth-card-body {
	gap: var(--space-4);
}

/* The page's h1: its size is title-1 (theme/titles.css); the card body's gap spaces it, so no margin of its own.
   Element-qualified: Ant Design spaces `h1.ant-typography` itself. */
h1.ant-typography.sd-auth-card-title {
	margin: 0;
}

/* A line of copy inside the card: the card body's gap spaces it, so it has no margin of its own. Element-qualified
   because Ant Design's `div.ant-typography` bottom margin beats a lone class. */
.ant-typography.sd-auth-text {
	margin: 0;
}

.ant-divider-horizontal.ant-divider-with-text.sd-auth-divider {
	margin: 0;
}

/* A form that adds no box: its fields and buttons sit in the card body's column, spaced by its gap. */
.sd-auth-form {
	display: contents;
}

/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css, which the CSS linter cannot resolve */
.sd-avatar {
	flex: none;
	display: inline-grid;
	place-items: center;
	border-radius: 50%;
	background: var(--surface-sunken);
	color: var(--ink);
	font-weight: 700;
	line-height: 1;
}

/* 32px at the default text size: inside AccountButton. */
.sd-avatar-md {
	width: var(--space-8);
	height: var(--space-8);
	font-size: var(--type-label-size);
}

/* 44px: at the top of the menu. */
.sd-avatar-lg {
	width: var(--touch-min);
	height: var(--touch-min);
	font-size: var(--type-body-strong-size);
}

/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css, which the CSS linter cannot resolve */
.sd-account-button {
	flex: none;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-1);
	box-sizing: border-box;
	min-width: var(--touch-min);
	min-height: var(--touch-min);
	padding: 0 var(--space-1);
	border: 0;
	border-radius: var(--radius-md);
	background: transparent;
	color: var(--ink);
	font: inherit;
	cursor: pointer;
}

.sd-account-button:hover {
	background: var(--surface-sunken);
}

/* Open, the button takes the sunken ground and the avatar flips to raised, so it stays tied to the open menu. */
.sd-account-button[aria-expanded='true'] {
	background: var(--surface-sunken);
}

.sd-account-button[aria-expanded='true'] .sd-avatar {
	background: var(--surface-raised);
}

.sd-account-button-chevron {
	color: var(--ink-muted);
}

/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css and Ant Design's theme variables (--sd-*), which the CSS linter cannot resolve */

/* border-box throughout this file: Ant Design only resets box-sizing inside its own components, so without it a
   width or min-height plus padding and border adds up past the design measure (the menu was 338px, rows 56px). */
/* The dropdown: 336px at the default text size (design: AccountMenu; no token holds it), never wider than the screen. */
.sd-account-menu {
	display: flex;
	flex-direction: column;
	box-sizing: border-box;
	width: 21rem;
	max-width: calc(100vw - var(--space-8));
	overflow: hidden;
	background: var(--surface-raised);
	color: var(--ink);
	border: var(--sd-line-width) solid var(--line);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-float);
}

.sd-account-menu:focus {
	outline: none;
}

/* The sheet: the drawer draws the frame, the panel fills it. */
.sd-account-menu-sheet {
	width: 100%;
	max-width: none;
	border: 0;
	border-radius: 0;
	box-shadow: none;
}

.sd-account-menu-person {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	padding: var(--space-4) var(--space-5);
}


/* A long name or address wraps inside the menu rather than widening it (WCAG 1.4.10). */
.sd-account-menu-who {
	display: flex;
	flex-direction: column;
	min-width: 0;
	overflow-wrap: anywhere;
}

.sd-account-menu-name {
	font-weight: 700;
}

.sd-account-menu-email {
	color: var(--ink-muted);
	font-size: var(--type-caption-size);
	line-height: var(--type-caption-line);
}

/* Going: on surface-raised, each row ending in a chevron. The rows are inset by the menu's 8px padding (design:
   AccountMenu) so a row's focus ring (2px, offset 2px) has room on all four sides inside the menu's clipped edge, and
   the gap keeps one row's ring off the next row. */
/* A hairline above the first row, inset by the container's 8px padding (design: AccountMenu), between the person block
   and the rows, in the menu and the sheet alike. */
.sd-account-menu-go {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: var(--space-1);
	padding: var(--space-2);
	background: var(--surface-raised);
}

.sd-account-menu-go::before {
	content: '';
	position: absolute;
	inset-inline: var(--space-2);
	top: 0;
	border-top: var(--sd-line-width) solid var(--line);
}

/* Acting: the theme choice and Sign out together in a surface band under a hairline, no chevrons. */
.sd-account-menu-act {
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
	padding: var(--space-3) var(--space-2) var(--space-2);
	background: var(--surface);
	border-top: var(--sd-line-width) solid var(--line);
}

/* Every row: the width of its container, the same 12px inset, icon then label. 48px tall at the default text size (design:
   AccountMenu; no token holds it), so never under touch-min. */
.sd-account-menu-row {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	box-sizing: border-box;
	width: 100%;
	min-height: 3rem;
	padding: var(--space-1) var(--space-3);
	border: 0;
	border-radius: var(--radius-md);
	background: transparent;
	color: var(--ink);
	font: inherit;
	text-align: start;
	text-decoration: none;
	cursor: pointer;
}

.sd-account-menu-row:hover {
	background: var(--surface-sunken);
	color: var(--ink);
}

.sd-account-menu-row[aria-current='page'] {
	background: var(--pine-soft);
}

.sd-account-menu-label {
	flex: 1 1 auto;
	min-width: 0;
}

.sd-account-menu-icon,
.sd-account-menu-chevron {
	flex: none;
	color: var(--ink-muted);
}

/* The label lines up with the rows' text (8px of band padding plus the rows' 12px). */
.sd-account-menu-theme .sd-theme-choice-label {
	padding-inline: var(--space-3);
}

/* In the 336px menu Light, Dark and Match device share one line at the default text size (design: AccountMenu): a
   tighter option, still touch-min tall. At a large text size the options wrap onto a second line, which is fine. */
.sd-account-menu-theme .sd-theme-choice-option {
	gap: var(--space-1);
	padding-inline: var(--space-2);
	white-space: nowrap;
}

.sd-account-menu-signout {
	margin: 0;
}

.sd-account-menu-foot {
	display: flex;
	justify-content: center;
	padding: var(--space-3);
	border-top: var(--sd-line-width) solid var(--line);
}

/* The sheet's frame: rounded top, never taller than the screen, and the grab handle. (The rounded rule used to name
   .ant-drawer-content, which Ant Design 6 no longer renders: the panel is .ant-drawer-section.) The panel grows to fit
   its content and, past the wrapper's cap below, shrinks so the body scrolls. */
.sd-account-sheet .ant-drawer-section {
	flex: 1 1 auto;
	min-height: 0;
	height: auto;
	border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

/* The header is one touch-min bar (design: AccountMenu): the grab handle centred on its top edge, Close at the end, no
   hairline, 16px at the sides. The title is visually hidden, so the bar holds only Close and the handle. The top
   padding is the extent of Close's focus ring (2px offset + 2px width): the panel clips at its rounded top, so a Close
   flush with the edge lost the top of its ring. */
.sd-account-sheet .ant-drawer-header {
	position: relative;
	padding: var(--space-1) var(--space-4) 0;
	border-bottom: 0;
}

/* The wrapper is as tall as its content (size auto) up to the dynamic viewport; past that the panel shrinks and the
   body scrolls, so Close and the name stay reachable at a large text size (WCAG 1.4.4, 1.4.10). */
.sd-account-sheet .ant-drawer-content-wrapper {
	display: flex;
	flex-direction: column;
	max-height: 100dvh;
}

.sd-account-sheet .ant-drawer-body {
	padding: 0 0 var(--space-4);
	/* A row scrolled into view (End at a large text size) keeps room for its focus ring: 2px offset + 2px width. */
	scroll-padding-block: var(--space-2);
}

.sd-account-sheet-handle {
	position: absolute;
	inset-inline: 0;
	top: var(--space-2);
	width: var(--space-12);
	height: var(--space-1);
	margin: 0 auto;
	border-radius: var(--radius-sm);
	background: var(--line-strong);
}

/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css, which the CSS linter cannot resolve */
/* The page gutter (16px a side, in rem), so a phone at 200% text keeps a column wide enough for a word. */
.sd-app-footer {
	box-sizing: border-box;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-2);
	padding: var(--space-6) var(--space-4);
}

.sd-app-footer-note {
	margin: 0;
	color: var(--ink-muted);
	text-align: center;
}

/* Stacked and centred on a phone; PoweredBy left and LegalFooter right from 768px. */
.sd-app-footer-row {
	box-sizing: border-box;
	width: 100%;
	max-width: var(--content-max);
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-2);
}

@media (min-width: 48em) {
	.sd-app-footer-row {
		flex-direction: row;
		justify-content: space-between;
	}
}

/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css, which the CSS linter cannot resolve */
.sd-app {
	min-height: 100dvh;
	display: flex;
	flex-direction: column;
	background: var(--surface);
}

.sd-app-header {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-3);
	padding: var(--space-2) var(--space-4);
}

.sd-app-header-find,
.sd-app-header-home {
	display: inline-flex;
	align-items: center;
	min-height: var(--touch-min);
}

.sd-app-header-find {
	color: var(--pine);
	font-weight: 700;
}

/* The account button (or Sign in) is always last on the right. */
.sd-app-header-end {
	margin-inline-start: auto;
	display: flex;
	align-items: center;
}

.sd-app-header-sign-in {
	min-height: var(--touch-min);
	display: inline-flex;
	align-items: center;
}

.sd-app-main {
	flex: 1 0 auto;
}

/* The heading's size is the shared title (theme/titles.css). The section around it spaces it, so it has no margin of
   its own. */
.section-heading-element {
	margin: 0;
}

/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css and Ant Design's theme variables (--sd-*), which the CSS linter cannot resolve */

/* Spacing the Settings sections share, from the spacing scale: a block that follows another inside a section, and a
   paragraph with no margin of its own (a modal's only line). */
.sd-settings-follows {
	margin-top: var(--space-3);
}

/* Element-qualified: Ant Design's `div.ant-typography` bottom margin (0,1,1) beats a lone class on a Paragraph. */
.ant-typography.sd-settings-flush {
	margin: 0;
}

/* The page: a 560px column at the default text size, centred, its sections 32px apart. */
.sd-settings-page {
	max-width: 35rem;
	padding: var(--space-4);
	margin: 0 auto;
	display: flex;
	flex-direction: column;
	gap: var(--space-8);
}

/* The page's h1: its size is title-1 (theme/titles.css); the page's gap spaces it, so no margin of its own.
   Element-qualified: Ant Design spaces `h1.ant-typography` itself. */
h1.ant-typography.sd-settings-title {
	margin: 0;
}

/* A status block (an error with its retry, a progress line, a ready download): its lines close together, then room
   before what follows. */
.sd-settings-status {
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
	margin-bottom: var(--space-3);
}

.sd-settings-actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
}

/* A button that follows another on the same line. */
.sd-settings-beside {
	margin-left: var(--space-2);
}

/* A section's load-failed alert sits above what follows, as the status block does. */
.sd-settings-load-error {
	margin-bottom: var(--space-3);
}

/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css and Ant Design's theme variables (--sd-*), which the CSS linter cannot resolve */

/* The primary button's label wraps rather than widening the page at a large text size or on a narrow screen (WCAG 1.4.4
   and 1.4.10); Ant Design's button is nowrap at a fixed height, so both are undone here, keeping the usual height as the
   minimum (the same override global.css makes for block buttons). */
.sd-email-change.ant-btn {
	margin-top: var(--space-3);
	height: auto;
	min-height: var(--sd-control-height);
	padding-block: var(--sd-padding-xxs);
	white-space: normal;
}

/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css, which the CSS linter cannot resolve */

/* One passkey: its name and dates, and Rename and Remove, which wrap under them when the row is narrow. */
.sd-passkey-row {
	gap: var(--space-4);
	padding-block: var(--space-3);
}

.sd-passkey-info {
	gap: var(--space-1);
}

.sd-passkey-name,
.sd-passkey-actions {
	gap: var(--space-2);
}

/* The list, then room before "Add a passkey": the same 16px a section's paragraph leaves above its button (as above
   "Request my data"), so the two sections line up. Without it the last row's divider touches the button. */
.sd-passkey-list {
	margin-bottom: var(--space-4);
}

/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css, which the CSS linter cannot resolve */

/* The device glyph beside a session: one step above body text, from the type scale. */
.sd-session-device-icon {
	font-size: var(--type-body-lg-size);
}

/* Ant Design's Flex hides itself when empty; this list does the same while the sessions load. */
.sd-session-list:empty {
	display: none;
}

.sd-session-list {
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
	margin-top: var(--space-3);
}

.sd-session {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	padding: var(--space-3);
	border: var(--sd-line-width) solid var(--line);
	border-radius: var(--radius-md);
	background: var(--surface-raised);
}

/* The current session sits in a well and the others are raised, so it stands apart from them, not only from the page:
   raised against sunken is the widest pair the surfaces give in both themes, where sunken against the page alone was
   faint in dark. Its icon tile is raised so it still reads in the well. */
.sd-session-current-row {
	background: var(--surface-sunken);
}

.sd-session-current-row .sd-session-icon {
	background: var(--surface-raised);
}

/* The device glyph's tile: 36px at the default text size. */
.sd-session-icon {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 2.25rem;
	height: 2.25rem;
	border-radius: var(--radius-md);
	background: var(--surface-sunken);
	flex-shrink: 0;
	color: var(--ink-muted);
}

.sd-session-body {
	flex: 1;
}

.sd-session-title {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--space-2);
}

/* The small text of a session row. Ant Design's typography colour and size rules are the same strength as a lone
   class, so the rules are element-qualified. */
.ant-typography.sd-session-small,
.ant-typography.sd-session-current,
.ant-typography.sd-session-meta {
	font-size: var(--sd-font-size-sm);
}

.ant-typography.sd-session-current {
	color: var(--pine);
}

.ant-typography.sd-session-meta {
	display: block;
	margin-top: var(--space-1);
}

.sd-session-revoke {
	flex-shrink: 0;
}

.ant-btn.sd-session-revoke-all {
	padding: 0;
	font-size: var(--sd-font-size-sm);
}

.ant-divider.sd-session-divider {
	margin: var(--space-5) 0 0;
}

/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css, which the CSS linter cannot resolve */

/* The Email row: the term, then the address and its link, wrapping on a narrow screen or at a large text size. The
   list's own margins and the definition's indent are the browser's, so they go. */
.sd-details-email,
.sd-details-email dd {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
	margin: 0;
}

/* atkinson-hyperlegible-next-latin-400-normal */
@font-face {
  font-family: 'Atkinson Hyperlegible Next';
  font-style: normal;
  font-display: swap;
  font-weight: 400;
  src: url(c510f25d4fe696cb1464.woff2) format('woff2'), url(ea73f7abe86ff3cd1a50.woff) format('woff');
}
/* atkinson-hyperlegible-next-latin-700-normal */
@font-face {
  font-family: 'Atkinson Hyperlegible Next';
  font-style: normal;
  font-display: swap;
  font-weight: 700;
  src: url(0b5e7bd6f8f8c4523fba.woff2) format('woff2'), url(3e87494964cc05731faa.woff) format('woff');
}
/* atkinson-hyperlegible-mono-latin-600-normal */
@font-face {
  font-family: 'Atkinson Hyperlegible Mono';
  font-style: normal;
  font-display: swap;
  font-weight: 600;
  src: url(6863ab363e8ad1d7efbe.woff2) format('woff2'), url(4fe4ea40ee3358515959.woff) format('woff');
}
/* atkinson-hyperlegible-mono-latin-700-normal */
@font-face {
  font-family: 'Atkinson Hyperlegible Mono';
  font-style: normal;
  font-display: swap;
  font-weight: 700;
  src: url(86f83bc077d3a0d57649.woff2) format('woff2'), url(130bc671adc782614e79.woff) format('woff');
}
/* Generated by scripts/generate-palette.mjs from tokens.json. Do not edit. */
:root[data-theme='light'] {
	--surface: #f6f4ef;
	--surface-raised: #ffffff;
	--surface-sunken: #ece9e1;
	--line: #dedad0;
	--line-strong: #857f73;
	--ink: #1c211d;
	--ink-muted: #535953;
	--pine: #1d5a40;
	--pine-hover: #164832;
	--pine-soft: #e1eee6;
	--on-pine: #ffffff;
	--brand-pine: #1d5a40;
	--harvest: #f0b43c;
	--on-harvest: #1c211d;
	--estimate: #7b4a00;
	--estimate-soft: #fbf0d9;
	--short: #a3361a;
	--short-hover: #852b14;
	--short-soft: #fbe7e0;
	--sub: #1f5aa6;
	--sub-hover: #174583;
	--sub-soft: #e3edfa;
	--danger: #b0221a;
	--danger-hover: #8f1a13;
	--on-danger: #ffffff;
	--danger-soft: #fce6e4;
	--focus: #1f5aa6;
	--plate: #ffffff;
	--scrim: rgba(13,15,13,0.48);
	--shadow-float: 0 1px 2px rgba(28,33,29,0.08), 0 4px 12px rgba(28,33,29,0.08);
	--shadow-overlay: 0 8px 32px rgba(28,33,29,0.18);
}

:root[data-theme='dark'] {
	--surface: #131613;
	--surface-raised: #1c201c;
	--surface-sunken: #0d0f0d;
	--line: #2e342e;
	--line-strong: #79817a;
	--ink: #eceee8;
	--ink-muted: #a9b0a8;
	--pine: #79cfa1;
	--pine-hover: #95dcb5;
	--pine-soft: #173325;
	--on-pine: #0b1a12;
	--brand-pine: #1d5a40;
	--harvest: #f0b43c;
	--on-harvest: #1c211d;
	--estimate: #f2c46b;
	--estimate-soft: #33280f;
	--short: #ff9a76;
	--short-hover: #ffb497;
	--short-soft: #3a1d14;
	--sub: #8fbaff;
	--sub-hover: #c2d9ff;
	--sub-soft: #16253d;
	--danger: #ff8c84;
	--danger-hover: #ffaca5;
	--on-danger: #2b0a07;
	--danger-soft: #3b1714;
	--focus: #8fbaff;
	--plate: #f4f2ec;
	--scrim: rgba(0,0,0,0.64);
	--shadow-float: 0 1px 2px rgba(0,0,0,0.5), 0 4px 12px rgba(0,0,0,0.4);
	--shadow-overlay: 0 8px 32px rgba(0,0,0,0.6);
}

:root {
	--space-1: 0.25rem;
	--space-2: 0.5rem;
	--space-3: 0.75rem;
	--space-4: 1rem;
	--space-5: 1.25rem;
	--space-6: 1.5rem;
	--space-8: 2rem;
	--space-12: 3rem;
	--radius-sm: 0.25rem;
	--radius-md: 0.5rem;
	--radius-lg: 0.75rem;
	--radius-pill: 62.4375rem;
	--touch-min: 2.75rem;
	--touch-lg: 3.25rem;
	--touch-aisle: 3.5rem;
	--row-aisle: 4rem;
	--plate-tile: 9rem;
	--content-max: 75rem;
	--type-display-size: 2rem;
	--type-display-line: 2.375rem;
	--type-title-1-size: 1.5rem;
	--type-title-1-line: 1.875rem;
	--type-title-2-size: 1.25rem;
	--type-title-2-line: 1.625rem;
	--type-title-3-size: 1.125rem;
	--type-title-3-line: 1.5rem;
	--type-body-lg-size: 1.125rem;
	--type-body-lg-line: 1.625rem;
	--type-body-size: 1rem;
	--type-body-line: 1.5rem;
	--type-body-strong-size: 1rem;
	--type-body-strong-line: 1.5rem;
	--type-label-size: 0.875rem;
	--type-label-line: 1.25rem;
	--type-caption-size: 0.875rem;
	--type-caption-line: 1.25rem;
	--type-body-sm-size: 0.875rem;
	--type-body-sm-line: 1.375rem;
	--type-price-lg-size: 1.5rem;
	--type-price-lg-line: 1.875rem;
	--type-price-size: 1rem;
	--type-price-line: 1.5rem;
	--type-qty-aisle-size: 1.75rem;
	--type-qty-aisle-line: 2rem;
	--type-code-size: 0.875rem;
	--type-code-line: 1.25rem;
	--font-sans: "Atkinson Hyperlegible Next", system-ui, -apple-system, "Segoe UI", sans-serif;
	--font-mono: "Atkinson Hyperlegible Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css, which the CSS linter cannot resolve */

/* The type scale's titles (tokens.json, "title-1" to "title-3"), defined once so every screen's headings match:
   - title-1: a page's one h1 (Settings, every staff page, the sign-in and sign-up card, a whole-page message);
   - title-2: each section's h2 (Settings sections, staff sections);
   - title-3: a card's or an in-page message's title.
   The public site keeps its own larger display scale (PublicChrome.css). No other stylesheet sizes a title: a test
   (theme/__tests__/titles.test.ts) reads them all.

   Each class is also written after `.ant-typography`: Ant Design sizes `h1.ant-typography` and `.ant-typography h1`
   (one class and one element), which a lone class does not out-rank. */
.sd-title-1,
.ant-typography.sd-title-1 {
	font-size: var(--type-title-1-size);
	line-height: var(--type-title-1-line);
	font-weight: 700;
}

.sd-title-2,
.ant-typography.sd-title-2 {
	font-size: var(--type-title-2-size);
	line-height: var(--type-title-2-line);
	font-weight: 700;
}

.sd-title-3,
.ant-typography.sd-title-3 {
	font-size: var(--type-title-3-size);
	line-height: var(--type-title-3-line);
	font-weight: 700;
}

/* eslint-disable css/no-invalid-properties -- the custom properties come from the generated tokens.css, which the CSS linter cannot resolve */

body {
	/* No browser default margin: the shell and the public pages draw their own gutters (the staff portal's staff.css
	   already did this; the shopper app had an 8px frame around every page). */
	margin: 0;
	background: var(--surface);
	color: var(--ink);
	font-family: var(--font-sans);
}

/* The focus ring: buttons, links, menu rows, tabs, switches. Text fields are left out: they keep Ant Design's own
   focus, a `focus` border and a flush 2px `focus` glow (theme.ts, FIELD_FOCUS), decided by Matthew 2026-10-15. A
   field is already a bordered box, so a ring 2px away would float beside it. In Ant Design 6 every text field,
   Select included, focuses an <input> or <textarea>. Leaving out every input also leaves out the hidden inputs of a
   checkbox, a radio and a Segmented option: Ant Design rings the box beside each instead, and the rule below gives
   those rings our offset. */
:focus-visible:not(input):not(textarea) {
	outline: var(--sd-line-width-focus) solid var(--focus);
	outline-offset: var(--sd-line-width-focus);
	box-shadow: none;
}

/* eslint-disable css/no-important -- reduced motion must beat component-level durations */
@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation-duration: 0s !important;
		transition-duration: 0s !important;
	}
}

/* Text for screen readers only: an icon-only control's name. The standard clip pattern; the box is one line-width
   (the pattern's 1px), not a size anyone sees. */
.sd-visually-hidden {
	position: absolute;
	width: var(--sd-line-width);
	height: var(--sd-line-width);
	margin: calc(-1 * var(--sd-line-width));
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* Full-width buttons wrap their label rather than clip it: at a large text size, or on a narrow screen, the label
   grows past one line (WCAG 1.4.4 and 1.4.10). Each size keeps its usual height as the minimum. */
.ant-btn.ant-btn-block {
	white-space: normal;
	height: auto;
	min-height: var(--sd-control-height);
	padding-block: var(--sd-padding-xxs);
}

.ant-btn.ant-btn-block.ant-btn-lg {
	min-height: var(--sd-control-height-lg);
}

.ant-btn.ant-btn-block.ant-btn-sm {
	min-height: var(--sd-control-height-sm);
}

/* Ant Design sizes a drawer's close button from its own font size (26px). A touch target is at least touch-min, so
   every drawer (the account sheet, the staff sections drawer, the Invitations drawer) gets the same size. Doubled
   class, like the overrides below: Ant Design 6 sizes it with `.ant-drawer .ant-drawer-close` (two classes), which a
   single class loses to. */
.ant-drawer .ant-drawer-close {
	box-sizing: border-box;
	width: var(--touch-min);
	height: var(--touch-min);
}

/* Ant Design draws its focus rings in colorPrimaryBorder, which theme.ts pins to `focus`, with a 1px offset. These
   rules restore the 2px offset and the 2px width (and the colour, should a component's own token differ: Button's is
   pine-soft, for its pressed fill; and the width: Pagination's lineWidthFocus is 1px, from the input tokens it
   inherits). Some rings are on the focused element; Ant Design draws others on the box round a hidden input (a
   checkbox, a radio, a Segmented option, a card tab), so those boxes are listed. `:not(#sd-focus-ring)` matches every
   element (no element has that id): it is there for its specificity, one id, which beats every Ant Design focus rule
   (classes and pseudo-classes only, up to six of them in Ant Design 6.6), whatever order the styles load in. */
:focus-visible:not(input):not(textarea):not(#sd-focus-ring),
.ant-checkbox:has(.ant-checkbox-input:focus-visible):not(#sd-focus-ring),
.ant-radio:has(.ant-radio-input:focus-visible):not(#sd-focus-ring),
.ant-radio-button-wrapper:has(:focus-visible):not(#sd-focus-ring),
.ant-segmented .ant-segmented-item-focused:not(#sd-focus-ring),
.ant-tabs-card .ant-tabs-tab-focus:has(.ant-tabs-tab-btn:focus-visible):not(#sd-focus-ring) {
	outline-color: var(--focus);
	outline-width: var(--sd-line-width-focus);
	outline-offset: var(--sd-line-width-focus);
}

/* The current page's item has its own 1px border in the primary colour; with the ring 2px outside it, focus read as two
   edges in two colours. While it has focus the ring alone marks it (its number stays in the primary colour). */
.ant-pagination .ant-pagination-item-active:focus-visible:not(#sd-focus-ring) {
	border-color: transparent;
}

/* Pagination's previous and next: the <li> takes focus and gets the ring above, and Ant Design draws a second ring on
   the button inside it. One ring, ours. */
.ant-pagination .ant-pagination-prev:focus-visible .ant-pagination-item-link:not(#sd-focus-ring),
.ant-pagination .ant-pagination-next:focus-visible .ant-pagination-item-link:not(#sd-focus-ring) {
	outline: none;
}

/* Where notices (Ant Design's message, through NOTICE_CONFIG in notice.tsx) sit: below a header bar of one touch
   target with its padding above and below (AppLayout.css, StaffLayout.css), then one more step of space, so a notice
   never covers the bar's account button or menu. */
:root {
	--notice-top: calc(var(--touch-min) + 3 * var(--space-2));
}

