/* CoinWorldCap Developer documentation site. */

/* ---- palette ----

   The neutrals and the accent are the CoinWorldCap design system's, not this
   site's own. Light takes its values as they are written there. Dark takes the
   two inverse surfaces the system defines and derives the rest the way the
   system's own dark mode does — invert(1), which for a neutral grey is
   255 - v. --border is the one value that derivation does not give, because
   the inverse it produces pairs with a darker surface than ours; it was
   raised by hand until its hairline sits on this page the way the system's
   sits on its own. The mapping, the sources and every measurement behind them
   are in the documentation service's design record.

   Dark is the design's home and light is its counterpart, but both are written
   out in full: a colour defined in only one of them renders wrong in the other,
   and that is invisible until someone with the other setting opens the page.

   The site follows the system preference by default. enhance.js writes an
   explicit choice onto <html data-theme>, which wins over the media query —
   with no JavaScript there is no toggle and the system preference stands. */

:root {
  color-scheme: dark;

  /* Two surfaces, not three: --bg is the page itself and --surface is a solid
     step lighter — never a translucent overlay, so it reads the same regardless
     of what is behind it. --code-bg was a third, a step darker again for code
     blocks; it is an alias for --surface now (see below) and kept only because
     "the code surface" and "a raised strip of chrome" are different decisions
     that may diverge again.
     --border is one low-contrast hairline, used everywhere a line is
     needed; nothing on this page has a second, stronger border weight. */
  --bg:      #0e0e0e;
  --surface: #1d1d1d;   /* sidebars, table headers, chips, the current nav item */
  /* The code surface IS the card surface. It used to be a step darker than
     --surface, which made a panel read as a title bar sitting on top of a
     separate black box; the two are one card now and the only line between
     them is the title bar's border-bottom. */
  --code-bg: var(--surface);
  --border:  #282828;

  /* --text is full-contrast ink. --muted is the one secondary tone used for
     everything from body copy to table headers to nav labels — it must hold
     4.5:1 against --bg on its own, so it is not just a dimmer --text, it is
     the floor every secondary colour on the page is allowed to sink to. */
  --text:  #f1f1f1;
  --muted: #999999;   /* measured 6.78:1 against --bg, 5.92:1 on --surface */

  /* Monochrome, per the design system: --accent carries ink's value, which
     makes it identical to --text in both themes. The system's chromatic
     tokens cannot do this job — its lime measures 1.77:1 on white, failing AA
     as text on paper by a wide margin, and the system only ever places it on
     dark.
     A link is therefore no longer marked by hue; what marks one is at the `a`
     rules below, which also says why colour alone was not enough.
     --accent-ink is the text placed *on* an --accent background (the landing
     page's primary button). Ink on paper and paper on ink, so it needs no
     per-theme special case. */
  --accent:      #f1f1f1;
  --accent-ink:  #0e0e0e;
  /* The heading self-link's chain icon. A hue, unlike --accent: the
     developer asked for it to match the blue link icon on CoinMarketCap's API
     pages. Computed contrast on --bg: 6.0:1 dark, 4.9:1 light — it is a
     non-text graphic, so the bar is 3:1. */
  --anchor-icon: #6188ff;

  --get:         #16c784;   /* method badge — the only method this API has is GET */
  --get-bg:      #0e2a20;

  --tok-key:     #7dd3fc;
  --tok-string:  #16c784;
  --tok-number:  #f0a020;
  --tok-literal: #c792ea;
  /* Punctuation and comments were #6b7280, which reaches 3.49:1 on this code
     surface — under AA. #828994 reaches 4.78:1 and is still visibly below
     --muted (5.92:1), so code keeps its de-emphasis. */
  --tok-punct:   #828994;
  --tok-comment: #828994;
  --tok-command: #f1f1f1;
  --tok-flag:    #7dd3fc;

  /* One height for every control in the top bar — the search box, the Request
     a key button and the theme toggle. They were 34, 29 and 32 pixels tall and
     sat on three different baselines. */
  --control-h: 2.125rem;

  --measure: 70ch;
  --sidebar: 15.5rem;
  /* 26rem, not the 23 it was: a curl line and a long decimal string both
     overflowed the panel and had to be scrolled sideways to read. The content
     column gives up the 3rem — it is capped at --measure anyway, so its prose
     does not change width; only the parameter cards narrow. Guide pages'
     table-of-contents rail stays at 15rem (.layout:has(.rail.toc)). */
  --rail: 26rem;

  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, "Noto Sans", Arial, sans-serif;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    color-scheme: light;

    --bg:      #ffffff;
    --surface: #eeeeee;
    /* Light code, not the dark block it used to be. "Code stays dark in light
       mode" was the earlier decision; it made every panel a hard black
       rectangle on the page, and it is reversed here deliberately. Reversing
       it is what forces the --tok-* overrides below: the dark palette's colours
       were picked for a dark code surface, and on this one every one of them
       fails AA. */
    --code-bg: var(--surface);
    --border:  #dddddd;

    --text:  #0e0e0e;
    --muted: #666666;   /* measured 5.74:1 against --bg, 4.95:1 on --surface */

    --accent:      #0e0e0e;
    --accent-ink:  #ffffff;
    --anchor-icon: #3861fb;

    --get:         #0c8f5f;
    --get-bg:      #e6f7f0;

    /* Syntax colours for a light code surface. Measured against --surface
       (#eeeeee), all at or above AA's 4.5:1: key/flag 5.56, string 5.31,
       number 4.68, literal 6.42, punct/comment 5.18. The dark palette's
       values all fail here — measured 1.03:1 to 3.04:1 — which is why this
       block exists rather than letting them inherit. */
    --tok-key:     #0b60a8;
    --tok-string:  #067043;
    --tok-number:  #9a5b00;
    --tok-literal: #7038a8;
    --tok-punct:   #5f636e;
    --tok-comment: #5f636e;
    --tok-command: #0e0e0e;
    --tok-flag:    #0b60a8;
  }
}

