/* ==========================================================================
   Control-F — Documentation chrome
   Styles the design-system site ITSELF. None of this ships to control-f.de.
   ========================================================================== */

/* Docs chrome links are navigation, not prose — no underlines. */
.docs-sidebar a,
.docs-card,
.docs-swatch { text-decoration: none; }

body.docs {
  display: grid;
  grid-template-columns: 17rem minmax(0, 1fr);
  min-height: 100vh;
  background: var(--grey-050);
  color: var(--text-primary);
}

/* ---------- sidebar ---------- */
/* The documentation is the first thing in the system to navigate itself, so
   it is also the first place the page transition can be seen working: the
   sidebar is named, which lifts it out of the root snapshot and leaves it
   standing perfectly still while the chapter behind it is raked in. Old and
   new differ in one thing — which entry carries the current-page plate — so
   the default cross-fade of the two snapshots is exactly the right animation
   for it, and it is left alone. → foundations/transitions.html */
.docs-sidebar {
  position: sticky;
  view-transition-name: docs-sidebar;
  top: 0;
  align-self: start;
  height: 100vh;
  overflow-y: auto;
  padding: var(--space-6);
  background: var(--surface-inverse);
  color: var(--grey-000);
}
.docs-sidebar__brand {
  display: block;
  padding-bottom: var(--space-6);
  margin-bottom: var(--space-6);
  border-bottom: 1px solid rgba(255, 255, 255, 0.14);
}
.docs-sidebar__brand img { width: 8.5rem; }
.docs-sidebar__brand span {
  display: block;
  margin-top: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  /* 0.45 composites to rgb(115,115,115) on the black sidebar — 4.43:1 at
     11 px, under AA. 0.46 clears it by 0.06; 0.50 clears it with room to spare
     and is still visibly quieter than the nav links below it. */
  color: rgba(255, 255, 255, 0.50);
}
/* LIME IS LIGHT, NOT INK — no lime letterforms on a dark surface, here or
   anywhere. Both of these used to be lime, which made this sidebar the
   loudest counter-example to a rule the product nav already keeps
   ("the current page is marked by weight of ink, not by a lime ground",
   components.css). The column is now three steps of one white:

     group title   white 100 %, 11 px mono uppercase   21.0:1
     link          white 66 %                           8.8:1
     current page  white 100 % on a white 14 % plate   15.5:1

   Titles and the current link share an alpha and still cannot be confused:
   one is a tracked 11 px mono label, the other a 15 px sans link carrying a
   plate. Hierarchy comes from ink and shape, which is what the rest of the
   system does. See foundations/colors.html#lime-is-light. */
.docs-nav__group + .docs-nav__group { margin-top: var(--space-6); }
.docs-nav__title {
  margin-bottom: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--grey-000);
}
.docs-nav__list { list-style: none; margin: 0; padding: 0; }
.docs-nav__list a {
  display: block;
  padding: 0.3rem 0;
  font-size: var(--text-md);
  color: rgba(255, 255, 255, 0.66);
  transition: color 120ms;
}
.docs-nav__list a:hover { color: var(--grey-000); }
/* The same marker .cf-nav__link[aria-current] carries on the site: a plate
   inside the plate, at the bar's own corner. The negative inline margin is
   what keeps the label on the same x as every other link — the plate grows
   outward from the text rather than pushing it. */
.docs-nav__list a[aria-current="page"] {
  color: var(--grey-000);
  font-weight: var(--weight-medium);
  background: rgba(255, 255, 255, 0.14);
  border-radius: var(--radius-sm);
  margin-inline: calc(var(--space-3) * -1);
  padding-inline: var(--space-3);
}

/* ---------- content ---------- */
/* min-width:0 or this grid item takes its min-content width from the widest
   demo on the page — the team strip, the blog grid — and pushes the whole
   documentation shell into a horizontal scroll instead of letting the demo
   scroll inside itself. */
.docs-main { min-width: 0; padding: var(--space-12) var(--space-12) var(--space-24); max-width: 76rem; }
.docs-header { margin-bottom: var(--space-12); }
.docs-header__eyebrow {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}
.docs-header h1 {
  margin-top: var(--space-2);
  font-family: var(--font-display);
  font-size: var(--text-display-2);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-display);
}
.docs-header p {
  margin-top: var(--space-4);
  max-width: 68ch;
  font-size: var(--text-lg);
  color: var(--text-secondary);
}

