/* The `.ds` foundation — shared by EVERY screen opted into the new design
   system, not just `/design-system`.

   Extracted out of `design-system-page.css` (which used to carry the whole
   system, foundation and route chrome mixed together, back when
   `/design-system` was the system's only screen). This file holds the base
   type/colour block, the old-system reset, the focus ring, the reduced-motion
   kill switch, the type/status/layout primitives every rebuilt screen reaches
   for, and the WHOLE `.ds-table*` border grammar. `design-system-page.css`
   keeps only `/design-system`'s own specimen chrome — the swatches, the
   border/radius/motion/layer demos, the reference-table classes — none of
   which any other screen imports.

   Both files are imported globally in `_root.tsx`, in that order: this file
   right after `design-system-tokens.css`, so route-specific chrome loaded
   after it can still override a rule here if it ever needs to. No rule is
   declared in both files. */

.ds {
	font-family: var(--font-sans);
	font-size: var(--text-base);
	line-height: var(--text-base-leading);
	font-weight: var(--weight-normal);
	color: var(--text-primary);
	background: var(--surface-page);
}

/* §2.1 — the sans figure feature, applied wherever the page sets sans
   text. Defaults to `normal` (Inter's own figures); a host may set
   `--font-sans-figures` to a stylistic set alongside its own `--font-sans`
   (§2.1A). Scoped to `.ds` so it cannot reach the rest of the app. A
   separate rule from the block above (rather than one more declaration in
   it) only because that is how `design-system-tokens.css`'s own doc
   comment and the page that used to hold this both already stated it. */
.ds {
	font-feature-settings: var(--font-sans-figures);
}

/* ---------------------------------------------------------------------
   Control height — DESIGN_COMPONENTS.md §4.1 rule 4 / §4.2 rule 1b.

   One figure per size. Button and Icon button both read it, so the two can
   never differ, and nothing under a brand scope (data-brand) can move it:
   a brand may change radius, inline padding and gap, never height.

   `fit` is the compact height: the text line, the size's vertical padding,
   and the border both edges reserve. `height` is what a control renders.
   On a coarse pointer `default` reserves `touch-target-min` (§2.7). `small`
   grows only to `control-touch-small`, so a small control stays smaller than
   a default one on touch; an invisible hit area reaches `touch-target-min`
   instead (button.css, icon-button.css). A labeled button and an icon
   button of one size stay the same height on both pointers.
   --------------------------------------------------------------------- */

.ds {
	--control-fit-default: calc(
		var(--text-base-leading) + 2 * var(--space-xs) + 2 *
			var(--border-width-light)
	);
	--control-fit-small: calc(
		var(--text-sm-leading) + 2 * var(--space-2xs) + 2 *
			var(--border-width-light)
	);
	--control-touch-small: 32px;
	--control-height-default: var(--control-fit-default);
	--control-height-small: var(--control-fit-small);
}

@media (pointer: coarse) {
	.ds {
		--control-height-default: max(
			var(--control-fit-default),
			var(--touch-target-min)
		);
		--control-height-small: max(
			var(--control-fit-small),
			var(--control-touch-small)
		);
	}
}

/* §2.3H — selection is a token, not the UA default. */
.ds ::selection {
	background: var(--selection-bg);
	color: var(--selection-fg);
}

/* §2.11 rules 1–3. `outline-offset` would leave the PAGE ground showing in
   the gap; the spec says the gap is filled with `focus-ring-offset`, which
   takes two stacked shadows. Keyboard-only via `:focus-visible` (rule 2). */
.ds :focus-visible {
	outline: none;
	box-shadow:
		0 0 0 var(--focus-ring-gap) var(--focus-ring-offset),
		0 0 0 calc(var(--focus-ring-gap) + var(--focus-ring-width))
			var(--focus-ring);
}

/* ---------------------------------------------------------------------
   Resetting the OLD system's element styles

   `packages/ui/src/styles/primitives.css` styles bare `table`, `th`, and
   `td` globally: a dotted `border-top` on every row after the first, a solid
   `border-left` on every cell after the first, a header underline, padding,
   and `vertical-align: top`. Scoping the TOKENS to `.ds` does not scope
   those — an element selector still matches inside this subtree, so every
   rule below was drawing a second line beside the one it intended.

   Reset here, ahead of the table rules, so every `.ds` screen draws exactly
   what DESIGN.md says and nothing the old system adds.
   --------------------------------------------------------------------- */

