/* symbolsoup — site stylesheet.
 *
 * A deliberately minimal system adapted from the "threshold" starter: design
 * tokens as CSS custom properties, automatic light/dark via prefers-color-scheme,
 * a compact reset, element scaffolding on one vertical rhythm, a centered reading
 * column, and the few symbolsoup-specific components (glyph specimen, annotation
 * rows, character index). One file, served as a Worker asset from /styles.css.
 *
 * Sections: tokens -> fonts -> reset -> scaffolding -> layout -> components.
 */

/* --- Tokens -------------------------------------------------------------- */
/* Change a value here and it updates everywhere. Dark mode is automatic: the
   dark overrides live in the prefers-color-scheme block below. */
:root {
  /* Colors: light theme (the default) */
  --background: #ffffff; /* page background */
  --background-soft: #f6f6f7; /* the glyph specimen, code, subtle fills */
  --font-color: #1f1f22; /* body text */
  --font-color-subtle: #6b6b70; /* codepoints, captions, footer, markers */
  --font-color-soft: #767676; /* quieter still: eyebrows, placeholders, the "browse all" link (kept at AA 4.5:1) */
  --border-color: #e5e5e7; /* hairlines and dividers */
  --border-strong: #8c8c8c; /* a visible boundary for UI controls (chips): meets the 3:1 non-text contrast minimum, unlike the hairline */
  --primary-color: #2563eb; /* links and highlights */

  /* Fonts */
  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  /* --font-glyph (the self-hosted display stack) is defined in the generated
     /fonts/faces.generated.css, linked before this file. */

  /* Sizes */
  --line-height: 1.6;
  --container-width: 45rem; /* reading measure: caps text/list content on detail pages */
  --container-wide: 82.5rem; /* 1320px frame so the grid inside is exactly 1280px (after the 1.25rem edge gutter each side). Grids fill it; detail content caps within. */
  --gutter: 1.25rem; /* space on the left and right edges */
  --header-height: 4rem;
}

@media (prefers-color-scheme: dark) {
  :root {
    --background: #18181b;
    --background-soft: #232327;
    --font-color: #ededed;
    --font-color-subtle: #a1a1aa;
    --font-color-soft: #8b8b93;
    --border-color: #2e2e33;
    --border-strong: #6a6a70;
    --primary-color: #7aa7ff;
  }
}

/* The self-hosted `@font-face` rules and the `--font-glyph` stack are generated
   from the shared face registry into /fonts/faces.generated.css (linked ahead of
   this file). See scripts/build-fonts.mjs and docs/overview.md → Fonts. */

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

/* Offset in-page anchor jumps (e.g. a marker deep-link into /glossary/notation) by
   the sticky header's height, so the targeted element lands below it, not under it.
   Scoped to the jump target (:target) rather than set as scroll-padding-top on the
   scroll container: a container padding also applies to the browser's caret-into-view
   scroll, so with the search box inside the sticky header, every keystroke nudged the
   page up until it crept away. scroll-margin-top on the anchored element alone keeps
   the deep-link offset without touching typing in the header. */
:target {
  scroll-margin-top: var(--header-height);
}

* {
  margin: 0;
}

body {
  min-height: 100svh;
  line-height: var(--line-height);
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  line-height: 1.15;
  text-wrap: balance;
}

p {
  text-wrap: pretty;
}

img,
picture,
svg {
  display: block;
  max-width: 100%;
  height: auto;
}

@media (prefers-reduced-motion: reduce) {
  /* The !important below is deliberate: this reduced-motion reset must override
     any animation/transition set later, whatever its specificity. */
  *,
  *::before,
  *::after {
    /* biome-ignore lint/complexity/noImportantStyles: reduced-motion override must win */
    animation-duration: 0.01ms !important;
    /* biome-ignore lint/complexity/noImportantStyles: reduced-motion override must win */
    animation-iteration-count: 1 !important;
    /* biome-ignore lint/complexity/noImportantStyles: reduced-motion override must win */
    transition-duration: 0.01ms !important;
  }
}

/* --- Scaffolding --------------------------------------------------------- */
/* Default styling for the basic elements, on the "spacing after" rhythm: every
   flow block owns the gap that follows it (margin-bottom only), so spacing never
   depends on margin-collapsing. 1.5rem is the site's spacing unit. */
