/* studkz-com — plain, mobile-first CSS. Base rules target small screens;
   min-width media queries add space on larger ones.

   Flat UI (flat-ui/flat-ui.css, loaded first) owns buttons and badges and the
   --ui-* tokens. This file holds only site layout and content styles: it may
   position Flat UI components but must not restyle them.

   The look is an academic notebook for students: warm paper, ink text,
   Literata (a book serif) for titles and reading, Onest (a friendly
   grotesk) for everything else, the brand green with a highlighter yellow
   as accents. Cards are two nested surfaces (a tinted rim around a white
   core). Motion animates transform and opacity only, eases out with a
   spring-like curve, and stops under prefers-reduced-motion. */

/* Fonts: self-hosted variable subsets (the CSP allows only 'self'); the
   unicode ranges load a subset only when the page uses it. Both families
   cover Kazakh, Russian and English. Licences: fonts/OFL-*.txt. */
@font-face {
  font-family: "Literata";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("../fonts/literata-v40-cyrillic-ext.woff2") format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

@font-face {
  font-family: "Literata";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("../fonts/literata-v40-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Literata";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("../fonts/literata-v40-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Literata";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("../fonts/literata-v40-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Onest";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("../fonts/onest-v11-cyrillic-ext.woff2") format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

@font-face {
  font-family: "Onest";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("../fonts/onest-v11-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Onest";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("../fonts/onest-v11-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Onest";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("../fonts/onest-v11-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* Paper and ink */
  --background: #f8f5ec;
  --surface: #ffffff;
  --text: #17221a;
  --muted: #5a6457;
  --faint: #8b9286;
  --link: #3d7a12;
  --highlight: #fbe27a;
  --mint: #ebf5df;
  --notice: #fff6dc;
  --hairline: rgba(23, 34, 26, 0.08);
  --hairline-strong: rgba(23, 34, 26, 0.16);
  --rim: rgba(23, 34, 26, 0.045);
  --rim-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.9);
  --toc-surface: #fcfaf4;
  --toc-hover: rgba(23, 34, 26, 0.035);
  --toc-active: #eeeae0;

  /* Shape: the rim is the outer surface of a card, the core sits inside
     it with a radius smaller by the rim, so the curves stay concentric. */
  --radius-xl: 28px;
  --radius-lg: 22px;
  --radius-md: 16px;
  --radius-sm: 12px;
  --radius: var(--radius-sm);
  --rim-width: 6px;

  --shadow-soft: 0 1px 2px rgba(23, 34, 26, 0.04), 0 14px 32px -18px rgba(23, 34, 26, 0.16);
  --shadow-lift: 0 2px 6px rgba(23, 34, 26, 0.05), 0 28px 48px -22px rgba(23, 34, 26, 0.24);
  --shadow-float: 0 1px 2px rgba(23, 34, 26, 0.05), 0 24px 64px -20px rgba(23, 34, 26, 0.28);

  --ease-spring: cubic-bezier(0.32, 0.72, 0, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  /* The article aside and its contents card, named as in the design and
     mapped onto the palette above. */
  --ink: var(--text);
  --core: var(--toc-surface);
  --shell: var(--toc-hover);
  --sage-deep: var(--text);
  --core-highlight: var(--rim-highlight);
  --ease: var(--ease-spring);
  --bezel-pad: var(--rim-width);
  --bezel-radius: 2rem;
  --nav-clearance: 6.5rem;
  --z-aside: 5;

  --font: "Onest", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-serif: "Literata", "Iowan Old Style", "PT Serif", Georgia, serif;

  --gutter: 16px;
  --page: 1160px;
  --measure: 44rem;
  --section-space: 3rem;
}

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

html {
  -webkit-text-size-adjust: 100%;
  text-rendering: optimizeLegibility;
}

/* The body's background spreads to the whole canvas, so the paper layers
   below (fixed, z-index -1) paint over it and under all content. */
body {
  margin: 0;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--background);
  color: var(--text);
  font-family: var(--font);
  font-size: 1rem;
  line-height: 1.6;
  overflow-wrap: anywhere;
  -webkit-font-smoothing: antialiased;
}

/* Paper: two soft washes of brand green and highlighter yellow, then a
   faint grain. Both layers are fixed and never take clicks. */
body::before,
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}

body::before {
  background:
    radial-gradient(42rem 28rem at 8% -6%, rgba(114, 192, 44, 0.13), transparent 70%),
    radial-gradient(36rem 24rem at 100% 0%, rgba(251, 226, 122, 0.22), transparent 72%);
}

body::after {
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

a {
  color: var(--link);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  transition: color 0.3s var(--ease-out), text-decoration-color 0.3s var(--ease-out);
}

a:hover {
  color: var(--ui-brand-active);
}

:focus-visible {
  outline: 3px solid var(--ui-brand);
  outline-offset: 2px;
}

::selection {
  background: var(--highlight);
  color: var(--text);
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: var(--font-serif);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.01em;
  margin: 1.5em 0 0.5em;
  text-wrap: balance;
}

h1 {
  font-size: 2rem;
  font-weight: 650;
  line-height: 1.1;
  letter-spacing: -0.025em;
  margin-top: 0.5em;
}

h2 {
  font-size: 1.4rem;
}

h3 {
  font-size: 1.2rem;
}

h4,
h5,
h6 {
  font-size: 1rem;
}

.container {
  width: 100%;
  max-width: var(--page);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

main.container {
  flex: 1 0 auto;
  padding-top: 1.5rem;
  padding-bottom: var(--section-space);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  left: var(--gutter);
  top: -3rem;
  padding: 0.5rem 1rem;
  background: var(--surface);
  border-radius: 999px;
  box-shadow: var(--shadow-soft);
  z-index: 40;
}

.skip-link:focus {
  top: 0.5rem;
}

.muted {
  color: var(--muted);
}

.lead {
  font-size: 1.1rem;
  color: var(--muted);
}

/* Eyebrow: a small pill over a page heading (or the article's summary)
   that names the section of the site the page belongs to. */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
  padding: 0.3rem 0.75rem 0.3rem 0.6rem;
  border-radius: 999px;
  background: var(--surface);
  box-shadow: 0 0 0 1px var(--hairline), var(--shadow-soft);
  color: var(--muted);
  font-family: var(--font);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  line-height: 1.4;
  text-transform: uppercase;
}

.eyebrow::before {
  content: "";
  flex: none;
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  background: var(--ui-brand);
  box-shadow: 0 0 0 3px rgba(114, 192, 44, 0.18);
}

/* Action rows are always right-aligned; on narrow screens they wrap and
   stay on the right edge. */
.actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: 0.5rem;
}

/* Header: a floating island detached from the top edge, with the logo on
   the left and two 44px icon controls (search, menu) on the right, at
   every width. It scrolls with the page. The menu panel is a dropdown
   that floats over the page under the island's right edge, so opening it
   moves nothing. */
.site-header {
  position: relative;
  z-index: 25;
  padding: 0.75rem var(--gutter) 0;
}

.site-header > .container {
  max-width: calc(var(--page) - 2 * var(--gutter));
}

.header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.3rem 0.3rem 0.3rem 0.5rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.86);
  box-shadow: 0 0 0 1px var(--hairline), var(--rim-highlight), var(--shadow-soft);
}

/* The brand gives way first, so the controls never leave the screen. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  min-width: 0;
  color: var(--text);
  font-weight: 700;
  font-size: 1.1rem;
  letter-spacing: -0.02em;
  text-decoration: none;
}

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

.brand-mark {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  background: var(--ui-brand);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35), inset 0 -2px 0 rgba(23, 34, 26, 0.12);
  color: #fff;
  font-family: var(--font-serif);
  font-size: 1.15rem;
  font-weight: 700;
}

.brand-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.brand-tld {
  color: var(--faint);
  font-weight: 500;
}

.header-controls {
  display: flex;
  flex: none;
  align-items: center;
  gap: 0.125rem;
  margin-left: auto;
}

/* The menu icon's three lines fold into a cross while the panel is open:
   the outer lines meet in the middle and turn, the middle one fades. */
[data-menu-toggle] [data-line] {
  transform-box: view-box;
  transform-origin: 12px 12px;
}

[data-menu-toggle][aria-expanded="true"] [data-line="top"] {
  transform: rotate(45deg) translateY(5px);
}

[data-menu-toggle][aria-expanded="true"] [data-line="middle"] {
  opacity: 0;
  transform: scaleX(0.2);
}

[data-menu-toggle][aria-expanded="true"] [data-line="bottom"] {
  transform: rotate(-45deg) translateY(-5px);
}

/* Menu panel: navigation, then the account, then the language switch, top
   to bottom in DOM and tab order. It is a card under the island, out of the
   page flow: the nav spans the island's width 8px below it and lets clicks
   through, the card inside it takes them. Its groups rise in one after
   another when it opens. A long menu scrolls inside the card. */
.site-menu {
  position: absolute;
  top: 100%;
  left: var(--gutter);
  right: var(--gutter);
  width: auto;
  max-width: calc(var(--page) - 2 * var(--gutter));
  margin: 0 auto;
  padding: 0.5rem 0 0;
  pointer-events: none;
}

.site-menu-inner {
  display: grid;
  gap: 0.5rem;
  max-height: calc(100vh - 6rem);
  max-height: calc(100dvh - 6rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0.5rem;
  transform-origin: top right;
  pointer-events: auto;
  border: var(--rim-width) solid var(--rim);
  border-radius: var(--radius-xl);
  background: var(--surface) padding-box;
  box-shadow: 0 0 0 1px var(--hairline), var(--shadow-float);
}

.menu-links {
  display: grid;
  margin: 0;
  padding: 0;
  list-style: none;
}

.menu-links a {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 0 0.75rem;
  border-radius: var(--radius-sm);
  color: var(--text);
  font-weight: 600;
  text-decoration: none;
  transition: background-color 0.3s var(--ease-out), color 0.3s var(--ease-out);
}

.menu-links a[hidden] {
  display: none;
}

.menu-links a:hover {
  background: var(--mint);
  color: var(--ui-brand-active);
}

.menu-group {
  display: grid;
  gap: 0.25rem;
  padding-top: 0.75rem;
  border-top: 1px dashed var(--hairline-strong);
}

.menu-label,
.session-name {
  margin: 0;
  padding: 0 0.75rem;
}

.menu-label {
  color: var(--faint);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.lang-switch {
  display: flex;
  gap: 0.25rem;
  margin: 0;
  padding: 0 0.25rem 0.25rem;
  list-style: none;
}

.session-name {
  font-weight: 600;
  overflow-wrap: anywhere;
}

/* Page heading: the eyebrow, the title with a hand-drawn green stroke
   under it, and the lead. */
.page-hero {
  padding: 1rem 0 0.5rem;
}

.page-hero h1 {
  margin: 1rem 0 0.75rem;
}

.page-hero h1::after {
  content: "";
  display: block;
  width: 4.5rem;
  height: 0.6rem;
  margin-top: 0.6rem;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 10'%3E%3Cpath d='M2 6.5c8-4 14-4 22-1s14 3 22-1 14-3.5 24 0' fill='none' stroke='%2372c02c' stroke-width='3' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat left center / contain;
}

.page-hero > p.muted {
  max-width: 38rem;
  margin: 0;
  font-size: 1.05rem;
}

/* Search form: the field and its buttons sit together in one island (the
   form's ::before draws it behind them), its core rounded 5px wider than
   the 10px Flat UI button inside so the corners stay concentric; the
   filters follow as toggle chips. Used by search, the landing page and My downloads. */
.search-form {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.75rem 0.25rem;
  margin: 1.5rem 0;
}

.search-form::before {
  content: "";
  grid-row: 1;
  grid-column: 1 / -1;
  align-self: stretch;
  border: var(--rim-width) solid var(--rim);
  border-radius: calc(15px + var(--rim-width));
  background: var(--surface) padding-box;
  box-shadow: 0 0 0 1px var(--hairline), var(--rim-highlight), var(--shadow-soft);
  transition: box-shadow 0.4s var(--ease-out);
}

.search-form:focus-within::before {
  box-shadow: 0 0 0 1px var(--ui-brand), 0 0 0 5px rgba(114, 192, 44, 0.16), var(--shadow-lift);
}

.search-form input[type="search"] {
  grid-row: 1;
  grid-column: 1;
  position: relative;
  width: 100%;
  min-width: 0;
  height: 64px;
  padding: 0 0.5rem 0 calc(var(--rim-width) + 1rem);
  border: 0;
  border-radius: calc(15px + var(--rim-width));
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 1.05rem;
}

.search-form input[type="search"]::placeholder {
  color: var(--faint);
}

.search-form input[type="search"]:focus {
  outline: none;
}

.search-form .actions {
  grid-row: 1;
  grid-column: 2;
  position: relative;
  flex-wrap: nowrap;
  padding-right: calc(var(--rim-width) + 5px);
}

.search-filters {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: flex-end;
  gap: 0.5rem;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
  color: var(--muted);
}

.search-filters label {
  display: inline-flex;
  align-items: flex-start;
  gap: 0.5rem;
  min-width: 0;
  max-width: 100%;
  min-height: 44px;
  padding: 0.6875rem 1rem 0.6875rem 0.8rem;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.7);
  font-size: 0.9rem;
  line-height: 1.4;
  cursor: pointer;
  transition: background-color 0.35s var(--ease-out), border-color 0.35s var(--ease-out), color 0.35s var(--ease-out);
}

.search-filters label:hover {
  border-color: var(--hairline-strong);
}

.search-filters label:has(input:checked) {
  background: var(--mint);
  border-color: rgba(114, 192, 44, 0.45);
  color: var(--text);
}

.search-filters input[type="checkbox"] {
  flex: 0 0 20px;
  width: 20px;
  height: 20px;
  margin: 0;
  accent-color: var(--ui-brand);
}

.search-filters input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--ui-brand);
  outline-offset: 3px;
}

.search-filters label > span {
  min-width: 0;
  overflow-wrap: anywhere;
}

.search-filters .search-exact-control {
  display: grid;
  gap: 0.25rem;
  min-width: 0;
  max-width: 100%;
}

/* Recent searches: a panel site.js hangs under the search field (it sets
   top, left and width to the field's), above the filter chips. */
.search-form {
  position: relative;
}

.recent-searches {
  position: absolute;
  z-index: 20;
  margin-top: 0.4rem;
  overflow: hidden;
  border-radius: 15px;
  background: var(--surface);
  box-shadow: 0 0 0 1px var(--hairline), var(--shadow-lift);
}

.recent-searches[hidden] {
  display: none;
}

.recent-searches ul {
  margin: 0;
  padding: 0.35rem 0;
  list-style: none;
}

.recent-searches [role="option"] {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  min-height: 40px;
  padding: 0 0.35rem 0 1rem;
  color: var(--text);
  cursor: pointer;
  transition: background-color 0.2s var(--ease-out);
}

.recent-searches [role="option"]:hover,
.recent-searches [role="option"][aria-selected="true"] {
  background: var(--mint);
}

.recent-searches svg {
  flex: none;
  fill: none;
  stroke: var(--faint);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.recent-searches [role="option"]:hover svg,
.recent-searches [role="option"][aria-selected="true"] svg {
  stroke: var(--ui-brand);
}

.recent-searches-text {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.recent-searches-remove {
  flex: none;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--faint);
  font: inherit;
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
}

.recent-searches-remove:hover,
.recent-searches-remove:focus-visible {
  color: var(--ui-brand-active);
  background: rgba(114, 192, 44, 0.12);
}

.recent-searches-clear {
  display: block;
  width: 100%;
  padding: 0.6rem 1rem;
  border: 0;
  border-top: 1px solid var(--hairline);
  background: transparent;
  color: var(--ui-brand-active);
  font: inherit;
  font-size: 0.875rem;
  text-align: left;
  cursor: pointer;
}

.recent-searches-clear:hover {
  color: var(--ui-brand-hover);
  text-decoration: underline;
}

@media (pointer: coarse) {
  .recent-searches [role="option"],
  .recent-searches-clear {
    min-height: 44px;
  }

  .recent-searches-remove {
    width: 44px;
    height: 44px;
  }
}

/* Landing */
.hero {
  padding: 1.5rem 0 0.5rem;
}

.hero h1 {
  margin: 1rem 0 0.75rem;
}

.points ul {
  display: grid;
  gap: 0.75rem;
  margin: 1rem 0 2rem;
  padding: 0;
  list-style: none;
  color: var(--muted);
}

.points li {
  padding: 1rem 1.25rem;
  border-radius: var(--radius-lg);
  background: rgba(255, 255, 255, 0.7);
  box-shadow: 0 0 0 1px var(--hairline);
}

/* Cards: a tinted rim (the border) around a white core (the padding box),
   with a hairline ring and a soft shadow. Lists of them rise in one after
   another; on hover a card lifts and its stronger shadow fades in. */
.results {
  display: grid;
  gap: 0.875rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.result {
  position: relative;
  padding: 1.1rem 1.25rem;
  border: var(--rim-width) solid var(--rim);
  border-radius: var(--radius-xl);
  background: var(--surface) padding-box;
  box-shadow: 0 0 0 1px var(--hairline), var(--rim-highlight), var(--shadow-soft);
}

.result::before {
  content: "";
  position: absolute;
  inset: calc(-1 * var(--rim-width));
  border-radius: inherit;
  box-shadow: var(--shadow-lift);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.6s var(--ease-out);
}

.result:hover::before {
  opacity: 1;
}

.result-title {
  margin: 0 0 0.35rem;
  font-size: 1.15rem;
  line-height: 1.3;
}

.result-title a {
  color: var(--text);
  text-decoration: none;
  transition: color 0.3s var(--ease-out);
}

.result-title a:hover {
  color: var(--ui-brand-active);
}

/* In lists with no other controls the title's link covers the whole card. */
.search-results .result-title a::after,
.article-related .result-title a::after,
.favorites-page .result-title a::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: calc(var(--radius-xl) - var(--rim-width));
}

.result-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0.5rem;
  color: var(--muted);
  font-size: 0.875rem;
}

.result-description {
  margin: 0 0 0.5rem;
  color: var(--muted);
  font-size: 0.95rem;
  line-height: 1.6;
}

/* The card's padding is its bottom edge. */
.result > :last-child {
  margin-bottom: 0;
}

.result-count {
  margin: 0 0 1rem;
  font-size: 0.875rem;
  font-weight: 500;
}

.pagination {
  margin: 1.5rem 0 0;
}

/* Narrow screens: the page counter gets its own row so Previous and Next
   stay side by side. */
.pagination-status {
  flex: 1 0 100%;
  text-align: right;
  font-size: 0.875rem;
}

/* Notices: empty states and alerts, on a sheet of graph paper. */
.notice {
  padding: 1.25rem 1.5rem;
  margin: 1rem 0;
  border-radius: var(--radius-lg);
  background:
    linear-gradient(var(--notice), rgba(255, 246, 220, 0.7)),
    repeating-linear-gradient(0deg, transparent 0 23px, rgba(23, 34, 26, 0.05) 23px 24px),
    repeating-linear-gradient(90deg, transparent 0 23px, rgba(23, 34, 26, 0.05) 23px 24px);
  background-blend-mode: multiply;
  box-shadow: 0 0 0 1px rgba(214, 170, 52, 0.22);
}

.notice > :first-child {
  margin-top: 0;
}

.notice > :last-child {
  margin-bottom: 0;
}

.notice h2 {
  margin-top: 0;
  font-size: 1.2rem;
}

.notice-ok {
  background: var(--mint);
  box-shadow: 0 0 0 1px rgba(114, 192, 44, 0.35);
}

/* Two-column pages (My downloads, Add article): the heading and the main
   form or summary in the aside, the list beside it. One column, aside
   first, on narrow screens. */
.page-split {
  display: grid;
  gap: 1.5rem;
}

.page-aside,
.page-main {
  min-width: 0;
}

.page-main > .search-form:first-child,
.page-main > .visually-hidden:first-child + .notice {
  margin-top: 0;
}

.page-main-title {
  margin: 0 0 1rem;
  font-size: 1.35rem;
}

/* Add article: the upload form is a card; the file field is a large
   dashed drop zone (files can be dropped on it), its button a dark pill
   beside the chosen file's name. */
.upload-form {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 1.25rem;
  margin: 1.25rem 0 0;
  border: var(--rim-width) solid var(--rim);
  border-radius: var(--radius-xl);
  background: var(--surface) padding-box;
  box-shadow: 0 0 0 1px var(--hairline), var(--rim-highlight), var(--shadow-soft);
}

.upload-form label {
  font-family: var(--font-serif);
  font-size: 1.1rem;
  font-weight: 600;
}

.upload-form input[type="file"] {
  width: 100%;
  max-width: 100%;
  padding: 1.75rem 1rem;
  border: 2px dashed rgba(114, 192, 44, 0.45);
  border-radius: var(--radius-lg);
  background:
    radial-gradient(circle at 1px 1px, rgba(23, 34, 26, 0.09) 1px, transparent 1.5px) 0 0 / 14px 14px,
    var(--mint);
  color: var(--muted);
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
  transition: border-color 0.35s var(--ease-out), background-color 0.35s var(--ease-out);
}

.upload-form input[type="file"]:hover,
.upload-form input[type="file"]:focus-visible {
  border-color: var(--ui-brand);
}

.upload-form input[type="file"]:disabled {
  border-color: var(--hairline-strong);
  background: var(--background);
  cursor: not-allowed;
}

.upload-form input[type="file"]::file-selector-button {
  margin-right: 0.75rem;
  padding: 0.6rem 1.25rem;
  border: 0;
  border-radius: 999px;
  background: var(--text);
  color: #fff;
  font: inherit;
  font-weight: 600;
  cursor: inherit;
}

.upload-form p {
  margin: 0;
}

.upload-form #upload-quota {
  font-weight: 600;
}

/* My downloads: the allowance is a card; its numbers are rows, the label
   on the left and the figure on the right, the remaining one in green. */
.allowance {
  padding: 1.25rem;
  margin: 1.25rem 0 0;
  border: var(--rim-width) solid var(--rim);
  border-radius: var(--radius-xl);
  background: var(--surface) padding-box;
  box-shadow: 0 0 0 1px var(--hairline), var(--rim-highlight), var(--shadow-soft);
}

.allowance h2 {
  margin: 0 0 1rem;
  font-size: 1.15rem;
}

.allowance > :last-child {
  margin-bottom: 0;
}

.allowance-stats {
  display: grid;
  gap: 0.375rem;
  margin: 0 0 1rem;
}

.allowance-stats > div {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 0.9rem;
  border-radius: var(--radius-md);
  background: var(--background);
}

.allowance-stats > div:last-child {
  background: var(--mint);
}

.allowance-stats dt {
  color: var(--muted);
  font-size: 0.875rem;
  line-height: 1.35;
}

.allowance-stats dd {
  flex: none;
  margin: 0;
  color: var(--text);
  font-family: var(--font-serif);
  font-size: 1.6rem;
  font-weight: 650;
  font-variant-numeric: tabular-nums lining-nums;
  line-height: 1;
}

.allowance-stats > div:last-child dd {
  color: var(--ui-brand-active);
}

.allowance-rules {
  padding-top: 0.75rem;
  border-top: 1px dashed var(--hairline-strong);
}

.allowance-rules summary {
  cursor: pointer;
  font-weight: 600;
}

.allowance-rules ul {
  margin: 0.5rem 0 0;
  padding-left: 1.25rem;
  color: var(--muted);
  font-size: 0.9rem;
}

.allowance-rules li + li {
  margin-top: 0.35rem;
}

.download-actions {
  justify-content: flex-start;
  margin: 0.75rem 0 0;
}

/* Live search: while a new query loads, the previous results stay in place,
   dimmed, and "Searching…" (the line above them) stands in for their count. */
.search-results[aria-busy="true"] {
  opacity: 0.45;
  transition: opacity 0.3s var(--ease-out);
}

.search-results[aria-busy="true"] .result-count {
  display: none;
}

/* Article */
.article-header {
  padding: 0.5rem 0 1rem;
}

/* Breadcrumbs: Home › article, the trail the BreadcrumbList JSON-LD
   describes, as a small pill over the title. The current page is the
   last, unlinked item. */
.breadcrumbs ol {
  display: inline-flex;
  flex-wrap: nowrap;
  gap: 0.25rem;
  max-width: 100%;
  margin: 0 0 0.25rem;
  padding: 0.3rem 0.85rem;
  list-style: none;
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: 0 0 0 1px var(--hairline), var(--shadow-soft);
  font-size: 0.8rem;
  color: var(--muted);
}

.breadcrumbs li {
  flex: none;
}

.breadcrumbs li[aria-current] {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.breadcrumbs li + li::before {
  content: "›";
  margin-right: 0.25rem;
  color: var(--faint);
}

.breadcrumbs a {
  color: var(--link);
  font-weight: 600;
  text-decoration: none;
}

/* Contents: a bezel card (a tinted shell around a core) at the foot of the
   article aside, with a numbered list. Numbers are CSS counters; chapters
   nest under their part as 1.1, 1.2. site.js marks the section being read
   with aria-current. The sizes are fixed: the reading scale leaves the
   contents alone. */
.bezel {
  padding: var(--bezel-pad);
  border-radius: var(--bezel-radius);
  background: var(--shell);
  box-shadow: inset 0 0 0 1px var(--hairline);
}

.bezel-core {
  border-radius: calc(var(--bezel-radius) - var(--bezel-pad));
  background: var(--core);
  box-shadow: var(--core-highlight), 0 1px 2px rgba(43, 33, 26, 0.04), 0 18px 40px -30px rgba(43, 33, 26, 0.25);
}

.toc {
  margin: 0;
}

.toc details {
  padding: 1.25rem 1.25rem 1rem;
}

.toc summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 32px;
  color: var(--faint);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  list-style: none;
  cursor: pointer;
}

.toc summary::-webkit-details-marker {
  display: none;
}

/* A plus drawn with two gradients; open, it turns into a cross. */
.toc summary::after {
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  content: "";
  background:
    linear-gradient(var(--ink), var(--ink)) center / 9px 1px no-repeat,
    linear-gradient(var(--ink), var(--ink)) center / 1px 9px no-repeat,
    var(--shell);
  transition: transform 600ms var(--ease);
}

.toc details[open] summary::after {
  transform: rotate(45deg);
}

.toc summary:focus-visible {
  outline: none;
}

.toc summary:focus-visible::after {
  outline: 3px solid var(--ui-brand);
  outline-offset: 2px;
}

.toc ol {
  counter-reset: toc;
  display: grid;
  gap: 0.15rem;
  margin: 0.75rem 0 0;
  padding: 0;
  list-style: none;
}

.toc li {
  counter-increment: toc;
}

.toc a {
  position: relative;
  display: grid;
  grid-template-columns: 2rem 1fr;
  gap: 0.25rem;
  padding: 0.6rem 0.5rem;
  border-radius: 0.875rem;
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1.4;
  text-decoration: none;
  transition: color 500ms var(--ease), background-color 500ms var(--ease), transform 600ms var(--ease);
}

.toc a::before {
  content: counter(toc, decimal-leading-zero);
  padding-top: 0.15em;
  color: var(--faint);
  font-size: 10.5px;
  letter-spacing: 0.12em;
}

.toc a:hover {
  color: var(--ink);
  background: var(--shell);
  transform: translateX(2px);
}

.toc a[aria-current] {
  color: var(--ink);
  background: var(--toc-active);
}

.toc a[aria-current]::before {
  color: var(--sage-deep);
}

.toc a:focus-visible {
  outline-offset: 0;
}

/* Chapters: indented by the number column and its gap, so they line up
   under their part's title; the part stays dark while one is read. */
.toc ol ol {
  margin: 0.15rem 0 0.25rem;
  padding-left: 2.25rem;
  gap: 0.1rem;
}

.toc ol ol a {
  padding: 0.45rem 0.5rem;
  font-size: 0.85rem;
}

.toc ol ol a::before {
  content: counters(toc, ".");
}

.toc li:has(> ol a[aria-current]) > a {
  color: var(--ink);
}

/* A section behind the paywall: dimmed, with a lock at the right edge;
   site.js scrolls to the call to action instead. */
.toc a.toc-locked {
  grid-template-columns: 2rem 1fr 16px;
  opacity: 0.7;
}

.toc-lock {
  width: 16px;
  height: 16px;
  margin-top: 0.1em;
  fill: none;
  stroke: var(--muted);
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Summary: an abstract card under the title. */
.abstract {
  position: relative;
  max-width: var(--measure);
  margin: 1.25rem 0 0;
  padding: 1.1rem 1.25rem 1.2rem;
  border-radius: var(--radius-lg);
  background: linear-gradient(135deg, var(--mint), rgba(251, 226, 122, 0.22));
  box-shadow: 0 0 0 1px rgba(114, 192, 44, 0.2);
}

.abstract .eyebrow {
  margin-bottom: 0.6rem;
  box-shadow: 0 0 0 1px var(--hairline);
}

.article-header .abstract .lead {
  margin: 0;
  color: var(--text);
  font-family: var(--font-serif);
  line-height: 1.65;
}

/* The published date and word count. */
.article-header > p.muted {
  margin: 0.75rem 0 0;
  font-size: 0.875rem;
}

.article-body {
  max-width: var(--measure);
  padding-bottom: 1rem;
  font-family: var(--font-serif);
  line-height: 1.75;
  font-optical-sizing: auto;
}

/* Body paragraphs carry a 1.25cm first-line indent with a tight gap between
   them. Only the body's own paragraphs (direct children of the body or of a
   section) are indented; the summary, headings, the contents list and the
   references start flush. */
.article {
  --paragraph-indent: 1.25cm;
}

.article-body > p,
.article-free > p,
.paywall > p,
.article-section > p {
  margin: 0 0 0.7em;
  text-indent: var(--paragraph-indent);
  hanging-punctuation: first;
}

/* Article images keep their aspect ratio from width/height and shrink to
   the column. */
.article-cover,
.article-image {
  display: block;
  max-width: 100%;
  height: auto;
  margin: 1.25rem 0;
}

/* A gated article's paid part follows its free part in .paywall; a reader
   without access finds the call to action there instead, a bezel card in
   the interface font. */
.paywall-cta {
  margin: 2rem 0 1rem;
  font-family: var(--font);
  line-height: 1.6;
}

.paywall-cta .bezel-core {
  padding: 1.5rem 1.5rem 1.25rem;
}

.paywall-cta .paywall-title {
  margin: 0 0 0.5rem;
  font-size: 1.25rem;
  line-height: 1.35;
}

.paywall-cta .paywall-title::before {
  display: none;
}

.paywall-cta p {
  margin: 0 0 1.25rem;
  color: var(--muted);
}

/* The cut page's end reads as an article that goes on behind the card:
   the free part fades out through a mask (the page background is a
   gradient, so no overlay colour could match it), blurred skeleton bars
   echo the next paragraphs, and the card floats over their foot. Every
   size is in em of the article body, so the reading scale carries them
   and their height is known at first paint. */
.article-free.is-cut {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 7em), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 7em), transparent);
}