/* The explicit choice, in both directions, so the toggle works either way. */
:root[data-theme="light"] {
  color-scheme: light;

  --bg:      #ffffff;
  --surface: #eeeeee;
  --code-bg: var(--surface);
  --border:  #dddddd;

  --text:  #0e0e0e;
  --muted: #666666;

  --accent:      #0e0e0e;
  --accent-ink:  #ffffff;
  --anchor-icon: #3861fb;

  --get:         #0c8f5f;
  --get-bg:      #e6f7f0;

  /* Syntax colours for a light code surface: the same values as the block
     above, where the measurements behind them are. */
  --tok-key:     #0b60a8;
  --tok-string:  #067043;
  --tok-number:  #9a5b00;
  --tok-literal: #7038a8;
  --tok-punct:   #5f636e;
  --tok-comment: #5f636e;
  --tok-command: #0e0e0e;
  --tok-flag:    #0b60a8;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.7 var(--sans);
  -webkit-font-smoothing: antialiased;
}

/* Prose links take an underline; chrome links do not — in the top bar, the
   sidebars, the rails and the endpoint index a link is marked by where it sits
   and what it sits on, and a rule on every row there is noise, not
   information.

   Why the underline, given --accent is ink: not because colour stopped
   separating a link from prose — prose is --muted, so ink-on-muted still
   measures 3.36:1 in light and 2.52:1 in dark. It is that the cue is now
   lightness alone, and dark is under the 3:1 WCAG 1.4.1 asks of a link
   marked by colour only. The underline states it outright. */
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

.content p a,
.content li a,
.content td a,
.landing p a,
.landing li a,
.landing td a { text-decoration: underline; text-underline-offset: 0.15em; }

/* Two shapes inside prose that are chrome, not prose, and keep the chrome
   rule: the endpoint index is a list of cards marked up as list items, and
   the landing page's buttons sit in a <p class="cta">. Both are already a
   target on their own — a rule through a button's label reads as a mistake.

   Both selectors have to outrank the rule above rather than be written short:
   `.cta a` loses to `.landing p a` on specificity and the underline stays,
   which is what the first screenshot of this change showed. */
.content .endpoint-index a,
.landing p.cta a { text-decoration: none; }

/* ---- inline code: a bordered chip, which is the most recognisable thing
        about this class of reference site ---- */

code {
  font-family: var(--mono);
  font-size: 0.86em;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 0.12em 0.4em;
  color: var(--text);
  white-space: nowrap;
}

/* ---- code panels ---- */

.code {
  position: relative;
  margin: 1.25rem 0;
  background: var(--code-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
}

.code-lang {
  position: absolute;
  top: 0.55rem;
  left: 0.9rem;
  font: 600 0.68rem/1 var(--sans);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.code pre {
  margin: 0;
  padding: 2.1rem 1rem 1rem;
  overflow-x: auto;
  background: none;
  border: 0;
}

.code code {
  background: none;
  border: 0;
  padding: 0;
  white-space: pre;
  font-size: 13px;
  line-height: 1.8;
  color: var(--text);   /* was a hardcoded #e6e9ef, which is invisible on a light code surface */
}

/* Nothing here hides this button, and that is the point: enhance.js creates it,
   so its presence in the DOM already proves the script ran and the handler is
   attached. The previous ".js .copy { display: block }" gate existed because
   the template shipped the button whether or not anything could act on it. */
.copy {
  position: absolute;
  top: 0.45rem;
  right: 0.5rem;
  padding: 0.2rem 0.55rem;
  font: 600 0.7rem/1.5 var(--sans);
  color: var(--muted);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
}
.copy:hover { color: var(--text); border-color: var(--muted); }   /* background is already --bg on .copy */

/* Revealed on hover — but only where there is a pointer to hover with.
   Gating on (hover: hover) rather than writing the rule unconditionally is
   what keeps the button reachable on a touch screen, where :hover either never
   fires or sticks after a tap: there it simply stays visible. */
@media (hover: hover) {
  .copy { opacity: 0; transition: opacity 0.12s ease-in-out; }
  .code:hover .copy,
  .copy:focus-visible { opacity: 1; }
}

.tok-k { color: var(--tok-key); }
.tok-s { color: var(--tok-string); }
.tok-n { color: var(--tok-number); }
.tok-l { color: var(--tok-literal); }
.tok-p { color: var(--tok-punct); }
.tok-c { color: var(--tok-comment); font-style: italic; }
.tok-f { color: var(--tok-command); font-weight: 600; }
.tok-g { color: var(--tok-flag); }

/* ---- top bar ---- */

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  height: 3.5rem;
  padding: 0 1.25rem;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--text);
  font-weight: 700;
  letter-spacing: -0.01em;
}
.brand:hover { text-decoration: none; }