body {
  font-family: var(--font-body);
  font-size: 1rem;
  color: var(--font-color);
  background: var(--background);
}

p,
ul,
ol {
  margin-block: 0 1.5rem;
}

h1,
h2,
h3 {
  font-weight: 700;
  margin-block: 2.5rem 1.5rem;
}

:is(h1, h2, h3):first-child {
  margin-block-start: 0;
}

h1 {
  font-size: 2rem;
}
h2 {
  font-size: 1.5rem;
}
h3 {
  font-size: 1.25rem;
}

a {
  color: var(--primary-color);
  text-underline-offset: 0.15em;
}

a:hover {
  text-decoration: none;
}

:focus-visible {
  outline: 2px solid var(--primary-color);
  border-radius: 0.25rem;
}

code,
kbd,
samp {
  font-family: var(--font-mono);
  font-size: 0.9em;
}

/* --- Layout -------------------------------------------------------------- */
/* The header inner, main, and footer all center on the same reading column and
   share the same edge gutter. Change either in the tokens above. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--background);
  border-block-end: 1px solid var(--border-color);
}

.site-header__inner,
.site-main,
.site-footer {
  max-width: var(--container-wide);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Grid pages (the homepage specimen sheet) run to the wide container; the shared
   header widens to match so the brand aligns with the grid's left edge. */
.site-main--wide,
.site-header__inner {
  max-width: var(--container-wide);
}

/* Brand on the left, then the search stretches to fill the rest of the row (a
   search-first front door). The search caps its own width so it never sprawls on
   wide viewports; past the cap the extra space trails to the right of it. */
.site-header__inner {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  min-height: var(--header-height);
}

.site-brand {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  color: var(--font-color);
  font-weight: 700;
  font-size: 1.0625rem;
  text-decoration: none;
}

/* The PER SIGN mark reads as an icon beside the wordmark, so size it near the
   cap height (not the full line box) and let its own currentColor track the theme.
   `block` drops the inline baseline gap so the flex centering is exact. */
.site-brand__mark {
  display: block;
  width: 1.5em;
  height: 1.5em;
  flex: none;
}

.site-main {
  padding-block: 2rem;
}

/* Reading-width column for prose pages (glossary, licenses): caps line length at the
   reading measure, left-aligned within the wide main so its left edge still lines up
   with the header brand and the capped content on character/index pages. */
.prose {
  max-width: var(--container-width);
}

/* Verbatim third-party legal notice (the Unicode License on /licenses): set off as
   a quiet, indented block in the soft caption color and a slightly smaller size, so
   the required boilerplate is present and readable without competing with the page. */
.license-notice {
  margin-block: 1.25rem;
  padding-inline-start: 1rem;
  border-inline-start: 2px solid var(--border-color);
  color: var(--font-color-soft);
  font-size: 0.875rem;
  max-width: 44rem;
}

.license-notice p {
  margin-block: 0.75rem;
}

/* Generous space above sets the footer off from page content (no rule needed); the
   provenance line leads, then the legal + nav clusters sit on a quieter second row. */
.site-footer {
  padding-block: 4.5rem 2.5rem;
  color: var(--font-color-subtle);
  font-weight: 500;
}

/* Footer links inherit the muted footer color and carry only a faint underline,
   which fills in to the text color on hover so each cluster reads as one quiet
   unit until touched. */
.site-footer a {
  color: inherit;
  text-decoration-color: var(--font-color-soft);
}

/* The provenance line: a source note about the data, set in italic (the editorial
   "aside" voice) so it reads distinctly from the upright legal boilerplate below.
   The brand mark reads as an icon beside the sentence, so size it near the text
   (not the header's wordmark height) and keep it on the baseline via the flex row. */
.site-footer__data {
  display: flex;
  align-items: baseline;
  gap: 0.5em;
  margin-block: 0;
  font-style: italic;
}

.site-footer__data .site-brand__mark {
  width: 0.9em;
  height: 0.9em;
  transform: translateY(0.1em);
}

/* Second row: attribution/legal on one side, the about-the-site nav on the other.
   Drops to a stacked layout when the two clusters can't share a line. The softer
   color (not a smaller size) marks it as quieter than the provenance statement. */
.site-footer__meta {
  display: flex;
  justify-content: space-between;
  gap: 1rem 1.5rem;
  flex-wrap: wrap;
  margin-block-start: 1rem;
  color: var(--font-color-soft);
}