.paywall-skeleton {
  padding-top: 0.35em;
  filter: blur(2px);
  -webkit-user-select: none;
  user-select: none;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to bottom, #000 30%, transparent);
  mask-image: linear-gradient(to bottom, #000 30%, transparent);
}

/* One bar per line of text: 0.7em of bar and 1.05em of leading make the
   body's 1.75 line height. */
.paywall-skeleton span {
  display: block;
  height: 0.7em;
  margin: 0.525em 0;
  border-radius: 999px;
  background: color-mix(in srgb, var(--text) 16%, transparent);
}

.paywall-skeleton span:nth-child(2) { width: 92%; }
.paywall-skeleton span:nth-child(3) { width: 96%; }
.paywall-skeleton span:nth-child(5) { width: 60%; }

/* The fourth bar opens a new paragraph. */
.paywall-skeleton span:nth-child(4) {
  width: calc(85% - var(--paragraph-indent));
  margin-inline-start: var(--paragraph-indent);
}

.paywall-cta {
  position: relative;
  z-index: 1;
  margin-top: -2.75em;
  background: linear-gradient(var(--shell), var(--shell)), color-mix(in srgb, var(--core) 90%, transparent);
  box-shadow: inset 0 0 0 1px var(--hairline), var(--shadow-float);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

.paywall-cta:focus {
  outline: none;
}

.paywall-cta .paywall-remaining {
  margin-top: -0.75rem;
  color: var(--faint);
  font-size: 0.875rem;
}

/* On a phone the actions stack, full width. */
@media (max-width: 639.98px) {
  .paywall-cta .actions {
    flex-direction: column;
    align-items: stretch;
  }
}

@media (min-width: 640px) {
  .paywall-cta {
    margin-top: -3.5em;
  }
}

@media (min-width: 768px) {
  .article-free.is-cut {
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 9.5em), transparent);
    mask-image: linear-gradient(to bottom, #000 calc(100% - 9.5em), transparent);
  }
}

.article-body [id] {
  scroll-margin-top: 1.5rem;
}

.article-body :is(h2, h3, h4, h5, h6)[id] {
  scroll-margin-top: var(--nav-clearance);
}

/* With contents the article is a grid under its header: the aside (toolbar
   and contents) and the main column (body and themes). Narrow, the aside
   stacks above the body; from 1024px it is a sticky four-column rail. */
.article-layout {
  display: grid;
  gap: 3rem;
  padding-bottom: 6rem;
}

.article-aside {
  position: relative;
  z-index: var(--z-aside);
  display: grid;
  gap: 1.25rem;
  align-content: start;
}

.article-main {
  min-width: 0;
}

/* Body headings: a short green stroke over each second-level heading
   marks where a new part of the paper begins. */
.article-body h2 {
  margin-top: 2em;
}

.article-body h2::before {
  content: "";
  display: block;
  width: 2.25rem;
  height: 3px;
  margin-bottom: 0.75em;
  border-radius: 3px;
  background: var(--ui-brand);
}

.article-body h3,
.article-body h4,
.article-body h5,
.article-body h6 {
  margin-top: 1.6em;
}

/* References close the body as a numbered list in the source's order. Long
   URLs wrap like body text (body sets overflow-wrap: anywhere). */
.article-references {
  margin-top: 2.5rem;
}

.article-references ol {
  margin: 0;
  padding-left: 1.75rem;
  color: var(--muted);
  font-family: var(--font);
  font-size: 0.875em;
  line-height: 1.6;
}

.article-references li::marker {
  color: var(--ui-brand-active);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.article-references li + li {
  margin-top: 0.6rem;
}

/* Themes close the article as a list of search links, one per line, each
   behind a small hash mark. The links keep the site's link colour and
   focus ring, and wrap like body text (body sets overflow-wrap: anywhere),
   so a long theme never widens or clips the page. */
.article-themes {
  max-width: var(--measure);
  padding: 0.5rem 0 1.5rem;
}

.article-themes ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.article-themes li {
  position: relative;
  padding-left: 1.25rem;
}

.article-themes li::before {
  content: "#";
  position: absolute;
  left: 0;
  color: var(--faint);
  font-weight: 600;
}

.article-themes li + li {
  margin-top: 0.5rem;
}

/* Related articles follow the article as search-result cards. */
.article-related {
  margin-top: 2rem;
  padding: 2rem 0 0;
  border-top: 1px dashed var(--hairline-strong);
}

.article-related h2 {
  margin: 0 0 1.25rem;
}

/* Reading text size. reading-scale.js sets data-reading-scale on <html>
   (steps of 10%, 90% to 150%). The title, summary and body (references
   included) grow together, and body headings are sized in em, so the heading
   hierarchy keeps its proportions. The toolbar, the contents list and the
   site navigation keep their size. */
:root[data-reading-scale="-1"] { --reading-scale: 0.9; }
:root[data-reading-scale="1"] { --reading-scale: 1.1; }
:root[data-reading-scale="2"] { --reading-scale: 1.2; }
:root[data-reading-scale="3"] { --reading-scale: 1.3; }
:root[data-reading-scale="4"] { --reading-scale: 1.4; }
:root[data-reading-scale="5"] { --reading-scale: 1.5; }

.article-header h1 {
  margin: 0.75rem 0 0.5rem;
  font-size: calc(1.85rem * var(--reading-scale, 1));
}

.article-header .lead {
  font-size: calc(1.05rem * var(--reading-scale, 1));
  text-indent: 0;
}

.article-body {
  font-size: calc(1.0625rem * var(--reading-scale, 1));
}

.article-body h2 {
  font-size: 1.35em;
}

.article-body h3 {
  font-size: 1.15em;
}

.article-body h4,
.article-body h5,
.article-body h6 {
  font-size: 1em;
}

/* Article toolbar: Download, Regenerate and More, 48px icon buttons in
   one island, fixed to the
   bottom of the viewport and centered (wide screens place it again, see
   the media queries). The box spans the gutters but takes no taps, so the
   text beside the island stays usable. Popovers and the status line open
   above the island, centered and never wider than the screen, so nothing
   scrolls sideways even at 320px. */
.article-toolbar {
  --toolbar-height: calc(44px + 0.5rem);
  position: fixed;
  left: var(--gutter);
  right: var(--gutter);
  bottom: max(1rem, env(safe-area-inset-bottom));
  z-index: 20;
  display: flex;
  justify-content: center;
  margin: 0;
  pointer-events: none;
}

.article-toolbar-row,
.article-toolbar .article-popover,
.article-toolbar-status {
  pointer-events: auto;
}

/* The bar never covers the end of the page. */
body:has([data-article-toolbar]) .site-footer {
  padding-bottom: calc(44px + 0.5rem + 2rem + env(safe-area-inset-bottom));
}

.article-toolbar-row {
  position: relative;
  display: flex;
  justify-content: flex-end;
  gap: 0.125rem;
  padding: 0.125rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.86);
  box-shadow: 0 0 0 1px var(--hairline), var(--rim-highlight), var(--shadow-soft);
}

/* AI action: brand-green icon via Flat UI's ghost label hooks; background stays neutral. */
.toolbar-btn--ai {
  --component-label-color: var(--ui-brand);
  --component-label-hover: var(--ui-brand-hover);
  --component-label-active: var(--ui-brand-active);
}

.toolbar-btn--ai:focus-visible {
  color: var(--ui-brand-hover);
}

/* The heart is outlined, and filled while pressed; its label says which. */
[data-favorite-toggle] [data-icon="heart-filled"],
[data-favorite-toggle][aria-pressed="true"] [data-icon="heart"],
[data-favorite-toggle] [data-label="on"],
[data-favorite-toggle][aria-pressed="true"] [data-label="off"] {
  display: none;
}

[data-favorite-toggle][aria-pressed="true"] [data-label="on"] {
  display: inline;
}

[data-favorite-toggle][aria-pressed="true"] [data-icon="heart-filled"] {
  display: block;
}

.article-popover {
  position: absolute;
  bottom: calc(var(--toolbar-height) + 0.5rem);
  left: 0;
  right: 0;
  z-index: 20;
  width: min(20rem, calc(100vw - 2 * var(--gutter)));
  margin-inline: auto;
  padding: 0.9rem;
  border: var(--rim-width) solid var(--rim);
  border-radius: var(--radius-xl);
  background: var(--surface) padding-box;
  box-shadow: 0 0 0 1px var(--hairline), var(--shadow-float);
  transform-origin: bottom center;
}

.article-popover p {
  margin: 0 0 0.75rem;
}

.article-popover label {
  display: block;
  margin-bottom: 0.5rem;
  font-weight: 600;
}

.article-popover input,
.article-dialog textarea {
  width: 100%;
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--background);
  box-shadow: inset 0 0 0 1px var(--hairline-strong);
  color: var(--text);
  font: inherit;
  transition: box-shadow 0.3s var(--ease-out), background-color 0.3s var(--ease-out);
}

