/* dessau.dev — product styles.
   Loaded last and unlayered in the document <head>, after dds.css, so any rule
   here wins over every DDS cascade layer with no !important and no specificity
   fight. See libs/dessau/agent/recipes/derive-a-design-system.md.

   Took the default on all six derive-a-design-system decisions (see
   /DECISIONS.md); the rules below are page-shell layout, not a re-theme —
   the same kind of thing Dessau's own reference site keeps in
   reference/assets/reference.css rather than in the design system itself. */

/* -----------------------------------------------------------------------------
   An icon-prefixed rule, in the "why it exists" list
   -----------------------------------------------------------------------------

   NOT .dds-cluster: that primitive is documented as "horizontal group that
   WRAPS" (layout.css) — right for a row of chips, wrong here. With
   flex-wrap active, a long enough line of text does not just wrap within
   its own box; the icon and the text can wrap onto separate lines from
   EACH OTHER, leaving the checkmark stranded above the paragraph. Reached
   for the wrapping primitive because it was the nearest icon+gap pattern,
   not because this needed to wrap — it needs the opposite: the icon pinned
   beside the paragraph always, however many lines the paragraph takes.
   ----------------------------------------------------------------------------- */
.site-rule {
  display: flex;
  flex-wrap: nowrap;
  align-items: flex-start;
  gap: var(--dds-space-sm);
}

/* .dds-icon's own correction for sitting slightly high (vertical-align:
   -0.125em, components.css) only applies in normal inline flow — a flex
   item ignores vertical-align entirely, so align-items: flex-start above
   lines the icon's box up with the TEXT SPAN's box, not with the visible
   cap-height of the first line inside it. Nudged down by eye to match,
   the same correction vertical-align was already making, done by hand
   because flex has no equivalent shorthand for it. */
.site-rule > .dds-icon {
  margin-block-start: 0.2em;
}

/* -----------------------------------------------------------------------------
   Sticky header with a brand mark that is large on arrival and shrinks once
   the reader scrolls
   -----------------------------------------------------------------------------

   Adapted from libs/dessau/reference/assets/reference.css, which this site
   copies deliberately rather than reinvents — see DECISIONS.md. On a
   single-page site the "large on arrival" moment happens exactly once
   instead of on every navigation, which is the case the technique suits
   best.
   ----------------------------------------------------------------------------- */
.site-header {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--dds-z-raised);

  background-color: color-mix(in srgb, var(--dds-color-surface-default) 88%, transparent);
  /* A blur so content scrolling underneath stays legible without a solid bar. */
  backdrop-filter: blur(8px);
  border-block-end: var(--dds-border-thin) solid var(--dds-color-border-subtle);

  /* One height for the row, so only the logo moves as it resizes — a sticky
     bar that changes height moves the content underneath it, which is the
     opposite of getting out of the way. Both logo sizes below fit inside it. */
  --site-row: 2.25rem;
  /* Arrival size. Was 1.25em; trimmed to 1.1em because at the widths where the
     nav goes inline (48rem+) the wordmark, the three nav links and the two
     action buttons were fighting for the row (#11). Still clearly larger than
     the 0.8em scrolled state, so the shrink-on-scroll moment still reads. */
  --site-brand-logo-size: 1.1em;
}

.site-header[data-site-scrolled] {
  --site-brand-logo-size: 0.8em;
}

/* Two properties on `.site-header` above get in the drawer's way, and both stop
   mattering the instant it opens (the whole page is behind the scrim then):

     - `backdrop-filter` makes the header a *containing block* for fixed-position
       descendants — same as `transform`/`filter` — so the 1.2 drawer's scrim
       and sliding panel (children of the header frame; the component looks the
       scrim up inside it, it cannot live elsewhere) would size to the ~69px
       header box instead of the viewport and cover nothing.
     - `z-index: var(--dds-z-raised)` makes the header a stacking context, which
       traps the scrim and panel below page content that comes later in the
       root stack.

   So for the open state, drop the blur and lift the layer to the panel level;
   both revert the moment the drawer closes, and the container query below makes
   this selector dead weight at ≥48rem anyway. */
.site-header:has(.dds-primary-nav[data-dds-open]) {
  backdrop-filter: none;
  z-index: var(--dds-z-panel);
}

.dds-siteheader {
  min-block-size: var(--site-row);
  /* Match the body's container (`--dds-container-xl`, set inline on <main>'s
     .dds-container in index.html). The stock `.dds-container` is `lg` (960px);
     at that width the brand, tagline, three nav links, two action buttons and
     the toggle packed together with no slack at every desktop size, and the
     brand sat ~120px inboard of the hero heading below it. At `xl` the row has
     240px more to work in, so the nav's `margin-inline-start: auto` has real
     free space to open up, and the brand lines up with the hero (#11). */
  --dds-container-width: var(--dds-container-xl);
}