/* Wrapped in `:where()` so the reset contributes NO specificity. Written
   plainly, `.ds thead th` (0-1-2) outranks `.ds-table th` (0-1-1) and `.ds
   table` (0-1-1) outranks `.ds-table` (0-1-0) — the reset would then beat
   the rules it exists to make room for, silently dropping the header
   separator, the header weight, and the table's own text size. */
.ds :where(table) {
	border-collapse: separate;
	border-spacing: 0;
	width: 100%;
	font-size: inherit;
}

.ds :where(th, td) {
	padding: 0;
	border: 0;
	text-align: left;
	vertical-align: middle;
}

.ds :where(thead th) {
	border-bottom: 0;
	font-weight: inherit;
}

/* ---------------------------------------------------------------------
   Type scale — §2.1
   --------------------------------------------------------------------- */

.ds-h1,
.ds-h2,
.ds-h3 {
	/* §2.1D rule 2: every header is semibold with -2% tracking. */
	font-weight: var(--weight-semibold);
	letter-spacing: var(--header-tracking);
	margin: 0;
}

.ds-h1 {
	font-size: var(--text-lg);
	line-height: var(--text-lg-leading);
}

.ds-h2 {
	font-size: var(--text-md);
	line-height: var(--text-md-leading);
}

.ds-h3 {
	font-size: var(--text-base);
	line-height: var(--text-base-leading);
}

.ds-mono {
	font-family: var(--font-mono);
}

.ds-link {
	color: var(--text-link);
	/* §2.1D rule 1 — dotted at `link-underline-thickness`. Hue carries
     nothing (§2.3 rule 2). Drawn as a background image; see
     `--link-underline-image` in tokens.css for why. */
	text-decoration-line: none;
	background-image: var(--link-underline-image);
	background-size: var(--ds-dotted-period) var(--link-underline-thickness);
	background-repeat: round no-repeat;
	background-position: 0 100%;
	/* Each wrapped line gets its own whole-dot run. */
	-webkit-box-decoration-break: clone;
	box-decoration-break: clone;
}

.ds-body {
	font-size: var(--text-base);
	line-height: var(--text-base-leading);
	color: var(--text-primary);
	margin: 0;
	max-width: 68ch;
}

/* A caption sits under the specimen it explains, and needs to clear it — at
   zero margin it touched the content above (§2.4 rule 5). */
.ds-caption {
	margin-top: var(--space-sm);
	font-size: var(--text-xs);
	line-height: var(--text-xs-leading);
	/* A caption explains what it sits under and is read alongside it, so it
     keeps text-primary — a choice about a caption's own role, not a
     constraint rule 10 imposes on every text-xs run. */
	color: var(--text-primary);
}

/* §3.2 rule 10: an identifier named in prose is inline code. CodeBlock's
   figure shares the class, so the chip targets `code` only. */
.ds-code {
	font-family: var(--font-mono);
	font-size: 0.9em;
}
code.ds-code {
	padding: var(--space-2xs) var(--space-xs);
	background-color: var(--surface-code);
	border: var(--border-width-light) solid var(--border-color-subtle);
	border-radius: var(--radius-mark);
	box-decoration-break: clone;
}

/* ---------------------------------------------------------------------
   §2.8 Icons — Phosphor, at a 16px native grid.

   Size comes from the token, not from Phosphor's `size` prop: the component
   writes `width`/`height` ATTRIBUTES on the svg, and an attribute cannot
   hold a `var()`. A class on the svg overrides both, which keeps
   `icon-default` and `icon-small` the single source of the figure.

   Weight is the opposite case — `regular` and `fill` select a different
   glyph path, so they are a React prop and nothing CSS can reach.
   --------------------------------------------------------------------- */

.ds-glyph {
	flex: 0 0 auto;
	color: var(--text-primary);
}