/* The mark ships as two files, one with white fills and one with black, and
   the fills are baked into the paths — currentColor cannot reach them, so the
   theme picks a file rather than recolouring one. Both are in the DOM and CSS
   hides the wrong one; <picture media> cannot be used here because it follows
   the media query only and would ignore the explicit choice enhance.js writes
   onto <html data-theme>.

   The file names say which ink the file carries, not which theme it belongs
   to: logo-square-light.svg is the white one and goes on the dark page. The
   class names say the background instead, so the pairing is readable here. */
.brand-logo { display: block; height: 1.5rem; width: auto; }
.brand-logo-on-paper { display: none; }

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .brand-logo-on-ink { display: none; }
  :root:not([data-theme="dark"]) .brand-logo-on-paper { display: block; }
}
:root[data-theme="light"] .brand-logo-on-ink { display: none; }
:root[data-theme="light"] .brand-logo-on-paper { display: block; }

/* The top bar shows the mark and the suffix only; the name is not printed.
   It stays in the accessible name, which is the link's only label besides the
   suffix: the images are decorative because the theme hides one of them —
   alt text on a display:none image reaches nobody. */
.brand-name {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.brand-suffix { font-weight: 700; color: var(--text); white-space: nowrap; }

.topbar-nav { display: flex; gap: 1.25rem; margin-right: auto; }
.topbar-nav a {
  position: relative;
  padding: 1.1rem 0;
  color: var(--muted);
  font-size: 0.9rem;
  font-weight: 500;
}
.topbar-nav a:hover { color: var(--text); text-decoration: none; }
.topbar-nav a.is-current { color: var(--accent); }
.topbar-nav a.is-current::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 2px;
  background: var(--accent);
}

/* The one filled button on the page. --accent/--accent-ink is the same pair the
   landing page's primary button uses — a pair rather than one colour plus
   white, because the two themes swap which of them is the fill. Measured as
   shipped: 17.09:1 on dark, 19.30:1 on light.

   "Request a key", not "Get API Key": keys here are issued per integrator by a
   person, and a button promising self-service would be describing a signup flow
   that does not exist.

   ".topbar-cta", not ".cta". The landing page's hero has carried
   <p class="cta"> since it was written, so the short name painted that whole
   paragraph amber the moment this rule shipped — a regression invisible from
   the reference pages, which is all that was screenshotted at the time. A class
   this file adds for one element in the top bar has to say so in its name. */
.topbar-cta {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  height: var(--control-h);
  padding: 0 0.9rem;
  border: 1px solid transparent;   /* matches the bordered controls' box, so all three line up */
  border-radius: 8px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 0.8125rem;
  font-weight: 650;
  white-space: nowrap;
}
.topbar-cta:hover {
  color: var(--accent-ink);
  text-decoration: none;
  background: color-mix(in srgb, var(--accent) 86%, var(--bg));
}

/* Filled with --surface rather than left transparent: an outline-only box
   beside a filled search field read as a different class of control. Same fill
   and border as the search box — one row of controls, one material. The theme
   control below takes the same fill and border, and the search box's height. */
.nav-toggle {
  display: none;
  padding: 0.3rem 0.65rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--muted);
  font: 500 0.8rem/1.6 var(--sans);
  cursor: pointer;
}

/* Light / dark / system, as a segmented control: the CoinWorldCap site's own
   theme control, placed in the bar rather than behind a settings menu because
   theme is the only setting this site has. The checked segment takes the
   --accent pill — ink on paper, paper on ink — so it needs no per-theme rule.
   enhance.js builds the three buttons; the empty container stays hidden until
   it has. */
.theme-toggle {
  display: none;
  flex: none;
  align-items: center;
  gap: 2px;
  height: var(--control-h);
  padding: 2px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.enhanced .theme-toggle { display: inline-flex; }
.theme-option {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 100%;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 6px;
  color: var(--muted);
  cursor: pointer;
}
.theme-option:hover { color: var(--text); }
.theme-option[aria-checked="true"] { background: var(--accent); color: var(--accent-ink); }
.theme-option:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---- search ---- */

.search-open {
  display: none;
  align-items: center;
  gap: 0.6rem;
  min-width: 13rem;
  height: var(--control-h);
  padding: 0 0.7rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--muted);
  font: 400 0.88rem/1.4 var(--sans);
  cursor: pointer;
  text-align: left;
}
.enhanced .search-open { display: flex; }
.search-open:hover { border-color: var(--muted); color: var(--text); }
.search-open span { margin-right: auto; }

kbd {
  padding: 0.08rem 0.32rem;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  font: 500 0.72rem/1.5 var(--mono);
  color: var(--muted);
}

.search-overlay {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 50;
  padding: 8vh 1rem 1rem;
  background: rgba(0, 0, 0, .55);
}
.search-overlay.is-open { display: block; }

.search-panel {
  max-width: 40rem;
  margin: 0 auto;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 24px 60px -20px rgba(0, 0, 0, .7);
}

.search-input {
  width: 100%;
  padding: 1rem 1.15rem;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--border);
  color: var(--text);
  font: 400 1.05rem/1.5 var(--sans);
  outline: none;
}

.search-results { list-style: none; margin: 0; padding: 0.4rem; max-height: 58vh; overflow-y: auto; }