/* The transition is switched on one frame AFTER the first state is applied
   (see assets/product.js), so a page that loads already scrolled — arriving
   on a #fragment, reloading halfway down — starts at the small size rather
   than animating down to it in front of a reader who scrolled nothing. */
.site-header[data-site-scroll-ready] .site-brand-logo {
  transition: block-size var(--dds-duration-base) var(--dds-ease-standard);
}

/* -----------------------------------------------------------------------------
   Anchor targets clear the sticky header
   -----------------------------------------------------------------------------

   dds/css/base.css sets a generic scroll-padding-block-start (--dds-space-2xl,
   48px) sized for clearing *a* sticky header in the abstract. This one is
   taller — and taller again once the nav wraps onto two rows below the 48rem
   breakpoint — so a click on "Why it exists" landed the heading tucked
   behind the bar instead of below it (WCAG 2.2 2.4.11 Focus Not Obscured).

   --site-header-height is kept live by product.js (ResizeObserver), so it
   tracks the header through every breakpoint and content change. The 8rem
   fallback is the tallest the header gets (two-row nav) — generous rather
   than exact, so a fragment link still lands clear before product.js has run,
   or if it never does. Unlayered, so it wins over dds.base's rule on the same
   selector with no specificity fight. */
html {
  scroll-padding-block-start: var(--site-header-height, 8rem);
}

/* ...but `scroll-padding-block-start` on the scroll container also governs
   where the browser scrolls to *reveal a newly focused element*, and the
   header's own controls sit permanently inside that top zone. So Tab-ing to
   (or clicking) the menu button while the page was scrolled flung it ~380px
   up — the "the page jumped" report, the half Dessau's #156 focus-restore fix
   cannot reach because the browser, not the component, does this scroll.
   A negative `scroll-margin-block-start` on the header's controls cancels the
   padding for them specifically: the reveal condition becomes `target.top >= 0`,
   which a `position: sticky` control pinned at the top already satisfies, so
   nothing scrolls. Anchor navigation and body-focus still use the padding. */
.site-header :where(a, button, input, select, [tabindex]) {
  scroll-margin-block-start: calc(-1 * var(--site-header-height, 8rem));
}

.site-brand {
  /* .dds-siteheader-brand defaults to align-items: center. Overridden to
     baseline so the wordmark and the tagline share a text baseline — the
     logo has no text of its own, but a flex item with no line boxes
     baselines on its bottom margin edge, which is where this logo's
     baseline already sits (give or take the file's own overshoot). */
  align-items: baseline;
  gap: var(--dds-space-xs);
}