.docs-section { margin-bottom: var(--space-16); scroll-margin-top: var(--space-8); }
.docs-section > h2 {
  padding-bottom: var(--space-3);
  margin-bottom: var(--space-6);
  border-bottom: 1px solid var(--border-strong);
  font-size: var(--text-h3);
  font-weight: var(--weight-medium);
}
.docs-section > h3 {
  margin: var(--space-8) 0 var(--space-4);
  font-size: var(--text-h4);
  font-weight: var(--weight-semibold);
}
/* A fourth level, for a subheading inside a variant that is not itself a
   variant — "Where it comes from" under Solid on the buttons page. Body size,
   so it groups the paragraphs under it without competing with the h3 that
   opened the variant; the only thing it needs from the browser default is
   somewhere to breathe above it. Previously the one bare h4 on the site was
   unstyled, because the only h4 rule in this file is scoped to .docs-rule. */
.docs-section > h4 {
  margin: var(--space-6) 0 var(--space-2);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
}
/* These four style this page's own writing — the prose a documentation page is
   made of. Everything inside .docs-demo is the component instead, and has to be
   left exactly as it ships, so the demo frame is excluded at the source rather
   than overridden further down.

   Overriding does not work here, and the difference is the whole reason for the
   :not(). A blanket reset inside .docs-demo has to pick one value, and there is
   no single right one: a component that declares nothing (.cf-footer__links)
   wants the browser default, while one that declares its own (.cf-prose li + li,
   12 px) wants its value kept. Zeroing serves the first and silently flattens
   the second. Not applying the rule at all serves both.

   Measured leaks this closes, demo vs the same component on a pattern page:
     .cf-footer__links   17.6 px indent + 72 ch cap it does not have on the site
     .cf-prose li + li   8 px instead of its own 12 px
     .cf-statement__body 72 ch instead of its own 46 ch
     .cf-value-row__body grey + capped instead of black + uncapped

   And one that is worse than the other four, because nothing was left unset for
   the docs to fill — the page had already decided and was overruled:
     .icon-bullets on foundations/iconography.html declares padding: 0 and
     gap: var(--space-3) at (0,1,0). .docs-section ul is (0,1,1) and won, so the
     list rendered with a 17.6 px indent it had explicitly zeroed and 20 px
     between items where the page asked for 12.

   The :not() sits on the element the declaration lands on, which for the two
   adjacent-sibling rules is the second one. Siblings share a parent, so either
   position behaves the same here; this is the one that stays correct if the
   selectors are ever changed. */
.docs-section p:not(:where(.docs-demo *)) { max-width: 72ch; color: var(--text-secondary); }
.docs-section p + p:not(:where(.docs-demo *)) { margin-top: var(--space-3); }
.docs-section ul:not(:where(.docs-demo *)) { max-width: 72ch; color: var(--text-secondary); padding-left: 1.1rem; }
.docs-section li + li:not(:where(.docs-demo *)) { margin-top: var(--space-2); }

/* A note the implementer must not skip. Contour + surface, no accent bar —
   the brand draws boxes with a 1 px line, not with a coloured stripe. */
.docs-note {
  margin: var(--space-6) 0;
  padding: var(--space-6);
  border: 1px solid var(--border-strong);
  background: var(--grey-000);
  font-size: var(--text-md);
  color: var(--text-secondary);
}
.docs-note strong { color: var(--text-primary); font-weight: var(--weight-semibold); }
.docs-note--warn { background: var(--grey-100); }