.search-hit {
  display: block;
  padding: 0.6rem 0.75rem;
  border-radius: 8px;
  color: var(--text);
}
/* --surface, not --code-bg. They hold the same value today, but this row is
   chrome and --code-bg names the code surface; the sidebar's current-item rule
   below picks --surface for the same reason. (The original reason — that
   --code-bg was pinned dark in the light theme — stopped being true when code
   panels adopted --surface.) */
.search-hit:hover, .search-hit.is-selected { background: var(--surface); text-decoration: none; }
.search-crumb { display: block; font: 600 0.85rem/1.5 var(--sans); color: var(--accent); }
.search-excerpt { display: block; font-size: 0.82rem; line-height: 1.55; color: var(--muted); }

.search-empty { padding: 1.1rem 0.9rem; color: var(--muted); font-size: 0.9rem; }

.search-hint {
  display: flex;
  gap: 0.35rem;
  align-items: center;
  margin: 0;
  padding: 0.55rem 0.9rem;
  border-top: 1px solid var(--border);
  color: var(--muted);
  font-size: 0.78rem;
}

/* ---- three columns ---- */

.layout {
  display: grid;
  grid-template-columns: var(--sidebar) minmax(0, 1fr) var(--rail);
  gap: 2.5rem;
  max-width: 84rem;
  margin: 0 auto;
  padding: 0 1.25rem;
}

/* Guide pages' rail is a table of contents (".rail.toc") and stays narrow;
   endpoint pages' rail pins two code panels and gets the wider default above.
   :has() rather than a body/html page-kind class: the generator (page.go)
   never writes one — KindEndpoint's rail is bare ".rail", KindGuide's and
   KindIndex's is ".rail.toc" — so that distinction is the hook. Browsers
   without :has() just render every rail at the endpoint width, which is a
   valid (if not ideal) three-column page rather than a broken one. */
.layout:has(.rail.toc) { --rail: 15rem; }

/* A page with no rail at all drops the third column instead of reserving an
   empty 15rem beside itself. The contract landing page is the case: its
   partition holds no h2 of its own, so its table of contents would have been an
   "On this page" heading over an empty list — the template omits the rail
   rather than print that, and the grid has to follow or the endpoint list sits
   in a column two thirds the page's width for no reason. */
.layout:not(:has(.rail)) { grid-template-columns: var(--sidebar) minmax(0, 1fr); }

.sidebar, .rail {
  position: sticky;
  top: 3.5rem;
  align-self: start;
  max-height: calc(100vh - 4rem);
  overflow-y: auto;
  padding: 1.75rem 0;
  font-size: 0.875rem;
}

.nav-group, .toc-title, .panel-title {
  margin: 0 0 0.6rem;
  font: 600 0.7rem/1 var(--sans);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
}

.nav-docs { list-style: none; margin: 0; padding: 0; }
.nav-docs a {
  display: block;
  padding: 0.42rem 0.7rem;
  margin-bottom: 0.15rem;
  border: 1px solid transparent;
  border-left-width: 2px;
  border-radius: 7px;
  color: var(--muted);
}
.nav-docs a:hover { color: var(--text); background: var(--surface); text-decoration: none; }
/* The current document gets a filled background plus a 2px accent bar on the
   left — text colour alone is not enough; it is the one thing in the sidebar
   that has to be findable at a glance.
   --surface, not --code-bg. The two are the same value now, but they are not
   the same decision: --code-bg names the code surface and is free to diverge
   again, while this pill is chrome. Naming the one that means "a raised strip
   of chrome" is what keeps a future change to code panels from silently
   restyling the sidebar. */
.nav-docs a[aria-current="page"] {
  color: var(--text);
  background: var(--surface);
  border-left-color: var(--accent);
  font-weight: 600;
}

/* Without this, a group's last link and the next group's label sit only
   0.15rem apart (the link's own margin-bottom) — "Errors" and the following
   "ENDPOINTS · V1" label read as one continuous block instead of two groups. */
.nav-docs + .nav-group { margin-top: 1.75rem; }

.toc ul { list-style: none; margin: 0; padding: 0; border-left: 1px solid var(--border); }
.toc a {
  display: block;
  padding: 0.28rem 0 0.28rem 0.85rem;
  margin-left: -1px;
  border-left: 2px solid transparent;
  color: var(--muted);
}
.toc a:hover { color: var(--text); text-decoration: none; }
.toc .lvl3 a { padding-left: 1.6rem; font-size: 0.95em; }
.toc a[aria-current="true"] { color: var(--accent); border-left-color: var(--accent); }

/* ---- endpoint page rail: pinned Request/Response panels ---- */

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 1px 2px rgb(0 0 0 / 0.06);
  overflow: hidden;   /* clips the title bar and code block to the card's corners */
}
.panel + .panel { margin-top: 1.5rem; }

/* The title bar carries the method badge and the path, so the shared
   .nav-group/.toc-title/.panel-title rule's uppercasing has to be undone here:
   it would render /openapi/v1/quotes as /OPENAPI/V1/QUOTES, which is a
   different path on a case-sensitive router. */
.panel .panel-title {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin: 0;
  padding: 0.55rem 0.9rem;
  background: var(--surface);   /* same value as the card and the code below it; the border-bottom is the only separation */
  border-bottom: 1px solid var(--border);
  text-transform: none;
  letter-spacing: normal;
}

/* The path is the one item in the bar that can outgrow it. It scrolls on its
   own rather than wrapping the bar onto a second line or widening the rail —
   and its scrollbar is hidden, because a visible one inside a 1.9rem title bar
   costs more than the overflow it reports. */