.article-popover input {
  height: 44px;
  margin-bottom: 0.5rem;
  padding: 0 0.75rem;
}

.article-popover[data-text-size] {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  width: max-content;
  padding: 0.25rem;
  border-radius: 999px;
}

.article-text-size-value {
  min-width: 3.5rem;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  text-align: center;
}

/* The download menu: one full-width Flat UI button per format, as wide as
   the longest label but never wider than the screen. */
.article-popover[data-download-menu] {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  width: max-content;
  max-width: calc(100vw - 2 * var(--gutter));
  padding: 0.25rem;
  border-radius: var(--radius-lg);
}

/* The More menu: the island's look, right-aligned with it and opening
   above it. One 48px Flat UI row per action, icon then label. Unlike the
   island it is opaque: it covers text, which would show through. */
.article-popover[data-more-menu] {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  bottom: calc(100% + 0.5rem);
  left: auto;
  right: 0;
  width: max-content;
  max-width: calc(100vw - 2 * var(--gutter));
  margin-inline: 0;
  padding: 0.25rem;
  border: 0;
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: 0 0 0 1px var(--hairline), var(--rim-highlight), var(--shadow-soft);
  transform-origin: bottom right;
}

/* Flat UI centers a button's content; the label takes the rest of the row,
   so every icon lines up on the left and the labels start after it. */