.ds-glyph--default {
	width: var(--icon-default);
	height: var(--icon-default);
}

.ds-glyph--small {
	width: var(--icon-small);
	height: var(--icon-small);
}

/* §3.10 — status renders as an outline icon plus its label, everywhere. The
   icon carries the hue; the label carries the meaning, and stays
   text-primary so the column still scans as text (rule 3). */
.ds-status {
	display: flex;
	align-items: center;
	/* §2.4 rule 8: "an icon and its label ... one adjacency, one distance, at
     every size: the gap does not shrink because the control does." The
     token table's "icon to label at small size" use-case for `space-2xs`
     describes a chip/small-button's own internal glyph, not this pairing —
     rule 8 is the normative one, and it names "an icon and its label"
     explicitly. `ds/StatusLockup`'s own doc comment already asserts
     `space-xs`; this brings the rule into line with it. */
	gap: var(--space-xs);
	font-size: var(--text-sm);
	line-height: var(--text-sm-leading);
	color: var(--text-primary);
}

/* `size="default"` (`icon-default`, 16px) pairs with `text-base`, not this
   component's own `text-sm` base — §2.8 rule 3's table. */
.ds-status--default {
	font-size: var(--text-base);
	line-height: var(--text-base-leading);
}

/* The recorded §3.10 rule 3 deviation (DESIGN.md §7): a lockup whose row
   reports an ABSENCE mutes its label so a synthetic row never outranks
   real data. The icon keeps its status hue. Set only via the component's
   own `muted` prop — never from a screen stylesheet. */
.ds-status--muted {
	color: var(--text-secondary);
}

/* A trailing glyph inside a text link or label — §2.4 rule 8's one
   adjacency, one distance, plus the inline-vertical trim that keeps a
   16px-line glyph from stretching the line box. */
.ds-glyph--trailing {
	margin-inline-start: var(--space-xs);
	vertical-align: text-bottom;
}

.ds-status__icon--success {
	color: var(--status-success-fg);
}

.ds-status__icon--error {
	color: var(--status-error-fg);
}

.ds-status__icon--warning {
	color: var(--status-warning-fg);
}

.ds-status__icon--neutral {
	color: var(--status-neutral-fg);
}

/* ---------------------------------------------------------------------
   §2.9 Motion — the reduced-motion kill switch.

   §2.9 rules 4–5: under a reduced-motion preference, transitions are REMOVED
   rather than shortened — but a looping indicator that is the only signal
   work is happening keeps running, marked `data-motion="essential"`. This is
   the ONE kill switch in the system; nothing downstream may add a second.
   --------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
	.ds *,
	.ds *::before,
	.ds *::after {
		transition: none !important;
	}

	.ds *:not([data-motion="essential"]):not(.ds-enter):not(.ds-exit),
	.ds *:not([data-motion="essential"]):not(.ds-enter):not(.ds-exit)::before,
	.ds *:not([data-motion="essential"]):not(.ds-enter):not(.ds-exit)::after {
		animation: none !important;
	}
}

/* ---------------------------------------------------------------------
   §2.10 Layering
   --------------------------------------------------------------------- */

/* §2.6 rules 4–5: an overlay is FLAT, on surface-overlay, with a
   border-color-default hairline in both themes. */
.ds-overlay {
	padding: var(--space-md);
	border-radius: var(--rounded-md);
	background: var(--surface-overlay);
	border: var(--border-width-light) solid var(--border-color-default);
	box-shadow: none;
}

.ds-scrim {
	height: calc(2 * var(--space-xl));
	border-radius: var(--rounded-sm);
	background: var(--surface-scrim);
}

/* ---------------------------------------------------------------------
   Specimen frame — the elevated, data-bearing container (§2.6)
   --------------------------------------------------------------------- */

/* A data-bearing panel is a record (§2.5 rule 1): square corners, and the
   barely-there lift of shd-diffuse (§2.6 rule 1). */
.ds-panel {
	background: var(--surface-raised);
	padding: var(--space-lg);
	/* §2.6 rules 1–3 and §2.3 rule 13: shadow in light, hairline in dark. */
	box-shadow: var(--elevation-shadow);
	border: var(--border-width-light) solid var(--elevation-hairline);
	z-index: var(--layer-raised);
}