.panel-path {
  flex: 1 1 auto;
  min-width: 0;
  overflow-x: auto;
  white-space: nowrap;
  font: 600 0.78rem/1.4 var(--mono);
  color: var(--text);
  scrollbar-width: none;
}
.panel-path::-webkit-scrollbar { display: none; }

/* Pushes the language label to the right edge in both panels: the request
   panel's .panel-path already grows, the response panel has this instead. */
.panel-label { flex: 1 1 auto; }

.panel-lang {
  flex: 0 0 auto;
  font: 600 0.68rem/1 var(--sans);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The panel's own code block sits flush inside the card instead of nesting a
   second bordered/rounded box inside the first. */
.panel .code {
  margin: 0;
  background: var(--code-bg);
  border: 0;
  border-radius: 0;
}

/* Panels scroll in both directions; they do not wrap.

   An earlier revision wrapped instead (white-space: pre-wrap plus
   overflow-wrap: anywhere) to stop the curl comment clipping at "sorted by id
   as…". It did stop the clipping, and it cost more than it saved:
   overflow-wrap: anywhere breaks inside a word, so a URL, a JSON string or a
   shell flag could split across lines at no meaningful boundary. Code that a
   reader is meant to copy should not be re-flowed — what they see has to be
   what the file says. Scrolling reports the overflow honestly instead of
   rewriting the line to hide it.

   No padding-top reserved for .code-lang: the language label lives in the
   title bar now, not absolutely positioned over the code. */
.panel .code pre {
  /* Top padding clears the copy button, exactly as .code pre does in the body.
     It was 1.25rem all round, and the button — absolutely positioned at
     top: 0.45rem and about 1.45rem tall — sat on top of the first line: on
     /trending the comment "# The board. by defaults to gainers..." ran under
     it and was unreadable. Nothing caught it, because the button is created by
     enhance.js and revealed only on hover, so neither the markup guards nor a
     plain screenshot ever show it. */
  padding: 2.1rem 1.25rem 1.25rem;
  /* No max-height. The cap was 22rem and it cut the JSON off mid-object with
     no visible affordance on macOS, where the scrollbar is hidden until you
     scroll — the panel looked like a complete response that simply ended. The
     rail is sticky and scrolls with the page, so a long body costs length, not
     a hidden scroll region. */
  overflow-x: auto;
}

/* Only the response gets a floor. A request is two or three lines of curl and
   a minimum turns the rest into dead space; a response is the thing worth
   showing at length, and the floor stops the shortest one — /global's, at nine
   lines — from reading as a stub beside the others. */
.panel-response .code pre { min-height: 20rem; }

/* ---- content ---- */

/* minmax(0, 1fr) on the grid plus min-width:0 here is what lets this column
   shrink; without it one wide table stretches the grid and the whole page
   scrolls sideways. */
.content { padding: 2rem 0 6rem; min-width: 0; }

.content h1 {
  margin: 0 0 1.25rem;
  font-size: 2rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}
.content h2 {
  margin: 3rem 0 1rem;
  padding-top: 1.75rem;
  border-top: 1px solid var(--border);
  font-size: 1.35rem;
  font-weight: 650;
  letter-spacing: -0.01em;
}
.content h3 { margin: 2rem 0 0.7rem; font-size: 1.05rem; font-weight: 650; }
.content h4 { margin: 1.5rem 0 0.5rem; font-size: 0.95rem; font-weight: 650; color: var(--muted); }

/* The self-link every h2 and h3 carries. Its space is reserved, not revealed:
   the link is always laid out and only its colour changes, so a heading does
   not reflow under the pointer. Transparent colour rather than display:none or
   visibility:hidden — those take it out of the accessibility tree and out of
   tab order, and :focus-visible below is what brings it back for anyone
   tabbing rather than pointing. */
.heading-anchor {
  display: inline-flex;
  margin-left: 0.4rem;
  color: transparent;
  vertical-align: middle;
  text-decoration: none;
  transition: color 0.12s ease-in-out;
}
.heading-anchor svg { width: 0.8em; height: 0.8em; }
.content h2:hover .heading-anchor,
.content h3:hover .heading-anchor,
.heading-anchor:focus-visible,
.heading-anchor:hover {
  color: var(--anchor-icon);
}

.content p, .content li { max-width: var(--measure); color: var(--muted); font-size: 1.0625rem; line-height: 1.75; }
.content strong { color: var(--text); font-weight: 650; }

.content blockquote {
  margin: 1.4rem 0;
  padding: 0.85rem 1.1rem;
  max-width: var(--measure);
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: 0 8px 8px 0;
}
.content blockquote p { margin: 0.4rem 0; }

/* ---- parameter cards ----

   These replace the four-column table the contract writes. The notes column was
   the problem: it holds a paragraph — /quotes' "size" is four lines about which
   paging mode the limit applies in — and in a table it got roughly a third of
   an already narrow content column, wrapping to eight lines beside three cells
   that were nearly empty. A card gives the note the full width and puts the
   name, type and default on one line above it. */

.params { list-style: none; margin: 1.35rem 0; padding: 0; }

.param {
  max-width: none;   /* overrides .content li's measure: the card is the column */
  padding: 0.8rem 0.95rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 0.9375rem;
}
.param + .param { margin-top: 0.6rem; }

/* Wraps rather than scrolls: /quotes' sort accepts
   "id | name | symbol | slug, optionally :asc / :desc", which is a type long
   enough to need a second line on a narrow viewport. */
.param-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.25rem 0.6rem; }