/* ---------- live demo frame ---------- */
/* The frame is a query container, and it is a NAMED one. Those are two
   separate fixes that landed in two steps, and they are worth keeping apart —
   a blame that lands here should not credit this line with both.

   BEING A CONTAINER. Documentation pages are built on .docs-main and never on
   .container, so nothing in the docs was a container at all, and a component
   that sizes itself against one could only ever show its fallback. Measured
   when that landed:

     components/process-card.html   stacked at every width  ->  two columns
     components/blog-grid.html      at 375 the grid folded but the timeline
                                    axis stayed — the axis is a SIBLING of
                                    .subdivide, so unlike the columns it had no
                                    container to ask. Now both fold together.

   HAVING THE NAME. base.css sizes .col-* with
   `@container layout (min-width: 56rem)` — the one named query in the system —
   and a named query matches only an ancestor whose container-name includes
   `layout`. An unnamed container in between is skipped, not substituted, and a
   documentation page has no .container to fall back to, so the rule could never
   fire here at any width.

   Nothing exercises that today: .col-2 … .col-12 have zero consumers in the
   repository, so the name moves no rendered pixel as things stand. The case
   below is CONSTRUCTED — a .col-6 pair injected into a framed demo — and what
   it buys is that the primitive becomes demonstrable at all, which is the
   precondition for ever documenting it with a live example.

   A container query measures the CONTENT box, so the figure that decides the
   match is the frame minus 66 px of padding and border. Both are given here,
   because the border-box number is the one a devtools ruler reports, and
   comparing THAT to 56rem (896 px) predicts a split at 1280 which does not
   happen:

     viewport   border-box   content-box   vs 56rem   .col-6
       1280        912 px        846 px      under    span 12  (unchanged)
       1440       1072 px       1006 px      over     span 12 -> span 6
       1920       1120 px       1054 px      over     span 12 -> span 6

   Both lists are snapshots, not closed sets: this reaches ANY container-queried
   component that ends up inside a demo frame, including through
   .docs-demo--plain, which resets padding, border and background but not the
   container — so a --plain demo queries the full 912 at 1280 and a framed one
   queries 846.

   ONE NAME, TWO IMPLEMENTERS. `layout` is now held by .container on the site
   and by .docs-demo here. Named lookup takes the NEAREST matching ancestor, so
   a future `@container layout (…)` written with the page container in mind gets
   intercepted by the demo frame on any documentation page — and at a different
   viewport, because the frame is always the narrower of the two. For .col-* that
   interception is precisely what is wanted. It is a constraint on what is added
   next, not a defect in this.

   DO NOT TIGHTEN THIS — and note what actually breaks glass, because it is not
   containment. Measured: `contain: paint`, `content` and `strict` all leave
   backdrop-filter working. Filter Effects 2 lists the real backdrop-root
   triggers — filter, opacity < 1, mask, clip-path, backdrop-filter,
   mix-blend-mode, and `will-change` naming any of those — so the way to
   flatten every frosted demo on a page is to fade one in with opacity, not to
   tighten this line.

   TWO THINGS THAT LOOK LIKE TRIGGERS AND ARE NOT, because both are one
   declaration away from the shipping bar and a later run will wonder.
   `isolation: isolate` is not one: it makes a stacking context, and a stacking
   context is not a backdrop root — there is a web-platform test asserting
   exactly that (`backdrop-filter-isolation-isolate.html`, both boxes behind an
   isolated parent must still be filtered). .cf-nav carries it and blurs
   correctly; leave it. Neither are z-index, transform, or sticky positioning.
   What DID break the bar's glass, and is on the list above, was
   `view-transition-name` behaving as one — see the note in base.css.

   Tightening is still wrong, for a different reason: paint containment clips
   descendants to the padding box (measured: a 200 px child in a 100 px box
   paints 100), and the navigation demo's glass sheet deliberately reaches
   --glass-lookahead past its own edge so content arrives already blurred.

   WHAT IT CANNOT FIX. A demo frame is narrower than the page it documents — at
   1280 a framed demo is 846 px inside its padding against 1139 px of page
   content. (1139, not 1152: the gutter is 5.5vw a side, so the page's content
   box is 0.89 of the viewport rather than the viewport less a fixed inset.) So
   .cf-article, which splits at 60rem, is stacked on components/article.html at
   1280 while patterns/blog-artikel.html shows two columns; it matches from
   1440 up.

   Nor does the frame widen monotonically, which is the part that catches
   people. The sidebar arrives at 901 px and takes 319 px out of the docs column
   in a single pixel of viewport — 786 px of frame at 900, 467 px at 901, not
   back to 786 until about 1220. So "make the window wider" is not always sound
   advice, and a container-queried demo can fold on the WIDE side of that edge:
   .subdivide does exactly that on components/blog-grid.html, five columns at
   900, stacked from 901 to 1073, five again at 1074. That is docs chrome rather
   than the component, and not this declaration's to fix — but any note about
   demo width has to survive it. Say the constraint on the page it affects; do
   not widen the frame to hide it. */
.docs-demo {
  container: layout / inline-size;
  padding: var(--space-8);
  border: 1px solid var(--border-default);
  background-color: var(--grey-050);
  background-image: var(--grain), var(--surface-page-wash);
  background-repeat: repeat, no-repeat;
  background-size: 140px 140px, cover;
}
.docs-demo--flat  { background: var(--grey-050); }
.docs-demo--dark  { background: var(--surface-inverse); }
.docs-demo--plain { padding: 0; border: 0; background: none; }
/* Show a component at 3x so a detail is legible at reading distance — the
   2 px corner, the travel of a gradient across a plate. `zoom` and not
   `transform: scale()` because zoom re-lays-out: the corner is drawn at 6 px
   rather than a 2 px corner resampled, which is the whole point of the demo.
   The cost is that the box takes its enlarged size in the flow, so on a phone
   a 256 px button becomes 768 px and takes the page sideways with it — that is
   what it did here, +96 px of horizontal scroll on the whole document at
   375 px. The scroller is therefore part of the utility rather than something
   each demo remembers to add. */