.article-more-label {
  flex: 1 1 auto;
  padding-right: 0.5rem;
  text-align: start;
}

.article-popover[hidden] {
  display: none;
}

/* A short confirmation ("Link copied") that floats over the content, so the
   article does not move. Empty, it has no box but stays a live region. */
.article-toolbar-status {
  position: absolute;
  bottom: calc(var(--toolbar-height) + 0.5rem);
  left: 0;
  right: 0;
  z-index: 10;
  width: fit-content;
  max-width: min(20rem, calc(100vw - 2 * var(--gutter)));
  margin: 0 auto;
  padding: 0.6rem 1rem;
  border-radius: var(--radius-md);
  background: var(--text);
  box-shadow: var(--shadow-float);
  color: #fff;
  font-size: 0.875rem;
  line-height: 1.4;
}

.article-toolbar-status:empty {
  padding: 0;
  box-shadow: none;
}

/* Dialogs (native <dialog>, opened with showModal): a large rounded card
   that settles in over a blurred, dimmed page. */
.article-dialog {
  width: min(calc(100% - 2 * var(--gutter)), 32rem);
  max-height: calc(100dvh - 2rem);
  padding: 0;
  border: var(--rim-width) solid rgba(255, 255, 255, 0.5);
  border-radius: var(--radius-xl);
  background: var(--surface) padding-box;
  color: var(--text);
  box-shadow: 0 0 0 1px var(--hairline), var(--shadow-float);
  overflow: auto;
}