.param-name code { font-size: 0.875rem; font-weight: 650; color: var(--text); }
.param-type { font-size: 0.8125rem; color: var(--muted); }

.param-default-label {
  margin-left: 0.2rem;
  font: 600 0.65rem/1.6 var(--sans);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.param-default { font-family: var(--mono); font-size: 0.8125rem; color: var(--text); }

.param-notes { display: block; margin-top: 0.45rem; color: var(--muted); line-height: 1.7; }

/* The contract's parameter tables have no Required column — every query
   parameter is optional — so this appears once per endpoint page, on the API
   key header, and is not competing with anything for attention. */
.param-required {
  font: 600 0.7rem/1.6 var(--sans);
  letter-spacing: 0.03em;
  color: var(--accent);
}

/* The Headers panel reuses the .panel card but holds prose, not code, so it
   needs the padding .panel .code pre carries and the card does not. */
.panel-headers .panel-body { padding: 0.75rem 0.9rem; }
.panel-headers .param-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.2rem 0.5rem;
  margin: 0;
}
.panel-headers .param-name code { font-size: 0.8125rem; font-weight: 650; color: var(--text); }
.panel-headers .param-notes { margin: 0.4rem 0 0; font-size: 0.8125rem; }

/* ---- the contract landing page's endpoint list ---- */

/* The generated "Endpoints" heading. It is an h2 inside .content and takes
   every .content h2 rule; this exists so the class in the template is not a
   hook nothing uses, and to hold the one thing that differs — it opens a
   generated list, so it does not want the 3rem top margin a contract h2 gets
   for separating prose sections. */
.endpoint-index-title { margin-top: 2.25rem; }

/* Rows separated by rules rather than boxed as cards: the summary is a whole
   sentence from the contract, so a row's height is set by prose and cards of
   wildly different heights read as a broken grid. */
.endpoint-index { list-style: none; margin: 1.25rem 0 0; padding: 0; }

.endpoint-index li {
  max-width: none;   /* overrides .content li's measure — the rule spans the column */
  padding: 0.9rem 0;
  border-top: 1px solid var(--border);
}
.endpoint-index li:last-child { border-bottom: 1px solid var(--border); }

.endpoint-index a { display: flex; align-items: center; gap: 0.7rem; }
/* The hover used to recolour the path to --accent. --accent is ink now and
   .endpoint-index-path is already --text, so that rule changed nothing at
   all — the row had no hover feedback left. The path takes the underline
   instead, the same mark a prose link carries; the row itself stays clean. */
.endpoint-index a:hover { text-decoration: none; }
.endpoint-index a:hover .endpoint-index-path {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.endpoint-index-path {
  font-family: var(--mono);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text);
}

.endpoint-index-summary {
  margin: 0.4rem 0 0;
  max-width: var(--measure);
  font-size: 0.9375rem;
}

/* ---- endpoint headings ---- */

.content h2.endpoint, .content h3.endpoint {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
.endpoint-path { font-family: var(--mono); font-size: 0.95rem; font-weight: 600; color: var(--text); }

.method {
  flex: none;
  padding: 0.18rem 0.5rem;
  border-radius: 5px;
  font: 700 0.7rem/1.4 var(--sans);
  letter-spacing: 0.04em;
}
/* GET is the only method this API exposes; the other five .method-* rules
   (and the --post/--put/--delete token pairs that backed them) were deleted
   with them rather than kept unused — see the style.css task notes. */
.method-get { color: var(--get); background: var(--get-bg); }

/* ---- endpoint head: method + full URL + auth requirement ---- */

.endpoint-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.85rem;
  padding: 0.85rem 1rem;
  margin: 0 0 1.75rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 9px;
}
.endpoint-head .url {
  font-family: var(--mono);
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--muted);
  /* anywhere, not break-all. break-all breaks greedily: at 1200px it split
     the host mid-token — "https://open-api.coinworldcap.com/openap" then
     "i/v1/trending" — although a break existed at the hyphen in "open-api"
     that would have kept the path whole. anywhere takes that break first and
     only splits a token when nothing else fits. Measured at 1100 / 1200 /
     1320 / 1440: identical except at 1200, where it yields
     "https://open-" + "api.coinworldcap.com/openapi/v1/trending". */
  overflow-wrap: anywhere;
}
/* The path segment is what distinguishes this endpoint from the other paths
   on the same base URL, so it takes the accent. Since the accent became ink
   that is emphasis by weight against the --muted host around it, not by hue:
   the <b> is 600 and the rest of the URL is not. */
.endpoint-head .url b { color: var(--accent); font-weight: 600; }
/* .keychip is gone with the markup it styled: the head block's
   "X-Api-Key (header)" chip was replaced by the rail's Headers panel, which
   states the same requirement and adds what the chip could not — that it is
   required, and the Authentication guide's own sentence saying why. */

/* ---- page head: eyebrow + copy button ---- */

.page-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.35rem;
}

/* Which part of the site you are standing in. The h1 below comes from the
   contract and names the document; this names the section. */
.eyebrow {
  margin: 0;
  color: var(--accent);
  font: 600 0.82rem/1.5 var(--sans);
  text-transform: capitalize;
}

/* An endpoint page's page-head shows this instead of .eyebrow: "Endpoints ·
   v1 / Quotes" is a trail, not a section label, so it stays muted rather than
   taking the accent colour .eyebrow uses for emphasis. */