.docs-demo--zoom { overflow-x: auto; }
.docs-demo--zoom > * { zoom: 3; width: max-content; }
/* A field of real artwork to demonstrate a material against. Frosted glass
   over a flat fill has nothing to calm and documents a grey box rather than a
   material, so anything blurred has to be shown over something complex. Held
   here rather than inline on each page: it is one system-level decision, it
   was already written two different ways across two pages, and the URL
   resolves against this stylesheet rather than against whichever page is
   using it — an inline page-relative path breaks the moment a demo is copied
   to a page at another depth. */
.docs-poster {
  background-image: url(../img/hero-poster.jpg);
  background-size: cover;
  background-position: center;
}
.docs-demo + .docs-code { margin-top: 0; border-top: 0; }

.docs-code {
  margin: var(--space-4) 0 0;
  padding: var(--space-4) var(--space-6);
  overflow-x: auto;
  background: var(--grey-800);
  color: #E6E6E6;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: 1.6;
  tab-size: 2;
}
/* Syntax colour is the foil taken apart: the three cool stops of the
   spectrum used as three inks, in the order the ramp runs them. It replaces
   a lime `attr` — lime letterforms on a dark plate, the thing the system
   forbids — and two invented colours (#9FE8FF, #FFD9A0) that belonged to no
   palette at all. Measured on grey-800: 11.0:1, 12.8:1, 9.3:1. The comment
   grey stays neutral, because a comment is meant to recede. */
.docs-code .tag  { color: var(--sky-300); }
.docs-code .attr { color: var(--glas-500); }
.docs-code .val  { color: var(--violett-300); }
.docs-code .com  { color: #8A8A8A; }

/* ---------- colour swatches ---------- */
.docs-swatches {
  --tile: 11rem;
  --flow: var(--space-3);
}
.docs-swatch {
  display: block;
  width: 100%;
  text-align: left;
  border: 1px solid var(--border-subtle);
  background: var(--grey-000);
  cursor: pointer;
}
.docs-swatch__chip { display: block; height: 5rem; border-bottom: 1px solid var(--border-subtle); }
.docs-swatch__meta { display: block; padding: var(--space-3); }
.docs-swatch__name { display: block; font-size: var(--text-md); font-weight: var(--weight-medium); }
.docs-swatch__hex,
.docs-swatch__var {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-secondary);
  letter-spacing: var(--tracking-label);
}
.docs-swatch__var { margin-top: var(--space-1); word-break: break-all; }
.docs-swatch.is-copied .docs-swatch__hex::after { content: "  ✓ kopiert"; color: var(--text-primary); }

/* A tint/shade ramp: one row per colour. */
.docs-ramp { display: grid; grid-template-columns: 7rem 1fr; gap: var(--space-4); align-items: center; }
.docs-ramp + .docs-ramp { margin-top: var(--space-3); }
.docs-ramp__name { font-size: var(--text-md); font-weight: var(--weight-medium); }
/* minmax(0, 1fr), not a bare 1fr. A bare fr floors at the item's min-content
   width, so the mono step labels and their padding set a minimum no narrower
   viewport can honour, and the page scrolls sideways instead.

   base.css states this rule twice in prose and scripts/check-grid-tracks.py
   enforces it — over the three stylesheets that SHIP. docs.css is out of that
   scope on purpose and stays out: the documentation chrome is not the product,
   and a check that governed it would be claiming otherwise. But the rule it
   breaks is about the page scrolling sideways, and a documentation page is
   still a page somebody reads at 320 px. This declaration was correct at seven
   steps and wrong in principle; the one that was wrong in fact is inline on
   the Grey ramp in foundations/colors.html, which overrides this rule with ten
   columns and had to be floored there too. */
.docs-ramp__steps { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.docs-ramp__step {
  padding: var(--space-4) var(--space-2);
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: var(--tracking-label);
  text-align: center;
}

/* ---------- spec tables ---------- */
/* display:block so a wide table scrolls inside itself instead of pushing the
   whole page sideways on a phone. */
.docs-table {
  display: block;
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  border-collapse: collapse;
  font-size: var(--text-md);
}
.docs-table th { white-space: nowrap; }
.docs-table th,
.docs-table td {
  padding: var(--space-3) var(--space-4);
  text-align: left;
  border-bottom: 1px solid var(--border-subtle);
  vertical-align: top;
}
.docs-table th {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  /* --text-muted (#919191) on --grey-050 is 2.97:1 at 11 px — below AA, and 11 px
     is not large text. --text-secondary is 8.61:1. This is the same substitution
     the README already documents for every mono label on the page wash; the
     ramp has no step between #919191 and #484848 to land on instead. */
  color: var(--text-secondary);
  border-bottom-color: var(--border-strong);
}
.docs-table code {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--text-primary);
}
.docs-table td:first-child { white-space: nowrap; }