.article-dialog::backdrop {
  background: rgba(23, 34, 26, 0.38);
  backdrop-filter: blur(6px);
}

/* The page behind an open dialog does not scroll. */
html:has(.article-dialog:modal) {
  overflow: hidden;
}

.article-dialog-body {
  padding: 1.5rem;
}

.article-dialog h2 {
  margin: 0 0 0.75rem;
  font-size: 1.35rem;
}

.article-dialog-context {
  display: grid;
  gap: 0.25rem;
  margin: 0 0 1rem;
  padding: 0.75rem 1rem;
  border-radius: var(--radius-md);
  background: var(--background);
  color: var(--muted);
  font-size: 0.875rem;
}

.article-dialog-context div {
  display: flex;
  gap: 0.35rem;
  min-width: 0;
}

.article-dialog-context dt {
  flex: none;
  font-weight: 600;
}

.article-dialog-context dt::after {
  content: ":";
}

.article-dialog-context dd {
  min-width: 0;
  margin: 0;
  color: var(--text);
}

.article-dialog-form label {
  display: block;
  font-weight: 600;
}

.article-dialog-form label + p {
  margin: 0.25rem 0 0.5rem;
  font-size: 0.875rem;
}

.article-dialog textarea {
  display: block;
  min-height: 8rem;
  padding: 0.75rem 0.9rem;
  resize: vertical;
}