.crumb {
  margin: 0;
  color: var(--muted);
  font: 500 0.82rem/1.5 var(--sans);
}

/* A split button: .copy-page-main copies the page's markdown, the chevron
   opens .copy-page-menu. No display gate: enhance.js creates the whole
   control, so its presence in the DOM already proves the script ran — same
   reason as .copy above. */
.copy-page {
  position: relative;
  display: inline-flex;
  flex: none;
}
.copy-page-main,
.copy-page-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  height: 2rem;
  padding: 0 0.7rem;
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text);
  font: 500 0.82rem/1 var(--sans);
  cursor: pointer;
}
.copy-page-main { border-radius: 8px 0 0 8px; }
.copy-page-toggle {
  padding: 0 0.45rem;
  border-left: 0;
  border-radius: 0 8px 8px 0;
  color: var(--muted);
}
.copy-page svg { flex: none; }
.copy-page-main:hover,
.copy-page-toggle:hover,
.copy-page-toggle[aria-expanded="true"] { background: var(--surface); color: var(--text); }

.copy-page-menu {
  position: absolute;
  top: calc(100% + 0.375rem);
  right: 0;
  /* Under the sticky top bar (20), so scrolling with the menu open slides it
     beneath the bar instead of painting over it. */
  z-index: 10;
  min-width: 14rem;
  padding: 0.3rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 8px 24px -8px rgb(0 0 0 / 0.25), 0 1px 2px rgb(0 0 0 / 0.08);
}
.copy-page-menu[hidden] { display: none; }
.copy-page-item {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  padding: 0.5rem 0.6rem;
  background: none;
  border: 0;
  border-radius: 6px;
  color: var(--text);
  font: 500 0.85rem/1.3 var(--sans);
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
}
.copy-page-item svg { color: var(--muted); }
.copy-page-item:hover { background: var(--surface); text-decoration: none; }
/* A ring, not just the hover fill: --surface on --bg is about 1.15:1, too
   faint to be the only sign of where keyboard focus is. */
.copy-page-item:focus-visible {
  background: var(--surface);
  text-decoration: none;
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.copy-page-status {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---- cards ----
   For the things that were paragraphs: one fact per card, scannable, and they
   stack on their own when the row runs out of width. */

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 0.85rem;
  margin: 1.25rem 0;
  padding: 0;
  list-style: none;
}

.card {
  padding: 1rem 1.1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.card h3 {
  margin: 0 0 0.35rem;
  font: 650 0.95rem/1.4 var(--sans);
  color: var(--text);
}
.card p {
  margin: 0;
  font-size: 0.92rem !important;
  line-height: 1.6 !important;
  color: var(--muted);
  max-width: none;
}
.card code { font-size: 0.85em; }

/* ---- tables ----
   Each scrolls in its own box rather than widening the page. The contracts have
   eleven of them and some are five columns wide. */

.content table {
  display: block;
  width: 100%;
  overflow-x: auto;
  border-collapse: collapse;
  margin: 1.4rem 0;
  font-size: 0.88rem;
}
.content th {
  padding: 0.6rem 0.9rem;
  text-align: left;
  vertical-align: bottom;
  white-space: nowrap;
  border-bottom: 1px solid var(--border);
  font: 600 0.72rem/1.5 var(--sans);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
}
.content td {
  padding: 0.7rem 0.9rem;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--border);
  color: var(--muted);
}
.content tbody tr:last-child td { border-bottom: 0; }
/* First column reads as the row's key — monospace, full-weight ink. Never
   --accent: that colour is reserved for links and the current-item state,
   and spending it on every parameter name in every table would make it
   meaningless. */
.content table td:first-child {
  font-family: var(--mono);
  font-weight: 650;
  color: var(--text);
}
/* The last column is deliberately NOT capped any more. It was, at 34%, to stop
   the Query parameters table's "sort" row shattering into one code chip per
   line — and that table is not a table now, it is a list of parameter cards
   (render/table.go). Every table left on the site ends in the column carrying
   the prose (Notes, or Cause on the error tables), so the cap had stopped
   protecting anything and was only taking width from the one column that
   needed it. */

/* ---- landing ---- */

.landing { max-width: 62rem; margin: 0 auto; padding: 3.5rem 1.25rem 6rem; }
.landing section { margin-bottom: 3.5rem; }
.landing h2 {
  margin: 0 0 1.1rem;
  font-size: 1.3rem;
  font-weight: 650;
  letter-spacing: -0.01em;
}
.landing p, .landing li { color: var(--muted); max-width: var(--measure); font-size: 1.0625rem; line-height: 1.75; }

.hero { padding-bottom: 1rem; }
.hero h1 {
  margin: 0 0 0.9rem;
  font-size: 2.6rem;
  line-height: 1.15;
  font-weight: 700;
  letter-spacing: -0.03em;
  color: var(--text);
}
.hero p { font-size: 1.05rem; }
.cta { display: flex; flex-wrap: wrap; gap: 0.7rem; margin-top: 1.8rem; }

.button {
  display: inline-block;
  padding: 0.6rem 1.15rem;
  border-radius: 8px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 0.9rem;
  font-weight: 600;
}
/* Not filter: brightness(). Multiplying does nothing to ink — brightness(1.1)
   takes #0e0e0e to #0f0f0f, a 1.007:1 change nobody can see. Mixing toward the
   page moves both themes the only direction each can go: measured 1.46:1
   against the resting fill in light and 1.35:1 in dark, with the label still
   at 13.2:1 / 12.6:1. */
.button:hover {
  text-decoration: none;
  background: color-mix(in srgb, var(--accent) 86%, var(--bg));
}
.button-quiet {
  background: none;
  color: var(--text);
  border: 1px solid var(--border);
}
.button-quiet:hover { background: var(--surface); filter: none; }

.steps { padding-left: 1.3rem; }
.steps > li { margin-bottom: 1.8rem; }
.steps h3 { margin: 0 0 0.4rem; font-size: 1rem; font-weight: 650; color: var(--text); }

.endpoints {
  display: block;
  width: 100%;
  overflow-x: auto;
  border-collapse: collapse;
  font-size: 0.88rem;
}
.endpoints th {
  padding: 0.6rem 0.9rem;
  text-align: left;
  border-bottom: 1px solid var(--border);
  font: 600 0.72rem/1.5 var(--sans);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
}
.endpoints td {
  padding: 0.75rem 0.9rem;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--border);
  color: var(--muted);
}
.endpoints td:first-child {
  font-family: var(--mono);
  font-weight: 650;
  color: var(--text);
}