/* Copyright + Licenses are upright legal boilerplate; a lighter weight lets them
   recede beneath the italic source note while the nav links keep the footer weight. */
.site-footer__legal {
  font-weight: 400;
}

.site-footer__nav {
  display: flex;
  gap: 1.25rem;
}

.site-footer a:hover {
  /* Hover darkens the link to the body text color; the underline follows via
     currentColor. Also override the global `a:hover` underline removal so hover
     strengthens the underline instead of dropping it. */
  color: var(--font-color);
  text-decoration-line: underline;
  text-decoration-color: currentColor;
}

/* --- Components ---------------------------------------------------------- */

/* A codepoint literal (U+2200): monospace and quiet, wherever it appears. */
.cp {
  font-family: var(--font-mono);
  color: var(--font-color-subtle);
  font-weight: 400;
}

/* Site search: a compact GET form in the header, plus a larger `--hero` instance
   on the homepage and the results page. Plain form controls, themed via tokens. */
.site-search {
  display: flex;
  gap: 0.4rem;
  align-items: center;
}

/* The label only carries the visually-hidden text; unwrap it so the input and
   button are direct flex items (and two forms on one page can't collide on ids). */
.site-search label {
  display: contents;
}

.site-search input[type="search"] {
  font: inherit;
  font-size: 0.9rem;
  min-width: 0;
  width: clamp(8rem, 26vw, 15rem);
  color: var(--font-color);
  background: var(--background-soft);
  border: 1px solid var(--border-color);
  border-radius: 0.5rem;
  padding: 0.35rem 0.6rem;
}

.site-search input::placeholder {
  color: var(--font-color-soft);
}

/* The native clear (×) button. WebKit renders it in a browser-default look (a small
   colored, gradient X that clashes with the monochrome UI); replace it with a flat ×
   drawn as a mask and tinted with a theme token, so it reads the same in both themes
   and darkens on hover. Firefox renders no cancel button, so there's nothing to style
   there. */
.site-search input[type="search"]::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
  width: 0.85rem;
  height: 0.85rem;
  cursor: pointer;
  background-color: var(--font-color-soft);
  -webkit-mask:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6 6 18 18M18 6 6 18' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E")
    center / contain no-repeat;
  mask:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6 6 18 18M18 6 6 18' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E")
    center / contain no-repeat;
}

.site-search input[type="search"]::-webkit-search-cancel-button:hover {
  background-color: var(--font-color);
}

/* The header instance: no button (submits on Enter). It grows to fill the row next
   to the brand, capped so it stays a comfortable bar rather than sprawling across a
   wide viewport. `relative` anchors the absolutely-positioned `⌘K` hint. */
.site-search--header {
  flex: 1;
  max-width: 60rem;
  position: relative;
}

/* A pill in the header (fully rounded). Reserve room on the right so the input's own
   text never slides under the `⌘K` hint, and a touch more on the left to balance the
   rounded ends. */
.site-search--header input[type="search"] {
  width: 100%;
  border-radius: 999px;
  padding-left: 0.9rem;
  padding-right: 3.4rem;
}

/* Once the field has text the `⌘K` hint gives way to the clear (×) button, which is
   narrower, so reclaim the reserved space to a tighter right padding. */
.site-search--header input[type="search"]:not(:placeholder-shown) {
  padding-right: 1.6rem;
}

/* The `⌘K` shortcut hint, pinned inside the input's right edge. `pointer-events:
   none` lets a click there still land on the input. Hidden while the box is focused
   (you're already there) and revealed from JS (see SEARCH_SHORTCUT in html.ts). */
.site-search__hint {
  position: absolute;
  right: 0.8rem;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
  font: inherit;
  font-size: 0.8rem;
  line-height: 1;
  color: var(--font-color-soft);
  background: var(--background);
  border: 1px solid var(--border-color);
  border-radius: 0.35rem;
  padding: 0.2rem 0.4rem;
}

/* Hide the `⌘K` hint whenever the box is focused (you're already there) or holds
   text (the clear button takes that corner instead), so the two never overlap. */
.site-search--header:focus-within .site-search__hint,
.site-search--header:has(input:not(:placeholder-shown)) .site-search__hint {
  opacity: 0;
}

.site-search button {
  font: inherit;
  font-size: 0.9rem;
  font-weight: 500;
  cursor: pointer;
  color: var(--background);
  background: var(--primary-color);
  border: 1px solid transparent;
  border-radius: 0.5rem;
  padding: 0.35rem 0.75rem;
}