/* ---------------------------------------------------------------------
   Page frame — §2.7, §5.1
   --------------------------------------------------------------------- */

.ds-page {
	display: flex;
	flex-direction: column;
	/* §5.2 rule 2 / §2.4 rule 5: sections are the widest step in the ladder. */
	gap: var(--space-2xl);
	padding-block: var(--space-xl);
}

/* A page header carrying controls that act on every section is laid on the
   §2.7 grid, not on flex: the title takes 4 of the 6 columns and the control
   cluster takes 2, aligned right. Fixed spans mean the split does not move as
   the title's length changes. */
.ds-pagehead {
	display: grid;
	grid-template-columns: repeat(6, minmax(0, 1fr));
	align-items: start;
	gap: var(--grid-gutter);
}

.ds-pagehead__title {
	grid-column: span 4;
	min-width: 0;
}

.ds-pagehead__controls {
	grid-column: span 2;
	justify-self: end;
	/* The cluster never wraps: controls that act on the whole page have to stay
     one row, or the header's height changes with the viewport. */
	flex-wrap: nowrap;
	white-space: nowrap;
}

.ds-pagehead__meta {
	display: block;
	margin-top: var(--space-xs);
	font-size: var(--text-sm);
	line-height: var(--text-sm-leading);
	color: var(--text-secondary);
}