.article-dialog textarea:focus,
.article-popover input:focus {
  background: var(--surface);
  box-shadow: inset 0 0 0 1.5px var(--ui-brand), 0 0 0 4px rgba(114, 192, 44, 0.16);
  outline: none;
}

.article-dialog textarea[aria-invalid="true"] {
  box-shadow: inset 0 0 0 1.5px var(--ui-danger), 0 0 0 4px rgba(220, 38, 38, 0.12);
}

.article-dialog-count {
  margin: 0.25rem 0 0;
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.article-dialog-error,
.article-dialog-status {
  margin: 0;
}

.article-dialog-error:not(:empty),
.article-dialog-status:not(:empty) {
  margin-top: 0.5rem;
}

.article-dialog-error {
  color: var(--ui-danger-active);
  font-weight: 600;
}

.article-dialog .actions {
  margin-top: 1.25rem;
}

.article-dialog-message {
  margin: 0;
}

.article-dialog-done p {
  margin: 0;
  font-weight: 600;
}

/* Word dialog: a heading with its close button, then the two actions. */
.article-dialog-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 1rem;
}

.article-dialog-head h2 {
  margin: 0;
}

.article-docx-actions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: 0.75rem;
}

.message {
  max-width: 36rem;
  margin: 1.5rem auto 0;
  padding: 2rem 1.5rem;
  border: var(--rim-width) solid var(--rim);
  border-radius: var(--radius-xl);
  background: var(--surface) padding-box;
  box-shadow: 0 0 0 1px var(--hairline), var(--rim-highlight), var(--shadow-soft);
}

.message h1 {
  margin-top: 0;
  font-size: 1.75rem;
}

/* Footer: its rule lines up with the content, like the header island. */
.site-footer {
  padding: 0 var(--gutter);
  color: var(--muted);
  font-size: 0.85rem;
}

.site-footer > .container {
  max-width: calc(var(--page) - 2 * var(--gutter));
  padding-left: 0;
  padding-right: 0;
}

.footer-row {
  padding-top: 1.25rem;
  padding-bottom: 1.5rem;
  border-top: 1px dashed var(--hairline-strong);
}

.footer-row p {
  margin: 0;
}

/* Motion, only for readers who have not asked to reduce it. Page parts
   rise in on load one after another (headings stay put, so the largest
   text paints at once); cards past the first screen rise as they scroll
   into view where the browser ties animations to scrolling; popovers,
   dialogs and the menu settle in; cards lift under a mouse. */