.site-search button:hover {
  filter: brightness(1.08);
}

/* The hero instance: bigger, and the input grows to fill the row. */
.site-search--hero {
  margin-block: 0 2rem;
}

.site-search--hero input[type="search"] {
  flex: 1;
  width: auto;
  font-size: 1rem;
  padding: 0.55rem 0.8rem;
}

.site-search--hero button {
  font-size: 1rem;
  padding: 0.55rem 1rem;
}

.search-hint {
  color: var(--font-color-subtle);
}

/* Screen-reader-only text (the search input's label). */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Homepage front door: a centered search hero, paths into the full grid (/unicode)
   and the blocks directory (/unicode/blocks), and a curated shortlist of featured
   blocks (the full set lives in the directory; the grid lives on /unicode). */
.home__hero {
  text-align: center;
  padding-block: 2rem 0.5rem;
}

.home__hero h1 {
  font-size: 2.25rem;
}

.home__hero > p {
  color: var(--font-color-subtle);
  margin-block: 0.5rem 1.5rem;
}

.home__hero .site-search--hero {
  max-width: 34rem;
  margin-inline: auto;
}

/* The browse path is a tonal secondary, not a second filled button: search is the
   front door's one primary action (the filled hero button), so this sits a tier below
   it — a soft primary-tinted fill rather than a solid one — and doesn't compete for
   the eye. */
.home__cta {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-weight: 500;
  color: var(--primary-color);
  background: color-mix(in srgb, var(--primary-color) 12%, transparent);
  border-radius: 0.55rem;
  padding: 0.6rem 1.15rem;
  text-decoration: none;
}

.home__cta:hover {
  background: color-mix(in srgb, var(--primary-color) 20%, transparent);
}

.home__blocks {
  text-align: center;
  margin-block-start: 3rem;
}

.home__blocks-label {
  margin-block-end: 0.8rem;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--font-color-soft);
}

.home__blocks ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: center;
}

.home__blocks ul a {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  border: 1px solid var(--border-strong);
  border-radius: 2rem;
  padding: 0.4rem 0.95rem;
  font-size: 0.9rem;
  color: var(--font-color);
  text-decoration: none;
}

.home__blocks ul a:hover {
  border-color: var(--primary-color);
  color: var(--primary-color);
}

/* The "see the rest" link under the curated chips: a plain text link, not a chip,
   so it reads as a way out of the shortlist rather than one more featured block. */
.home__blocks-all {
  display: inline-block;
  margin-block-start: 1.25rem;
  font-size: 0.9rem;
  color: var(--font-color-soft);
  text-decoration: none;
}

.home__blocks-all:hover {
  color: var(--primary-color);
}

/* A quiet pointer to the machine front doors (JSON API + llms.txt) for agents and
   scripts, set well below the browse path in soft type so it never competes with the
   search-first hero. */
.home__machine {
  margin-block-start: 3rem;
  text-align: center;
  font-size: 0.85rem;
  color: var(--font-color-soft);
}