@media (max-width: 639px) {
	.ds-pagehead {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	.ds-pagehead__title,
	.ds-pagehead__controls {
		grid-column: span 2;
	}

	.ds-pagehead__controls {
		justify-self: start;
	}
}

/* THE VERTICAL RHYTHM (§2.4 rules 5-6).
   A uniform `gap` cannot express a hierarchy — every pair gets the same
   distance, so a heading sits as far from its content as that content sits
   from the next block, and the eye finds no grouping. The ladder is built
   from margins on the PAIR instead, so each relationship gets its own step:

     heading  -> its note      space-xs   (bound tightest)
     heading  -> its content   space-sm
     note     -> its content   space-sm
     block    -> block         space-lg
     subsection -> subsection  space-xl
     section  -> section       space-2xl  (.ds-page gap)                    */
.ds-section {
	display: flex;
	flex-direction: column;
	gap: 0;
}

/* Default distance between things in a section: a new subsection. */
.ds-section > * {
	margin-top: var(--space-xl);
}

.ds-section > *:first-child {
	margin-top: 0;
}

/* A heading binds to whatever it labels. */
.ds-section > .ds-h3 + * {
	margin-top: var(--space-sm);
}

/* ...and binds tighter still to its own supplemental line, which then binds
   to the content (§3.5 rule 7). */
.ds-section > .ds-h3 + .ds-section__note {
	margin-top: var(--space-xs);
}

.ds-section > .ds-section__note + * {
	margin-top: var(--space-sm);
}

.ds-section > .ds-caption {
	margin-top: var(--space-sm);
}

/* Two peer blocks under one heading — closer than two subsections. */
.ds-section > .ds-table__scroll + .ds-table__scroll,
.ds-section > .ds-stack + .ds-stack {
	margin-top: var(--space-lg);
}

/* §2.2B: the dotted style is the border underneath a section header. Drawn
   as a repeating gradient because `border-style: dashed` cannot state a
   dash length. */
.ds-section__head {
	/* §2.10: sticky content is in-flow and sits at layer-sticky. The ground is
     surface-page, not surface-sticky — an opaque fill matching the page still
     occludes the rows passing beneath it, without the header announcing
     itself as a tinted band. */
	position: sticky;
	top: 0;
	z-index: var(--layer-sticky);
	background: var(--surface-page);
	/* §2.6 rule 3: a sticky SECTION header casts no shadow. It is a heading
     belonging to the page, not a plane above it — the rule about shadowing
     an occluding header is about a table's own header, where rows are
     genuinely passing underneath a row of column labels. */
	padding-top: var(--space-sm);
	padding-bottom: var(--space-sm);
	background-image: linear-gradient(
		to right,
		var(--border-color-default) 0 var(--border-style-dotted-dash),
		transparent var(--border-style-dotted-dash) var(--ds-dotted-period)
	);
	background-repeat: repeat-x;
	background-size: var(--ds-dotted-period) var(--border-width-light);
	background-position: bottom left;
}

/* §3.5 rules 7-8: supplemental copy under a heading has ONE treatment
   everywhere — text-sm, text-secondary, space-xs beneath. There
   is no second style for this role. */
.ds-section__note {
	margin: var(--space-xs) 0 0;
	font-size: var(--text-sm);
	line-height: var(--text-sm-leading);
	color: var(--text-secondary);
	max-width: 68ch;
}

/* ---------------------------------------------------------------------
   Tabs — DESIGN_COMPONENTS.md §4.26
   --------------------------------------------------------------------- */

.ds-tabs__list {
	display: flex;
	gap: var(--space-2xs);
	/* Rule 1: the list sits on a light subtle rule running the full width. */
	border-bottom: var(--border-width-light) solid var(--border-color-subtle);
}

.ds-tabs__trigger {
	appearance: none;
	background: var(--action-ghost-bg);
	border: none;
	/* Rule 4: space-sm vertical, space-md horizontal. */
	padding: var(--space-sm) var(--space-md);
	font-family: var(--font-sans);
	font-size: var(--text-base);
	line-height: var(--text-base-leading);
	/* Rule 2: secondary, primary + medium when active. */
	color: var(--text-secondary);
	font-weight: var(--weight-normal);
	cursor: pointer;
	/* Rule 1: the active mark is a medium bottom border in
     border-color-selected. Held transparent when inactive so selection does
     not shift the row. */
	border-bottom: var(--border-width-medium) solid transparent;
	margin-bottom: calc(-1 * var(--border-width-light));
	transition:
		color var(--duration-fast) var(--ease-standard),
		border-color var(--duration-fast) var(--ease-standard);
}

.ds-tabs__trigger:hover {
	background: var(--action-ghost-bg-hover);
	color: var(--text-primary);
}

.ds-tabs__trigger--active {
	color: var(--text-primary);
	font-weight: var(--weight-medium);
	border-bottom-color: var(--border-color-selected);
}

/* ---------------------------------------------------------------------
   §2.7 Layout — the 6-column grid
   --------------------------------------------------------------------- */

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

.ds-grid__col {
	height: var(--space-xl);
	border-radius: var(--rounded-sm);
	background: var(--surface-sunken);
	border: var(--border-width-light) solid var(--border-color-subtle);
}

/* §2.7 rule 7: the ONLY breakpoint-dependent thing here — six columns
   becomes two below `narrow`. Written as a literal because a custom
   property cannot appear in a media query condition; `--breakpoint-narrow`
   in the token file records the intended value. */
@media (max-width: 639px) {
	.ds-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

/* ---------------------------------------------------------------------
   §4.8 Table — the border grammar, in full. A table carries a HEAVY solid
   left border marking where the structure starts (§2.2A rule 1) and a
   LIGHT solid border on the other three sides. Interior separators alone
   are not a table; they are a list wearing columns. `ds/Table.tsx` and any
   hand-rolled table markup (§4.19's loading specimen, `/design-system`'s own
   reference tables) both build on these classes.
   --------------------------------------------------------------------- */
.ds-table {
	width: 100%;
	/* §4.8: column widths come from the table's own declaration, never from
     the content of its cells. Auto layout measures every cell and
     re-measures whenever the rows change, so filtering a table resized its
     columns — a layout shift the reader caused but never asked for. Fixed
     layout reads the `<colgroup>` and nothing else, so a column is the same
     width before and after any filter, sort, or empty state. */
	table-layout: fixed;
	border-collapse: separate;
	border-spacing: 0;
	font-size: var(--text-sm);
	line-height: var(--text-sm-leading);
	border-left: var(--border-width-heavy) solid var(--border-color-default);
	border-top: var(--border-width-light) solid var(--border-color-default);
	border-right: var(--border-width-light) solid var(--border-color-default);
	border-bottom: var(--border-width-light) solid var(--border-color-default);
}

/* §2.6 rule 1: a table is data-bearing, so it lifts a hair off the page. The
   shadow sits on the scroll container rather than the table, so it is not
   clipped by the cell edges. */
.ds-table__scroll {
	overflow-x: auto;
	box-shadow: var(--elevation-shadow);
}

/* §4.8 rule 5: header labels are weight-medium in text-secondary. They were
   text-primary, which made a label read as data. */
.ds-table th {
	text-align: left;
	font-weight: var(--weight-medium);
	color: var(--text-secondary);
	padding: var(--space-xs);
	vertical-align: middle;
	/* §2.2A rule 1: medium separates a table header from its rows. */
	border-bottom: var(--border-width-medium) solid var(--border-color-default);
	white-space: nowrap;
}

.ds-table td {
	padding: var(--space-xs);
	white-space: nowrap;
	vertical-align: middle;
	/* A cell can never paint outside its own column. Under `table-layout:
     fixed` a cell does not grow to fit its content, so anything too wide
     simply drew over the neighbouring column — a row with two tags in the
     Account cell put its second tag on top of Amount. §3.8 rule 1 already
     says a cell is one line and truncates; this is what enforces it rather
     than leaving it to whether the content happens to fit. */
	overflow: hidden;
	text-overflow: ellipsis;
}

/* §4.8 rule 2: body rows separate with border-style-dotted — square dots, not
   a solid hairline. Drawn as a gradient because CSS cannot state a dash
   length (§2.2B rule 2). `border-collapse: separate` so a cell's own edge can
   never paint a second line under the dots. */
.ds-table tbody tr:not(:last-child) > * {
	border-bottom: none;
	background-image: linear-gradient(
		to right,
		var(--border-color-default) 0 var(--border-style-dotted-dash),
		transparent var(--border-style-dotted-dash) var(--ds-dotted-period)
	);
	background-repeat: repeat-x;
	background-position: bottom left;
	background-size: var(--ds-dotted-period) var(--border-width-light);
}

/* §4.8 rule 6: column separators are lighter than row separators — a reader
   scans across a row far more often than down a column. */
.ds-table th:not(:last-child),
.ds-table td:not(:last-child) {
	border-right: var(--border-width-light) solid var(--border-color-subtle);
}

/* §2.7 rule 4 / §4.8 rule 25: row height is one of the two `size-row-*`
   tokens (§2.12) — space-xs above and below plus the tallest content is the
   derivation that explains the two figures, not a ban on naming them.
   `min-height` only guarantees the floor for a row whose content is shorter
   than the text line. */
/* The height goes on the CELLS, not the row: `min-height` on a `<tr>` does
   nothing in table layout, so a control table's rows sized to their content
   and came out uneven — 24px beside 25px — which §2.7 rule 5 forbids. On a
   cell, `height` acts as a minimum, which is exactly the derived floor §2.7
   rule 4 describes. */
.ds-table--text td,
.ds-table--text th {
	height: var(--size-row-text);
}

.ds-table--control td,
.ds-table--control th {
	height: var(--size-row-control);
}

/* §3.4 rule 1: financial values align right. */
.ds-table__num {
	text-align: right;
	font-family: var(--font-mono);
}

/* ---------------------------------------------------------------------
   Small shared layouts
   --------------------------------------------------------------------- */

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

.ds-stack--tight {
	gap: var(--space-xs);
}

/* `.ds-stack`'s `gap` already sets the distance between items — a
   `.ds-caption` child's own `margin-top: space-sm` (meant for a caption
   sitting below a specimen, not inside a stack) stacks on top of it instead
   of replacing it, landing on a step (`space-xs` + `space-sm` = 12px) that
   is not one of §2.4's named rungs. */
.ds-stack > .ds-caption {
	margin-top: 0;
}

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

.ds-inline--tight {
	gap: var(--space-xs);
}

/* Screen-reader-only text. Ten components (Spinner's status label,
   InputField's hidden hints, and others) render `.sr-only` children; the
   host app defined this in polychrome, so the package must carry it. */
.ds .sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}