.badge {
  display: inline-block;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  font: 600 0.72rem/1.5 var(--sans);
}
.badge-live { color: var(--get); background: var(--get-bg); }

.note { color: var(--muted); font-size: 0.88rem; max-width: var(--measure); }

.footer {
  border-top: 1px solid var(--border);
  padding: 1.75rem 1.25rem;
  color: var(--muted);
  font-size: 0.88rem;
}
.footer p { max-width: var(--measure); margin: 0 auto; }

/* ---- narrower screens ----

   The right rail goes first: the content column is what wide parameter
   tables (and, on an endpoint page, the body's own copy of the request curl)
   live in, and giving it the width back matters more than keeping the table
   of contents or the pinned panels visible — a guide page's anchors are still
   reachable through the left sidebar and the page itself, and an endpoint
   page's examples are still in the body (the panel is a pinned *copy* of the
   first request, never the only place it lives).
   ".rail" covers both shapes the generator emits — the guide/index rail is
   ".rail.toc", the endpoint rail is bare ".rail" — a selector of just ".toc"
   would drop only the former and leave an endpoint page's panels stranded in
   a layout that no longer has a third grid column for them. */

@media (max-width: 1180px) {
  .layout { grid-template-columns: var(--sidebar) minmax(0, 1fr); }
  .rail { display: none; }
}

@media (max-width: 52rem) {
  .layout { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .topbar { gap: 1rem; }
  .topbar-nav { display: none; }
  /* The CTA goes too, and it is the one that actually buys the room. Measured
     at a 390px viewport (CDP Emulation.setDeviceMetricsOverride — headless
     Chrome's own --window-size floors at ~485 and hides this entirely): the
     bar needs 463px for brand 109 + search 24 + CTA 120 + theme 28 +
     Contents 78, plus 64 of gap and 40 of padding. That is 73 over, and the
     document's scrollWidth came out 444 against a 390 viewport — a real
     horizontal scroll, with .nav-toggle the element hanging off the end.
     Dropping the CTA frees 136 (its width plus one gap), which cleared 360 as
     well. Those figures are from 2026-09-21. Since then the brand shrank to
     the mark alone (20 on a phone) and the theme control grew to three
     segments (94); the documentation service's 2026-09-24 design record has
     the re-measurement. The landing page's hero keeps its own CTA, so the ask
     is still on the first page a reader lands on; what is lost is the
     shortcut from deep inside the reference on a phone. */
  .topbar-cta { display: none; }
  /* The brand does not shrink: the suffix is nowrap text and the mark has a
     fixed aspect. The suffix goes and the mark steps down a size, so a phone
     shows the mark alone. */
  .brand-suffix { display: none; }
  .brand-logo { height: 1.25rem; }
  /* Inline code may break here, and only here. The nowrap above is right for
     the common case — it keeps id:asc and percent_change.24h from splitting
     at a colon or a dot, which reads as two different values. But the
     contract also carries long unbreakable tokens in prose, and one of them,
     `design/20260917-trending-board-semantics.md` on /trending, is 43
     characters: at a 390px viewport it reached x=412 and took the document's
     scrollWidth to 423, so the whole page scrolled sideways to show one
     filename. A break inside a token is the lesser of the two on a phone. */
  code { white-space: normal; overflow-wrap: anywhere; }
  .search-open { min-width: 0; }
  .search-open span { display: none; }
  .sidebar {
    position: static;
    max-height: none;
    padding: 1rem 0;
    border-bottom: 1px solid var(--border);
  }
  /* Gated on "enhanced", the class enhance.js sets for itself — not on "js",
     which the inline <head> script sets and which only means scripting is on.
     These three move together: they hide the sidebar and reveal the one
     control that brings it back. Gated on "js", a missing enhance.js (404,
     CSP, dropped connection) left a phone with no navigation at all and a
     toggle that did nothing. Without any script the sidebar stays open and
     stacked above the content, which is the point of rendering it
     server-side. */
  .enhanced .nav-toggle { display: block; }
  .enhanced .sidebar { display: none; }
  .enhanced .sidebar.open { display: block; }
  .hero h1 { font-size: 2rem; }
}