.home__machine a {
  color: var(--font-color-soft);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.home__machine a:hover {
  color: var(--primary-color);
}

/* The glyph specimen on a character page: a large, centered rendering in a soft
   framed box, modeled on a NamesList chart cell. */
.glyph-display {
  font-family: var(--font-glyph);
  font-size: 6rem;
  line-height: 1;
  text-align: center;
  /* Left-aligned, capped specimen: the page frame is wider, so the box sits at the
     left rather than stretching or centering across it. */
  max-width: 30rem;
  padding: 3rem 1rem;
  margin-block: 0 2rem;
  background: var(--background-soft);
  border: 1px solid var(--border-color);
  border-radius: 0.75rem;
}

/* Annotation rows (NamesList style): a marker glyph in a fixed gutter, then the
   text or cross-reference link. */
.annotations {
  list-style: none;
  padding: 0;
  max-width: var(--container-width);
}

.ann {
  display: flex;
  gap: 0.6rem;
  padding-block: 0.35rem;
  border-block-start: 1px solid var(--border-color);
}

/* The marker links to its definition in the notation glossary but stays a quiet
   glyph — the subtle marker color, no underline — so it never competes with the
   blue link to a target character beside it; color alone tells the two apart.
   Hover just deepens its color. */
.ann .marker {
  flex: none;
  width: 1.25rem;
  text-align: center;
  color: var(--font-color-subtle);
  text-decoration: none;
}

.ann .marker:hover {
  color: var(--font-color);
  text-decoration: none;
}

.ann .marker abbr {
  text-decoration: none;
}

.no-annotations {
  color: var(--font-color-subtle);
  font-style: italic;
}

/* The notation glossary tables (/glossary/notation): a quiet reference grid. The
   marker column carries the chart glyph plus the raw NamesList source character; a
   deep-linked row lights up via :target so a jump from a character page is obvious. */
/* On a narrow viewport the multi-column table scrolls inside its own box rather
   than pushing the whole page sideways. */
.table-scroll {
  max-width: var(--container-width);
  margin-block: 1rem 2rem;
  overflow-x: auto;
}

.notation {
  border-collapse: collapse;
  width: 100%;
  min-width: 30rem; /* keep columns legible; the wrapper scrolls when narrower */
  font-size: 0.95rem;
}

.notation th,
.notation td {
  text-align: start;
  vertical-align: baseline;
  padding: 0.5rem 0.75rem;
  border-block-start: 1px solid var(--border-color);
}

.notation thead th {
  border-block-start: 0;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--font-color-soft);
}

.notation tbody th {
  font-weight: 500;
  white-space: nowrap;
}

/* Markers render in the UI font at body size and the subtle marker color, exactly
   as they do on character pages (`.ann .marker`); only the target-character glyphs
   use `--font-glyph`, so a marker like the cross-reference arrow is identical here. */
.notation .marker-glyph {
  font-size: 1rem;
  color: var(--font-color-subtle);
}

.notation__marker {
  white-space: nowrap;
}

.notation__example {
  color: var(--font-color-subtle);
}

.notation tr:target {
  background: var(--background-soft);
}

/* The homepage specimen grid: a responsive grid of square character cards, modeled
   on the hand-drawn reference — the codepoint sits at the top, the glyph fills the
   center, and the name runs along the bottom. Long names wrap to two lines and then
   truncate (the full name is on the card's own page, its title, and its aria-label). */
.card-grid {
  display: grid;
  /* 8.5rem min fits 8 cards across at the ~80rem frame (9.5rem only fit 7); the
     grid still reflows to fewer columns as the viewport narrows. */
  grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  gap: 0.75rem;
  margin-block-end: 1rem;
}

.card {
  display: grid;
  grid-template-rows: auto 1fr auto;
  aspect-ratio: 1 / 1;
  padding: 0.75rem 0.85rem;
  gap: 0.35rem;
  border: 1px solid var(--border-color);
  border-radius: 0.6rem;
  background: var(--background);
  color: var(--font-color);
  text-decoration: none;
  overflow: hidden;
  /* Skip layout/paint for offscreen cards: the specimen grid can run to hundreds
     of cards. The intrinsic size keeps the scrollbar stable (cards are square,
     ~9.5rem wide at the grid's minimum column). */
  content-visibility: auto;
  contain-intrinsic-size: auto 9.5rem;
  transition:
    border-color 0.15s,
    background 0.15s;
}

.card:hover {
  border-color: var(--primary-color);
  background: var(--background-soft);
}

.card__cp {
  font-size: 0.7rem;
  /* .cp already sets the mono face + subtle color. */
}

.card__glyph {
  display: grid;
  place-items: center;
  font-family: var(--font-glyph);
  font-size: 3rem;
  line-height: 1;
  min-height: 0; /* let the glyph row shrink instead of overflowing the card */
}

.card__name {
  font-size: 0.7rem;
  line-height: 1.25;
  color: var(--font-color-subtle);
  /* Wrap to at most two lines, then ellipsis. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

/* In-block subheadings (the NamesList `@` groups, e.g. "Set membership"). The block page
   defaults to the flat specimen grid; when a block has curated subheadings, a
   flat/grouped toggle appears and the grouped view splits the same cards under an
   anchored heading per group. */

/* Left cluster of the block head: the range/count text with the flat/grouped toggle
   beside it (when the block has subheadings). Keeping them together lets the head's
   space-between still push the prev/next stepper to the far right. `center` seats the
   taller toggle pill against the single line of count text; the cluster's own baseline
   stays the text's, so the stepper still aligns to the count. With no toggle it holds
   only the range, rendering identically to a bare count. */
.block-head__lead {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.75rem;
}

/* One subheading group: its anchored heading over that group's grid. Groups stack with
   breathing room so the subheadings read as real section breaks. */
.block-section {
  margin-block-start: 2.25rem;
  /* Anchor jumps (and :target) land the heading clear of the sticky site header. */
  scroll-margin-top: 5rem;
}

.block-section__title {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin-block: 0 1rem;
  padding-block-end: 0.4rem;
  border-block-end: 1px solid var(--border-color);
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--font-color);
}