@media (prefers-reduced-motion: no-preference) {
  /* The paywall card settles in once, when site.js sees it arrive. */
  .paywall-cta {
    transition: opacity 250ms ease-out, transform 250ms ease-out;
  }

  .paywall-cta[data-reveal="pending"] {
    opacity: 0;
    transform: translateY(8px);
  }

  .page-hero > :not(h1),
  .hero > :not(h1),
  .allowance,
  .upload-form,
  .notice,
  .message,
  .results > .result {
    animation: rise 0.8s var(--ease-out) backwards;
  }

  .page-hero > :nth-child(3),
  .hero > :nth-child(3),
  .results > .result:nth-child(2) {
    animation-delay: 0.06s;
  }

  .page-hero > :nth-child(4),
  .hero > :nth-child(4),
  .results > .result:nth-child(3) {
    animation-delay: 0.12s;
  }

  .allowance,
  .upload-form,
  .results > .result:nth-child(4) {
    animation-delay: 0.18s;
  }

  .results > .result:nth-child(5) {
    animation-delay: 0.24s;
  }

  .results > .result:nth-child(n+6) {
    animation-delay: 0.3s;
  }

  .allowance .notice {
    animation: none;
  }

  @supports (animation-timeline: view()) {
    .results > .result:nth-child(n+7) {
      animation: rise var(--ease-out) backwards;
      animation-timeline: view();
      animation-range: entry 0% entry 70%;
    }
  }

  .site-menu:not([hidden]) .site-menu-inner {
    animation: menu-open 0.5s var(--ease-spring) backwards;
  }

  .site-menu:not([hidden]) .site-menu-inner > * {
    animation: rise-small 0.6s var(--ease-out) backwards;
  }

  .site-menu:not([hidden]) .site-menu-inner > :nth-child(2) {
    animation-delay: 0.06s;
  }

  .site-menu:not([hidden]) .site-menu-inner > :nth-child(3) {
    animation-delay: 0.12s;
  }

  .article-popover {
    animation: popover-open 0.45s var(--ease-spring) backwards;
  }

  .article-popover[data-more-menu] {
    animation: more-open 0.18s var(--ease-out) backwards;
  }

  .article-dialog[open] {
    animation: dialog-open 0.5s var(--ease-spring) backwards;
  }

  [data-favorite-toggle][aria-pressed="true"] [data-icon="heart-filled"] {
    animation: pop 0.5s var(--ease-spring);
  }

  [data-menu-toggle] [data-line] {
    transition: transform 0.55s var(--ease-spring), opacity 0.3s var(--ease-out);
  }

  .brand-mark,
  .result,
  .menu-links a,
  .search-filters label,
  .upload-form input[type="file"]::file-selector-button {
    transition-property: transform, background-color, box-shadow, color;
    transition-duration: 0.6s, 0.3s, 0.35s, 0.3s;
    transition-timing-function: var(--ease-spring), var(--ease-out), var(--ease-out), var(--ease-out);
  }

  .search-filters label:active {
    transform: scale(0.97);
  }

  @media (hover: hover) {
    .brand:hover .brand-mark {
      transform: rotate(-8deg) scale(1.04);
    }

    .result:hover {
      transform: translateY(-3px);
    }

    .menu-links a:hover {
      transform: translateX(2px);
    }

    .upload-form input[type="file"]:not(:disabled):hover::file-selector-button {
      transform: translateY(-1px);
    }
  }
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(18px);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes rise-small {
  from {
    opacity: 0;
    transform: translateY(10px);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes menu-open {
  from {
    opacity: 0;
    transform: translateY(-8px) scale(0.98);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes popover-open {
  from {
    opacity: 0;
    transform: translateY(-6px) scale(0.96);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes more-open {
  from {
    opacity: 0;
    transform: translateY(6px) scale(0.96);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes dialog-open {
  from {
    opacity: 0;
    transform: translateY(16px) scale(0.97);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes pop {
  0% {
    transform: scale(0.6);
  }

  60% {
    transform: scale(1.18);
  }

  100% {
    transform: scale(1);
  }
}

@media (min-width: 560px) {
  h1 {
    font-size: 2.5rem;
  }

  .article-header h1 {
    font-size: calc(2.4rem * var(--reading-scale, 1));
  }

  .search-filters {
    gap: 0.5rem 0.625rem;
  }

  .pagination-status {
    flex: 0 1 auto;
  }

  .site-menu-inner {
    max-width: 22rem;
    margin-left: auto;
  }

  .points ul {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (min-width: 768px) {
  :root {
    --gutter: 24px;
    --section-space: 5rem;
  }

  main.container {
    padding-top: 2.5rem;
  }

  h1 {
    font-size: 3rem;
  }

  .header-row {
    padding-left: 0.75rem;
  }

  .hero {
    padding-top: 3rem;
  }

  .page-hero {
    padding-top: 1.5rem;
  }

  .search-page,
  .favorites-page {
    max-width: 52rem;
    margin: 0 auto;
  }

  .search-page .page-hero,
  .favorites-page .page-hero {
    padding-bottom: 1rem;
  }

  .result {
    padding: 1.35rem 1.6rem;
  }

  .result-title {
    font-size: 1.25rem;
  }

  .favorites-page .results,
  .article-related .results {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem;
  }

  /* An odd last card spans the row, so the grid never ends on a gap. */
  .favorites-page .results > .result:last-child:nth-child(odd),
  .article-related .results > .result:last-child:nth-child(odd) {
    grid-column: 1 / -1;
  }

  .allowance,
  .upload-form {
    padding: 1.5rem;
  }

  /* One centred reading column; related articles line up with it. */
  .article,
  .article-related {
    max-width: calc(var(--measure) + 4rem);
    margin-left: auto;
    margin-right: auto;
  }

  .article-header {
    padding-bottom: 1.5rem;
  }

  .article-header h1 {
    font-size: calc(3rem * var(--reading-scale, 1));
    max-width: 22em;
  }

  .article-body {
    font-size: calc(1.125rem * var(--reading-scale, 1));
  }

  .abstract {
    padding: 1.4rem 1.6rem 1.5rem;
  }

  .article-dialog-body {
    padding: 2rem;
  }

  .message {
    margin-top: 3rem;
    padding: 2.5rem;
  }
}

/* 1024px: the article with contents becomes a twelve-column grid under a
   full-width header. The aside takes four columns and stays in view; the
   toolbar sits at its top in the flow, its popovers opening below it, and
   the contents card scrolls on its own when the list outgrows the screen. */
@media (min-width: 1024px) {
  .article:has(.article-layout),
  .article:has(.article-layout) ~ .article-related {
    max-width: none;
  }

  .article-layout {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: 2rem;
    padding-bottom: 8rem;
  }

  .article-aside {
    position: sticky;
    top: var(--nav-clearance);
    grid-column: 1 / span 4;
    align-self: start;
    padding-right: 1.5rem;
  }

  .article-main {
    grid-column: 5 / -1;
  }

  .toc details {
    max-height: calc(100vh - var(--nav-clearance) - 7rem);
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  .article-aside .article-toolbar {
    position: relative;
    inset: auto;
    justify-content: flex-start;
    margin: 0;
    pointer-events: auto;
  }

  .article-aside .article-popover,
  .article-aside .article-toolbar-status {
    top: calc(var(--toolbar-height) + 0.5rem);
    right: auto;
    bottom: auto;
    left: 0;
    margin-inline: 0;
    transform-origin: top left;
  }

  /* The aside's island sits on the left, so its menu opens below and
     to the right, inside the aside. */
  .article-aside .article-popover[data-more-menu] {
    top: calc(100% + 0.5rem);
    right: auto;
    bottom: auto;
    left: 0;
    transform-origin: top left;
  }

  body:has(.article-aside [data-article-toolbar]) .site-footer {
    padding-bottom: 0;
  }

  /* Related articles line up with the main column (column 5). */
  .article:has(.article-layout) ~ .article-related {
    padding-left: calc((100% + 2rem) / 3);
  }
}

/* Wide screens: the split pages put the aside on the left. An article
   without contents stays a centred column, its toolbar in the flow on the
   right under the title. */
@media (min-width: 1080px) {
  h1 {
    font-size: 3.5rem;
  }

  .page-split {
    grid-template-columns: minmax(0, 23rem) minmax(0, 1fr);
    column-gap: 4rem;
    align-items: start;
  }

  .page-split .page-main {
    padding-top: 2.5rem;
  }

  .page-split h1 {
    font-size: 2.75rem;
  }

  .article-header h1 {
    font-size: calc(3.5rem * var(--reading-scale, 1));
  }

  /* Without contents the toolbar stays in the flow, on the right under
     the title, its popovers opening below it. */
  .article:not(:has(.article-layout)) .article-toolbar {
    position: relative;
    inset: auto;
    justify-content: flex-end;
    margin: 0.5rem 0 0.25rem;
    pointer-events: auto;
  }

  .article:not(:has(.article-layout)) .article-popover,
  .article:not(:has(.article-layout)) .article-toolbar-status {
    top: calc(var(--toolbar-height) + 0.5rem);
    right: 0;
    bottom: auto;
    left: auto;
    margin-inline: 0;
    transform-origin: top right;
  }

  .article:not(:has(.article-layout)) .article-popover[data-more-menu] {
    top: calc(100% + 0.5rem);
  }

  body:has([data-article-toolbar]) .site-footer {
    padding-bottom: 0;
  }
}

/* Previous / next article from the latest search: two round links site.js
   adds only to an article opened from search results. Narrow screens put
   them in the bottom corners, level with the centred toolbar island and
   under its popovers; once the side gutters fit a 44px button with 32px to
   spare on each side of the reading column, they sit there, vertically
   centred and clear of the text. */
.search-nav-link {
  --button-icon-size: 22px;
  --search-nav-column: calc(var(--measure) + 4rem);
  position: fixed;
  bottom: max(1rem, env(safe-area-inset-bottom));
  z-index: 19;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  color: var(--text);
  background: var(--surface);
  box-shadow: 0 0 0 1px var(--hairline-strong), var(--shadow-lift);
  text-decoration: none;
  transition: background-color 0.15s var(--ease-out), color 0.15s var(--ease-out);
}

.search-nav-link:hover {
  color: var(--link);
  background: var(--mint);
}

.search-nav-link[data-dir="prev"] {
  left: var(--gutter);
}

.search-nav-link[data-dir="next"] {
  right: var(--gutter);
}

/* The position flashed after a move to the previous / next article. It
   never takes clicks or moves the layout; reduced motion skips the fade. */
.search-nav-flash {
  position: fixed;
  left: 50%;
  bottom: calc(24px + env(safe-area-inset-bottom));
  z-index: 30;
  transform: translateX(-50%);
  padding: 0.35rem 0.85rem;
  border-radius: 999px;
  color: #fff;
  background: rgba(0, 0, 0, 0.55);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  font-size: 0.875rem;
  font-variant-numeric: tabular-nums;
  line-height: 1.4;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s var(--ease-out), visibility 0s linear 0.2s;
}

.search-nav-flash[data-visible] {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.2s var(--ease-out), visibility 0s;
}

@media (prefers-reduced-motion: reduce) {
  .search-nav-flash,
  .search-nav-flash[data-visible] {
    transition: none;
  }
}

/* Side mode: the centred column (article and related list) is
   --measure + 4rem wide; each link is centred in its gutter. */
@media (min-width: calc(48rem + 2 * (44px + 32px))) {
  .search-nav-link {
    top: 50%;
    bottom: auto;
    transform: translateY(-50%);
  }

  .search-nav-link[data-dir="prev"] {
    left: calc((100% - var(--search-nav-column)) / 4 - 22px);
  }

  .search-nav-link[data-dir="next"] {
    right: calc((100% - var(--search-nav-column)) / 4 - 22px);
  }
}

/* With contents the article spans the page container (--page less its
   gutters) from 1024px, so the links go back to the bottom corners until
   the container leaves room beside it. The toolbar then sits in the aside,
   so the footer needs its own room under the links. */
@media (min-width: 1024px) {
  body:has(.article-layout) .search-nav-link {
    --search-nav-column: calc(var(--page) - 2 * var(--gutter));
  }
}

@media (min-width: 1024px) and (max-width: calc(1160px + 2 * (44px + 32px) - 0.02px)) {
  body:has(.article-layout) .search-nav-link {
    top: auto;
    bottom: max(1rem, env(safe-area-inset-bottom));
    transform: none;
  }

  body:has(.article-layout) .search-nav-link[data-dir="prev"] {
    left: var(--gutter);
  }

  body:has(.article-layout) .search-nav-link[data-dir="next"] {
    right: var(--gutter);
  }

  body:has(.article-layout):has(.search-nav) .site-footer {
    padding-bottom: calc(44px + 2rem + env(safe-area-inset-bottom));
  }
}

@media print {
  .search-nav {
    display: none;
  }
}

/* Reading mode, on phones only (<html data-reading-mode>, which
   reading-scale.js and site.js set only while the reader has it on and
   the viewport matches): the island collapses into one round More button
   in the bottom-right corner, its menu takes over Download, Regenerate and
   the search neighbours, and the corner buttons make way. The menu shows
   each layout's own items, and the toggle only where the mode exists. The
   media query repeats site.js's, so a desktop never changes layout. */
html:not([data-reading-mode]) [data-more-menu] [data-reading-only],
html[data-reading-mode] [data-more-menu] [data-reading-mode-toggle] {
  display: none;
}

.article-more-group {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
}

[data-more-menu] .article-more-group:empty {
  display: none;
}

@media (min-width: 768.02px), not all and (pointer: coarse) {
  [data-more-menu] [data-reading-only],
  [data-more-menu] [data-reading-mode-toggle] {
    display: none;
  }
}

@media (max-width: 768px) and (pointer: coarse) {
  html[data-reading-mode] .article-toolbar {
    justify-content: flex-end;
  }

  html[data-reading-mode] .article-toolbar-row [data-download-toggle],
  html[data-reading-mode] .article-toolbar-row [data-regenerate] {
    display: none;
  }

  html[data-reading-mode] .article-toolbar-row {
    background: var(--surface);
    box-shadow: 0 0 0 1px var(--hairline-strong), var(--shadow-lift);
  }

  /* Both menus open from the corner: upward and to the left, and the long
     one scrolls inside itself when the screen is short. */
  html[data-reading-mode] .article-popover[data-download-menu] {
    bottom: calc(100% + 0.5rem);
    left: auto;
    right: 0;
    margin-inline: 0;
    transform-origin: bottom right;
  }

  html[data-reading-mode] .article-popover[data-more-menu] {
    max-height: calc(100dvh - var(--toolbar-height) - 1.5rem - max(1rem, env(safe-area-inset-bottom)) - env(safe-area-inset-top));
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  /* A tall menu reaches the header island at the top of the page; it
     stays above it while open (a tap on the header closes it), and so
     does the aside that holds it on pages with contents. */
  html[data-reading-mode] .article-aside:has([data-more-menu]:not([hidden])),
  html[data-reading-mode] .article-toolbar:has([data-more-menu]:not([hidden])) {
    z-index: 26;
  }

  html[data-reading-mode] [data-more-menu] > *,
  html[data-reading-mode] .article-more-group > * {
    flex-shrink: 0;
  }

  html[data-reading-mode] .search-nav {
    display: none;
  }

  /* The button sits over the text column, so the article ends above it. */
  html[data-reading-mode] .article {
    padding-bottom: calc(44px + 0.5rem + 1rem + env(safe-area-inset-bottom));
  }
}

/* Without JavaScript the menu toggle cannot work, so it is hidden and the
   menu stays open, back in the page flow so it covers nothing. */
@media (scripting: none) {
  .header-controls [data-menu-toggle],
  .article-toolbar {
    display: none;
  }

  .site-menu[hidden] {
    display: block;
  }

  .site-menu {
    position: static;
    pointer-events: auto;
  }
}

/* The contents' motion stops for readers who ask for less. */
@media (prefers-reduced-motion: reduce) {
  .toc summary::after,
  .toc a {
    transition-duration: 1ms;
  }
}

/* On paper the aside (toolbar and contents) has nothing to do. */
@media print {
  .article-aside {
    display: none;
  }
}

/* Empty-search source titles; actions stay at the right edge on mobile. */
.source-suggestions { list-style: none; padding: 0; margin: 1rem 0; }
.source-suggestions li { display: flex; flex-direction: column; align-items: flex-end; gap: .75rem; padding: .75rem 0; }
.source-suggestions li > span { align-self: stretch; overflow-wrap: anywhere; }
@media (min-width: 40rem) { .source-suggestions li { flex-direction: row; align-items: center; justify-content: space-between; } .source-suggestions li > span { flex: 1; } .source-suggestions .button { flex-shrink: 0; } }

.search-page .source-discovery { background: #f4f0fc; border: 1px solid #e3daef; border-radius: .75rem; padding: 1rem; margin-top: 1.25rem; }
.search-page .source-discovery h2 { font-size: 1.15rem; margin: 0 0 .4rem; }
.search-page .source-discovery p { margin: 0; }
.search-page .source-suggestions { margin: .4rem 0 0; }
.search-results[aria-busy="true"] [data-public-empty] { display: none; }

.search-page[data-private-pending] [data-public-empty]:not([data-exact-empty]) { display: none; }