@supports (mask-image: url("brand/dessau-logo.svg")) {
  .site-brand-logo {
    display: inline-block;
    flex-shrink: 0;

    block-size: var(--site-brand-logo-size);
    aspect-ratio: 629.98 / 101.34;

    background-color: currentColor;
    mask-image: url("brand/dessau-logo.svg");
    mask-size: contain;
    mask-repeat: no-repeat;
    mask-position: center;
  }

  /* The word is in the logo now. Kept in the DOM and only visually hidden —
     same recipe as .dds-sr-only — so the link's accessible name is still
     "Dessau DS A base for design systems" whether or not mask-image resolves. */
  .site-brand-name {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
}

.site-brand-sub {
  font-family: var(--dds-font-body);
  font-size: var(--dds-font-size-xs);
  font-weight: var(--dds-font-weight-regular);
  color: var(--dds-color-text-muted);
}

/* The sub-line is the first thing with room to give when the row gets tight —
   the same layout fix reference.css makes, for the same reason. reference.css
   drops it below the 48rem tablet breakpoint; this site holds it back further,
   to 60rem, because between 48 and 60rem the nav has just gone inline and is
   sharing the row with the two action buttons (#11) — the tagline is what puts
   that band over the edge. Visually hidden rather than `display: none`: it is
   part of the link's accessible name and that name should not change with the
   viewport. */
@media (max-width: 59.999rem) {
  .site-brand-sub {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
}

/* -----------------------------------------------------------------------------
   Reference / GitHub: in the bar at ≥48rem, inside the drawer panel below it
   -----------------------------------------------------------------------------

   Dessau's `siteheader` keeps `.dds-siteheader-actions` in the top bar at every
   width — right for its own reference site, which carries only a theme toggle
   there. This site carries two text links (Reference, GitHub) as well, and
   below the 48rem collapse threshold they did not fit the bar beside the brand,
   the theme toggle and the menu button — at 320px flex-wrap broke the row into
   a three-row pile (#11).

   Below 48rem the collapsed nav is Dessau 1.2's modal drawer (`data-dds-drawer`
   on the toggle, see index.html), a fixed panel sliding in from the inline-end
   edge — so an action link cannot be in the bar and in the panel at once. The
   markup carries two copies:

     - `.dds-siteheader-actions` — the bar copy, the component's own element.
       Used at ≥48rem, `display: none` below it.
     - `.site-drawer-actions` — inside `<nav class="dds-primary-nav">`, so it
       travels into the drawer panel for free. `display: none` at ≥48rem, shown
       below it.

   Exactly one is displayed at any width, so only one is ever a tab stop. The
   nav (panel and links) is behind the disclosure below 48rem, so with
   JavaScript off both links are only in the footer there — the same
   progressive-enhancement position AGENTS.md records for the in-page anchors.

   The theme toggle is a sibling of `.dds-siteheader-actions`, not a child (see
   index.html), so it holds its place in the bar at every width. Above 48rem it
   needs `order: 4` to trail the actions cluster instead of sorting to its
   source position mid-row; below it, it carries the free-space auto-margin so
   the bar reads `[brand] … [toggle][menu]`.
   ----------------------------------------------------------------------------- */
.site-theme-toggle {
  order: 4;
}

/* The drawer-panel copy: hidden at ≥48rem, where the bar copy is used. */
.site-drawer-actions {
  display: none;
}

@container dds-siteheader (inline-size < 48rem) {
  .site-theme-toggle {
    order: 98;
    /* Carry the free-space gap: brand on the left, [toggle][menu] on the right. */
    margin-inline-start: auto;
  }

  /* The bar copy is redundant here — the drawer panel carries its own. */
  .dds-siteheader-actions {
    display: none;
  }

  /* The two external links render as panel rows via `.dds-primary-nav a`
     (display:flex, full width, 44px tall). Set the group apart from the three
     in-page anchors above with a gap — no divider rule, the external-link icon
     on each already marks the change of kind. */
  .site-drawer-actions {
    display: block;
    margin-block-start: var(--dds-space-md);
  }

  .site-drawer-actions a {
    /* `.dds-primary-nav a` sets no gap; the ↗ icon needs one. */
    gap: var(--dds-space-2xs);
  }
}

/* -----------------------------------------------------------------------------
   The primary nav doubles as this page's table of contents
   -----------------------------------------------------------------------------

   #primary-nav carries data-dds-toc (index.html), so
   components-navigation.js already marks the current section's link with
   aria-current="location" as the reader scrolls — the same geometric
   tracking the .dds-toc sidebar component uses, reused rather than
   reimplemented (#2). Dessau's own components-navigation.css only styles
   [aria-current="page"] on .dds-primary-nav a, because a normal multi-page
   product's header never needs "location" there. These two rules are that
   same styling, copied rule-for-rule so the two states look identical, with
   "location" substituted for "page". Both layouts (rail below 48rem,
   underline at and above it) mirror components-navigation.css exactly,
   including its own note on why the underline replaces rather than joins
   the rail: two markers on one item reads as two different states.
   ----------------------------------------------------------------------------- */
.dds-primary-nav a[aria-current="location"] {
  color: var(--dds-color-text-link);
  background-color: var(--dds-color-surface-selected);
  border-inline-start-color: var(--dds-color-action-primary);
  font-weight: var(--dds-font-weight-semibold);
}

@container dds-siteheader (inline-size >= 48rem) {
  .dds-primary-nav a[aria-current="location"] {
    background-color: transparent;
    box-shadow: inset 0 -2px 0 0 var(--dds-color-action-primary);
    border-inline-start-color: transparent;
    border-radius: 0;
  }
}

/* -----------------------------------------------------------------------------
   The two get-started prompts
   -----------------------------------------------------------------------------

   dds/css/base.css gives every `pre` `overflow-x: auto` — right for a short
   command, wrong for a prompt meant to be read, not scrolled through
   sideways at 82 characters a line. This is prose with preserved line
   breaks, not code with columns that matter, so it wraps instead: pre-wrap
   keeps every explicit newline from the source (the copy button reads
   textContent, so what gets copied is unaffected either way) while long
   lines break inside the card instead of needing a horizontal scrollbar —
   which matters most exactly where it would otherwise bite hardest, at
   320px.
   ----------------------------------------------------------------------------- */
.site-prompt {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