.block-section__count {
  font-size: 0.8rem;
  font-weight: 400;
  color: var(--font-color-soft);
}

/* The bar above a result set: the count line and the grid/list view toggle sit
   together at the left, so the toggle stays anchored to the count (and the results)
   instead of stranding at the far container edge when the grid is only a few wide. */
.results-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 0.85rem;
  margin-block-end: 1rem;
}

.results-bar > .results-count {
  margin: 0;
}

/* The grid/list segmented control: two link segments sharing a rounded border, the
   active view filled in. A plain navigation (per-view URL), so it needs no JS. */
.view-toggle {
  display: inline-flex;
  flex: none;
  border: 1px solid var(--border-color);
  border-radius: 0.5rem;
  overflow: hidden;
}

.view-toggle__opt {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.6rem;
  font-size: 0.85rem;
  color: var(--font-color-subtle);
  text-decoration: none;
  background: var(--background);
}

.view-toggle__opt + .view-toggle__opt {
  border-inline-start: 1px solid var(--border-color);
}

.view-toggle__opt svg {
  width: 1rem;
  height: 1rem;
}

.view-toggle__opt:hover {
  background: var(--background-soft);
  color: var(--font-color);
}

/* The active segment reads as pressed: filled and in the full text color. */
.view-toggle__opt[aria-current="true"] {
  background: var(--background-soft);
  color: var(--font-color);
  font-weight: 500;
}

/* The /unicode landing index: one quiet row per character; the glyph leads, then
   its codepoint and name. The whole row is the link. */
.character-index {
  list-style: none;
  padding: 0;
  max-width: var(--container-width);
}

.character-index a {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  padding: 0.5rem 0.75rem;
  margin-inline: -0.75rem;
  border-radius: 0.5rem;
  color: var(--font-color);
  text-decoration: none;
}

.character-index a:hover {
  background: var(--background-soft);
}

.character-index .glyph {
  font-family: var(--font-glyph);
  font-size: 1.5rem;
  line-height: 1;
  flex: none;
  width: 2rem;
  text-align: center;
}

/* The blocks directory (/unicode/blocks): one row per block — name, range, count.
   The whole row is the link. */
.block-index {
  list-style: none;
  padding: 0;
  max-width: var(--container-width);
}

.block-index a {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.3rem 0.75rem;
  padding: 0.6rem 0.75rem;
  margin-inline: -0.75rem;
  border-radius: 0.5rem;
  color: var(--font-color);
  text-decoration: none;
}

.block-index a:hover {
  background: var(--background-soft);
}

.block-index__name {
  font-weight: 500;
}

.block-index__count {
  margin-inline-start: auto;
  color: var(--font-color-soft);
  font-size: 0.9rem;
}

/* Cross-reference / decomposition targets follow the NamesList chart layout:
   codepoint, then the target glyph, then the name. The glyph uses the display
   stack; the codepoint stays quiet (via .cp) and the name carries the link color. */
.ann a {
  text-decoration: none;
}

.ann a:hover {
  text-decoration: underline;
}

.ann .glyph {
  font-family: var(--font-glyph);
  color: var(--font-color);
}

/* Breadcrumb trail (character + block pages): a quiet inline path back up the
   hierarchy, with "›" separators between crumbs and the current page in full ink. */
.breadcrumb ol {
  list-style: none;
  padding: 0;
  margin-block: 0 1.5rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.875rem;
}

.breadcrumb li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--font-color-subtle);
}

.breadcrumb li + li::before {
  content: "›";
  color: var(--font-color-subtle);
}

.breadcrumb a {
  color: var(--font-color-subtle);
  text-decoration: none;
}

.breadcrumb a:hover {
  color: var(--primary-color);
}

.breadcrumb [aria-current="page"] {
  color: var(--font-color);
}