/* ---------- do / don't ----------
   Both halves get the same 1 px contour. They are told apart by their label and
   by the material underneath — white for do, sunken grey for don't — not by a
   coloured bar. Red stays reserved for form errors. */
.docs-rules { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-6); }
.docs-rule {
  padding: var(--space-6);
  border: 1px solid var(--border-strong);
  background: var(--grey-000);
}
.docs-rule--dont { background: var(--grey-100); }
.docs-rule h4 {
  padding-bottom: var(--space-3);
  margin-bottom: var(--space-4);
  border-bottom: 1px solid var(--border-strong);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}
.docs-rule--dont h4 { color: var(--text-secondary); }
.docs-rule ul { margin: 0; padding-left: 1.1rem; font-size: var(--text-md); }

/* ---------- card links on the index ---------- */
.docs-cards { --tile: 15rem; }
.docs-card {
  display: block;
  padding: var(--space-6);
  background: var(--grey-000);
  border: 1px solid var(--border-subtle);
  transition: border-color 160ms, transform 160ms;
}
.docs-card:hover { border-color: var(--border-strong); transform: translateY(-2px); }
.docs-card > span { display: block; }
.docs-card__num {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-label);
  color: var(--text-secondary);
}
.docs-card__title { margin-top: var(--space-2); font-size: var(--text-h4); font-weight: var(--weight-medium); }
.docs-card__desc  { margin-top: var(--space-2); font-size: var(--text-md); color: var(--text-secondary); }

/* ---------- source-material gallery ---------- */
.docs-plates { --tile: 19rem; }
.docs-plate { display: block; text-decoration: none; }
.docs-plate img {
  width: 100%;
  border: 1px solid var(--border-strong);
  background: var(--grey-000);
  transition: outline-color 160ms;
  outline: 0 solid transparent;
}
.docs-plate:hover img { outline: 2px solid var(--border-strong); outline-offset: -1px; }
.docs-plate figcaption {
  margin-top: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-secondary);
}
.docs-plate figcaption b {
  display: block;
  font-weight: var(--weight-medium);
  color: var(--text-primary);
}

/* Wrap a wide table in this. A table has a min-content width it will not go
   below, so on a phone it pushes the whole page sideways unless something
   around it is allowed to scroll. */
.docs-table-scroll { overflow-x: auto; }

/* Tall full-page mockups: cap the height, scroll inside the frame. */
.docs-scroller {
  max-height: 34rem;
  overflow-y: auto;
  border: 1px solid var(--border-strong);
  background: var(--grey-000);
}
.docs-scroller img { width: 100%; display: block; }

/* ---------- full-page pattern previews ---------- */
.docs-frame {
  width: 100%;
  height: 80vh;
  border: 1px solid var(--border-strong);
  background: var(--grey-000);
}

/* ---------- consent banner, shown in place ---------- */
/* The component is `position: fixed` by definition. This unpins it so the
   documentation can show it as a static specimen. Documentation only — it is
   not a variant, and a page must never use it. */
.cf-consent--static { position: static; }

@media (max-width: 900px) {
  /* minmax(0, 1fr), not 1fr: a bare 1fr track floors at min-content, so one
     long line in a <pre> pushes the whole documentation page sideways on a
     phone instead of scrolling inside its own box. */
  body.docs { grid-template-columns: minmax(0, 1fr); }
  /* And it gives up its view-transition-name with its column. Beside the
     content the sidebar is furniture and holding still is the whole point;
     stacked above it, it is the first screenful — a named element that fills
     the viewport would hold the entire page still for the length of the wipe
     and the reader would see nothing answer their tap. Furniture is a
     position, not a component. */
  .docs-sidebar { position: static; height: auto; view-transition-name: none; }
  .docs-main { padding: var(--space-8) var(--space-6) var(--space-16); }
  .docs-rules { grid-template-columns: 1fr; }
  .docs-ramp { grid-template-columns: 1fr; }
  /* A table cannot shrink below its min-content width, so on a phone it has to
     scroll inside itself rather than take the page with it. */
  .docs-table { display: block; overflow-x: auto; }
}