/* Zero the heading's top margin when a breadcrumb precedes it (the breadcrumb
   already owns the gap below itself). */
.breadcrumb + h1 {
  margin-block-start: 0;
}

/* The /unicode landing groups its index by block; each block heading links to
   that block's own index page. */
.block-group {
  margin-block-end: 2.5rem;
}

.block-group h2 a {
  color: var(--font-color);
  text-decoration: none;
}

.block-group h2 a:hover {
  color: var(--primary-color);
}

/* Block heading band: the codepoint range sits on the left, a compact prev/next
   stepper on the right of the same row, so lateral navigation adds no vertical band
   before the grid. The h1 stays on its own line above. */
.block-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.4rem 2rem;
  margin-block-end: 2rem;
}

.block-head__range {
  margin-block-end: 0;
}

/* Compact stepper on the heading row: fixed "Previous"/"Next" strings (no block name)
   so the control keeps a constant size and doesn't shift as you step between blocks.
   The named links live in the footer pager. A missing neighbor at the ends of the list
   drops out (the cluster is content-width). */
.block-stepper {
  display: flex;
  align-items: baseline;
  gap: 1.5rem;
  font-size: 0.9rem;
}

.block-step {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  text-decoration: none;
  white-space: nowrap;
  color: var(--font-color-subtle);
}

a.block-step:hover {
  color: var(--primary-color);
}

.block-step--empty {
  display: none;
}

.block-step__dir {
  color: var(--font-color-soft);
}

/* Narrow: range and stepper can't share a row, so the head stacks and the stepper
   spreads full width, prev-left / next-right. Fixed-width strings never overflow. */
@media (max-width: 40rem) {
  .block-head {
    flex-direction: column;
    align-items: stretch;
    gap: 0.6rem;
  }

  /* Stacked full width: count left, toggle right on the same row (mirrors the stepper). */
  .block-head__lead {
    justify-content: space-between;
  }

  .block-stepper {
    justify-content: space-between;
  }

  .block-step--empty {
    display: block;
    visibility: hidden;
  }
}

/* Footer pager: the named prev/next, a full-width strip after the grid. Prev pulls
   left, next right; an absent neighbor keeps its (hidden) slot so the survivor holds
   its edge. Long names ellipsize rather than wrap. */
.block-pager {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin-block-start: 3rem;
  font-size: 0.95rem;
}

.block-pager__link {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  min-width: 0;
  max-width: 48%;
  text-decoration: none;
  color: var(--font-color-subtle);
}

a.block-pager__link:hover {
  color: var(--primary-color);
}

.block-pager__link--empty {
  visibility: hidden;
}

.block-pager__dir,
.block-pager__label {
  color: var(--font-color-soft);
  flex: none;
}

.block-pager__name {
  font-weight: 500;
  color: var(--font-color);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

a.block-pager__link:hover .block-pager__name {
  color: var(--primary-color);
}

@media (max-width: 30rem) {
  .block-pager__label {
    display: none;
  }
}

/* Back-to-top: a floating affordance revealed by the layout's scroll script once
   a page has scrolled past one viewport (see the TO_TOP script in html.ts). Sits
   above the sticky header (z-index 10) and, with no JS, stays out entirely via the
   `hidden` attribute. It's kept in flow but invisible until `--visible` so its
   entrance and exit can fade; the reduced-motion reset above zeroes that transition. */
.to-top {
  position: fixed;
  right: 2rem;
  bottom: 2rem;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3.375rem;
  height: 3.375rem;
  padding: 0;
  color: var(--font-color);
  background: var(--background);
  border: 1px solid var(--border-color);
  border-radius: 50%;
  box-shadow:
    0 1px 3px rgb(0 0 0 / 0.12),
    0 4px 12px rgb(0 0 0 / 0.08);
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transform: translateY(0.5rem);
  transition:
    opacity 0.2s ease,
    visibility 0.2s ease,
    transform 0.2s ease;
}

/* Setting `display` above overrides the UA `[hidden] { display: none }`, so restate
   it: with no JS the button ships `hidden` and must stay out of the layout entirely
   (the script clears the attribute and drives visibility via `--visible` instead). */
.to-top[hidden] {
  display: none;
}

.to-top--visible {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.to-top:hover {
  color: var(--primary-color);
  border-color: var(--primary-color);
}

.to-top svg {
  width: 1.5rem;
  height: 1.5rem;
}
