/*
 * The main site's structural stylesheet — milestone 5.1.
 *
 * Structure only. Every colour, every family and every type size is a custom
 * property from public/assets/generated/palette.css and layout.css, which the
 * Admin writes and this file is always loaded before (app/Render/SiteAssets
 * links it first, so the owner's Custom CSS can beat it — docs/08's fixed
 * link order). There is no literal hex here, per docs/10-conventions.md's CSS
 * rule, and no value that expresses a decision the Layout screen owns.
 *
 * ONE FLUID BUILD. There is one DOM, from ~360px to ~1600px+
 * (docs/02-mainsite-spec.md §2). The Admin's Desktop and Mobile tabs are two
 * configurations of it, and everything that differs between them lives inside
 * the compiler's own split at 860px:
 *
 *   - visibility  -> .ha-section--hide-wide / .ha-section--hide-narrow
 *   - order       -> .ha-section--n1 … --n12 (--n13 is the overflow bucket),
 *                    scoped to the narrow query
 *
 * Section content arrived with milestone 5.2 and the header and footer with
 * 5.3.
 */

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;

  /*
   * docs/02 §7: `html { scroll-behavior: smooth }` — "respect
   * prefers-reduced-motion in production", which the query at the foot of this
   * file does. The padding is docs/04's own figure for the same thing: "anchor
   * scrolling is smooth with an 80px offset for the sticky header", expressed
   * once here rather than per anchor.
   */
  scroll-behavior: smooth;
  scroll-padding-top: 80px;
}

.ha-site {
  margin: 0;
  min-height: 100vh;
  /* Header, column, footer — so the footer sits at the bottom of a short page. */
  display: flex;
  flex-direction: column;
  background: var(--paper);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--type-base-size);
  /* CLAUDE.md: never render UI text at weight 400. */
  font-weight: var(--weight-body);
  line-height: 1.7;
}

/*
 * CLAUDE.md: "Give every interactive element a visible focus ring —
 * outline: 2px solid var(--teal), 2px offset." Declared once, on everything,
 * so a control added later cannot be born without one.
 */
:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 2px;
}

/*
 * The page column. 1240px is the design's own max width
 * (design/MainSite.dc.html; docs/02-mainsite-spec.md §3.2) — HTML wins for
 * values, per CLAUDE.md's precedence rule.
 */
.ha-main {
  display: flex;
  flex-direction: column;
  /* Grows into whatever the header and footer leave. */
  flex: 1 0 auto;
  width: 100%;
  /*
   * 44px between bands is the design's own rhythm (`margin: 0 0 44px` on
   * every one of them in design/MainSite.dc.html). It is structure, not a
   * decision the Layout screen owns — no field expresses the distance
   * BETWEEN two sections, only the padding inside each.
   */
  gap: 44px;
  max-width: 1240px;
  margin: 0 auto;
  padding: 32px;
}

.ha-section {
  /* A flex child will not shrink below its content without this. */
  min-width: 0;
}

/*
 * A band with nothing to show (a Banner with no picture chosen, a cleared
 * Quote, a Featured band with no article) is still shipped as an empty
 * wrapper — and an empty flex item would still cost the 44px gap above.
 * page.php writes the wrapper with nothing between its tags, so :empty
 * holds exactly when the renderer returned nothing.
 */
.ha-section:empty {
  display: none;
}

/* Hidden on the wide configuration only. */
@media (min-width: 861px) {
  .ha-section--hide-wide {
    display: none;
  }
}

@media (max-width: 860px) {
  .ha-main {
    padding: 0 20px;
  }

  /* Hidden on the narrow configuration only. */
  .ha-section--hide-narrow {
    display: none;
  }

  /*
   * The narrow configuration's section order. DOM order is the wide
   * configuration's, so only this half needs stating — ADR-0004 rule 1: the
   * two configurations never inherit from each other, and neither is a
   * base for the other.
   *
   * --n13 is the OVERFLOW bucket, not a thirteenth position: a section past
   * the twelfth shares it (HA\Render\ThemeEngine::MAX_ORDERED_SECTIONS), so
   * it sorts after everything ordered and keeps DOM order among its
   * fellows. Without a class it would land at order 0 — before all of them.
   */
  .ha-section--n1 { order: 1; }
  .ha-section--n2 { order: 2; }
  .ha-section--n3 { order: 3; }
  .ha-section--n4 { order: 4; }
  .ha-section--n5 { order: 5; }
  .ha-section--n6 { order: 6; }
  .ha-section--n7 { order: 7; }
  .ha-section--n8 { order: 8; }
  .ha-section--n9 { order: 9; }
  .ha-section--n10 { order: 10; }
  .ha-section--n11 { order: 11; }
  .ha-section--n12 { order: 12; }
  .ha-section--n13 { order: 13; }
}

/* ═════════════════════════════════════════════════════════════════════════
 * MILESTONE 5.2 — the six section renderers
 *
 * Everything below draws structure with values it does not choose:
 *
 *   - `--featured-title-size`, `--quote-section-pad`, `--latest-card-bg` and
 *     the ~60 others are `public/assets/generated/layout.css`'s, written by
 *     the Admin from the layout cascade, desktop at `:root` and mobile inside
 *     its own `@media (max-width: 860px)`. Nothing here restates one.
 *   - `--ink`, `--teal`, `--tint-massage` and the rest are `palette.css`'s.
 *     There is no hex in this file and no `rgba()` literal either: a
 *     translucency is `color-mix(in srgb, var(--token) N%, transparent)`, so
 *     a palette change carries it along — docs/04-design-system.md's own
 *     instruction for the category hover tints, applied everywhere.
 *   - `--ha-cat-color` / `--ha-cat-tint` are the **per-instance** pair: a
 *     card's own category accent and soft tint, set by the class
 *     HA\Render\TokenClasses emits, because the site's CSP drops the inline
 *     `style` attribute docs/08 and docs/10 prescribe
 *     (docs/12-open-questions.md#q22).
 *
 * Layout SELECT fields arrive as two classes, one per device configuration
 * (HA\Render\LayoutFields): `ha-o--…` is resolved above 860px, `ha-on--…` at
 * 860px and below. Only values that differ from this file's own base need a
 * rule — the default costs nothing, which is why each family below lists
 * fewer rules than the field has options.
 * ═════════════════════════════════════════════════════════════════════════ */

/*
 * The shared surfaces. Named once so a band, a card and a row cannot drift
 * apart, and so the border SELECT has one thing to change per block.
 */
.ha-site {
  --ha-hairline-color: color-mix(in srgb, var(--white) 65%, transparent);
  --ha-hairline: 1px solid var(--ha-hairline-color);
  --ha-glass: color-mix(in srgb, var(--white) 50%, transparent);
  --ha-soft-shadow: 0 10px 30px color-mix(in srgb, var(--ink) 12%, transparent);

  /*
   * The arch. `180px 28px 28px 28px` is the design's own top-left arch
   * (design/MainSite.dc.html) and the three small corners are the Admin's
   * `featured.image.radius`; only the big one is structural, so only it is
   * here — the same standing as `.ha-main`'s 1240px above.
   */
  --ha-arch: 180px;

  /* A pill, in the one sense every pill in the design agrees on. */
  --ha-pill: 999px;

  /* CLAUDE.md and docs/02 §7: 44px minimum touch target. */
  --ha-touch: 44px;
}

/* Screen-reader-only, for a real <label> that the design draws as a placeholder. */
.ha-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/*
 * An element the Admin turned off on one configuration only. The section-level
 * pair above is `.ha-section--hide-*`; this is its within-a-section twin, and
 * it is what keeps a `show` toggle live on BOTH device tabs without a second
 * markup tree.
 */
@media (min-width: 861px) {
  .ha-hide-wide { display: none !important; }
}

@media (max-width: 860px) {
  .ha-hide-narrow { display: none !important; }
}

/* ── The frame every band shares ────────────────────────────────────────── */

.ha-featured,
.ha-quote,
.ha-banner,
.ha-listing,
.ha-newsletter,
.ha-share {
  --ha-border: var(--ha-hairline);
  --ha-shadow: none;
  border: var(--ha-border);
  box-shadow: var(--ha-shadow);
}

/* `border: none` / `soft-shadow` — the two values that are not the default. */
@media (min-width: 861px) {
  .ha-o--featured-section-border-none,
  .ha-o--quote-section-border-none,
  .ha-o--banner-section-border-none,
  .ha-o--articles-section-border-none { --ha-border: none; --ha-shadow: none; }

  .ha-o--featured-section-border-soft-shadow,
  .ha-o--quote-section-border-soft-shadow,
  .ha-o--banner-section-border-soft-shadow,
  .ha-o--articles-section-border-soft-shadow { --ha-border: none; --ha-shadow: var(--ha-soft-shadow); }
}

@media (max-width: 860px) {
  .ha-on--featured-section-border-none,
  .ha-on--quote-section-border-none,
  .ha-on--banner-section-border-none,
  .ha-on--articles-section-border-none { --ha-border: none; --ha-shadow: none; }

  .ha-on--featured-section-border-soft-shadow,
  .ha-on--quote-section-border-soft-shadow,
  .ha-on--banner-section-border-soft-shadow,
  .ha-on--articles-section-border-soft-shadow { --ha-border: none; --ha-shadow: var(--ha-soft-shadow); }
}

/* ── The category pair, per instance ────────────────────────────────────── */

/*
 * The neutral answer, for an article with no category and for a token the
 * palette does not carry. Both are legitimate states, and neither is a hole.
 */
.ha-card,
.ha-row,
.ha-featured {
  --ha-cat-color: var(--text-muted);
  --ha-cat-tint: var(--placeholder-bg);
}

/*
 * The identity mapping HA\Render\TokenClasses documents: token X becomes
 * `var(X)`. No colour is chosen here — which token a category wears is
 * `ha category:update --color=… --tint=…`, and what the token is worth is the
 * active palette's.
 *
 * tests/Unit/SiteSectionsTest.php asserts that every one of the 27 core
 * tokens has both rules, so this list cannot fall behind
 * HA\Support\Ui\PaletteTokens.
 */
.ha-fg--ink { --ha-cat-color: var(--ink); }
.ha-fg--teal { --ha-cat-color: var(--teal); }
.ha-fg--teal-deep { --ha-cat-color: var(--teal-deep); }
.ha-fg--sage { --ha-cat-color: var(--sage); }
.ha-fg--lake { --ha-cat-color: var(--lake); }
.ha-fg--paper { --ha-cat-color: var(--paper); }
.ha-fg--white { --ha-cat-color: var(--white); }
.ha-fg--text { --ha-cat-color: var(--text); }
.ha-fg--text-soft { --ha-cat-color: var(--text-soft); }
.ha-fg--text-muted { --ha-cat-color: var(--text-muted); }
.ha-fg--text-faint { --ha-cat-color: var(--text-faint); }
.ha-fg--link { --ha-cat-color: var(--link); }
.ha-fg--green-mid { --ha-cat-color: var(--green-mid); }
.ha-fg--green-deep { --ha-cat-color: var(--green-deep); }
.ha-fg--blue-deep { --ha-cat-color: var(--blue-deep); }
.ha-fg--base-a { --ha-cat-color: var(--base-a); }
.ha-fg--base-b { --ha-cat-color: var(--base-b); }
.ha-fg--base-c { --ha-cat-color: var(--base-c); }
.ha-fg--wash-a { --ha-cat-color: var(--wash-a); }
.ha-fg--wash-b { --ha-cat-color: var(--wash-b); }
.ha-fg--wash-c { --ha-cat-color: var(--wash-c); }
.ha-fg--tint-massage { --ha-cat-color: var(--tint-massage); }
.ha-fg--tint-kinesiology { --ha-cat-color: var(--tint-kinesiology); }
.ha-fg--tint-cures { --ha-cat-color: var(--tint-cures); }
.ha-fg--tint-advice { --ha-cat-color: var(--tint-advice); }
.ha-fg--placeholder-bg { --ha-cat-color: var(--placeholder-bg); }
.ha-fg--placeholder-text { --ha-cat-color: var(--placeholder-text); }

.ha-tint--ink { --ha-cat-tint: var(--ink); }
.ha-tint--teal { --ha-cat-tint: var(--teal); }
.ha-tint--teal-deep { --ha-cat-tint: var(--teal-deep); }
.ha-tint--sage { --ha-cat-tint: var(--sage); }
.ha-tint--lake { --ha-cat-tint: var(--lake); }
.ha-tint--paper { --ha-cat-tint: var(--paper); }
.ha-tint--white { --ha-cat-tint: var(--white); }
.ha-tint--text { --ha-cat-tint: var(--text); }
.ha-tint--text-soft { --ha-cat-tint: var(--text-soft); }
.ha-tint--text-muted { --ha-cat-tint: var(--text-muted); }
.ha-tint--text-faint { --ha-cat-tint: var(--text-faint); }
.ha-tint--link { --ha-cat-tint: var(--link); }
.ha-tint--green-mid { --ha-cat-tint: var(--green-mid); }
.ha-tint--green-deep { --ha-cat-tint: var(--green-deep); }
.ha-tint--blue-deep { --ha-cat-tint: var(--blue-deep); }
.ha-tint--base-a { --ha-cat-tint: var(--base-a); }
.ha-tint--base-b { --ha-cat-tint: var(--base-b); }
.ha-tint--base-c { --ha-cat-tint: var(--base-c); }
.ha-tint--wash-a { --ha-cat-tint: var(--wash-a); }
.ha-tint--wash-b { --ha-cat-tint: var(--wash-b); }
.ha-tint--wash-c { --ha-cat-tint: var(--wash-c); }
.ha-tint--tint-massage { --ha-cat-tint: var(--tint-massage); }
.ha-tint--tint-kinesiology { --ha-cat-tint: var(--tint-kinesiology); }
.ha-tint--tint-cures { --ha-cat-tint: var(--tint-cures); }
.ha-tint--tint-advice { --ha-cat-tint: var(--tint-advice); }
.ha-tint--placeholder-bg { --ha-cat-tint: var(--placeholder-bg); }
.ha-tint--placeholder-text { --ha-cat-tint: var(--placeholder-text); }

/* ── Featured (docs/02 §3.3.1) ──────────────────────────────────────────── */

.ha-featured {
  --ha-featured-media-order: 0;
  --ha-featured-media-display: block;
  /*
   * docs/02 §3.3.1's `minmax(360px, 1fr)`, capped at the card's own width:
   * on a phone the card's content box is narrower than 360px, and a bare
   * 360px floor pushed the text and picture past the card and the page to
   * ~413px — wide enough that the phone's full-screen menu overlay grew with
   * it and put its close button off-screen (owner, 2026-09-28).
   */
  --ha-featured-cols: repeat(auto-fit, minmax(min(360px, 100%), 1fr));
  --ha-kicker-position: static;
  --ha-kicker-inset: auto;
  --ha-kicker-order: 0;
  --ha-kicker-weight: var(--weight-label);
  --ha-title-weight: var(--weight-body);
  --ha-title-align: left;
  --ha-meta-align: right;
  --ha-button-position: static;
  --ha-button-inset: auto;
  --ha-image-radius: var(--ha-arch) var(--featured-image-radius) var(--featured-image-radius) var(--featured-image-radius);

  position: relative;
  display: grid;
  grid-template-columns: var(--ha-featured-cols);
  gap: 52px;
  align-items: center;
  background: var(--featured-section-bg);
  border-radius: var(--featured-section-radius);
  padding: var(--featured-section-pad);
}

.ha-featured__text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-width: 0;
}

.ha-featured__kicker {
  order: var(--ha-kicker-order);
  position: var(--ha-kicker-position);
  inset: var(--ha-kicker-inset);
  margin: 0 0 18px;
  padding: 7px 14px;
  border-radius: var(--ha-pill);
  border: var(--ha-hairline);
  background: var(--featured-kicker-bg);
  color: var(--featured-kicker-color);
  font-size: var(--featured-kicker-size);
  font-weight: var(--ha-kicker-weight);
  letter-spacing: .18em;
  text-transform: uppercase;
}

.ha-featured__title {
  order: 1;
  width: 100%;
  margin: 0 0 14px;
  font-size: var(--featured-title-size);
  font-weight: var(--ha-title-weight);
  line-height: 1.12;
  color: var(--featured-title-color);
  text-align: var(--ha-title-align);
  text-wrap: pretty;
}

.ha-featured__title-link {
  color: inherit;
  text-decoration: none;
}

.ha-featured__title-link:hover {
  text-decoration: underline;
}

/*
 * `featured.desc.lines` is a line COUNT, so the excerpt is clamped rather
 * than trimmed server-side: trimming would put an ellipsis in the markup and
 * make the text depend on a number the mobile configuration can set
 * differently, which one DOM cannot express. The clamp can.
 */
.ha-featured__excerpt {
  order: 2;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--featured-desc-lines);
  line-clamp: var(--featured-desc-lines);
  overflow: hidden;
  max-width: 44ch;
  margin: 0 0 18px;
  font-size: var(--featured-desc-size);
  color: var(--featured-desc-color);
  line-height: 1.7;
  text-wrap: pretty;
}

.ha-featured__meta {
  order: 3;
  width: 100%;
  margin: 0 0 26px;
  font-size: var(--featured-meta-size);
  color: var(--featured-meta-color);
  text-align: var(--ha-meta-align);
}

.ha-featured__button {
  order: 4;
  position: var(--ha-button-position);
  inset: var(--ha-button-inset);
  display: inline-flex;
  align-items: center;
  min-height: var(--ha-touch);
  padding: 0 28px;
  border-radius: var(--featured-button-radius);
  background: var(--featured-button-bg);
  color: var(--featured-button-color);
  font-size: var(--featured-button-size);
  font-weight: var(--weight-label);
  text-decoration: none;
}

.ha-featured__media {
  order: var(--ha-featured-media-order);
  display: var(--ha-featured-media-display);
  overflow: hidden;
  height: var(--featured-image-height);
  border: var(--ha-image-border, none);
  border-radius: var(--ha-image-radius);
  background-color: var(--ha-cat-tint);
}

/* `featured.image.border` — a toggle, so it is on per configuration. */
@media (min-width: 861px) {
  .ha-o--featured-image-border-on { --ha-image-border: 1px solid var(--featured-image-bcolor); }
}

@media (max-width: 860px) {
  .ha-on--featured-image-border-on { --ha-image-border: 1px solid var(--featured-image-bcolor); }
}

.ha-featured__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/*
 * `featured.section.media` — image-right is the base. `image-above` collapses
 * to one column and puts the picture first; `no-image` removes it.
 */
@media (min-width: 861px) {
  .ha-o--featured-section-media-image-left { --ha-featured-media-order: -1; }
  .ha-o--featured-section-media-image-above { --ha-featured-cols: 1fr; --ha-featured-media-order: -1; }
  .ha-o--featured-section-media-no-image { --ha-featured-media-display: none; }

  /*
   * `featured.kicker.place`. `over-image` lifts the pill out of the flow and
   * pins it to the top of the picture's side of the band — an approximation
   * of the design's intent that needs no second copy of the element, which
   * is what moving it between columns would cost.
   */
  .ha-o--featured-kicker-place-below-title { --ha-kicker-order: 2; }
  .ha-o--featured-kicker-place-over-image { --ha-kicker-position: absolute; --ha-kicker-inset: 24px 24px auto auto; }

  .ha-o--featured-kicker-weight-regular { --ha-kicker-weight: var(--weight-body); }
  .ha-o--featured-title-weight-bold { --ha-title-weight: var(--weight-label); }
  .ha-o--featured-title-align-center { --ha-title-align: center; }
  .ha-o--featured-meta-side-left { --ha-meta-align: left; }

  /* `featured.button.place` — both non-default values leave the text column. */
  .ha-o--featured-button-place-below-image { --ha-button-position: absolute; --ha-button-inset: auto 32px 32px auto; }
  .ha-o--featured-button-place-top-right { --ha-button-position: absolute; --ha-button-inset: 32px 32px auto auto; }

  .ha-o--featured-image-corner-all-corners { --ha-image-radius: var(--featured-image-radius); }
  .ha-o--featured-image-corner-top-arch { --ha-image-radius: var(--ha-arch) var(--ha-arch) var(--featured-image-radius) var(--featured-image-radius); }
}

@media (max-width: 860px) {
  .ha-featured { gap: 26px; }

  .ha-on--featured-section-media-image-left { --ha-featured-media-order: -1; }
  .ha-on--featured-section-media-image-above { --ha-featured-cols: 1fr; --ha-featured-media-order: -1; }
  .ha-on--featured-section-media-no-image { --ha-featured-media-display: none; }

  .ha-on--featured-kicker-place-below-title { --ha-kicker-order: 2; }
  .ha-on--featured-kicker-place-over-image { --ha-kicker-position: absolute; --ha-kicker-inset: 20px 20px auto auto; }

  .ha-on--featured-kicker-weight-regular { --ha-kicker-weight: var(--weight-body); }
  .ha-on--featured-title-weight-bold { --ha-title-weight: var(--weight-label); }
  .ha-on--featured-title-align-center { --ha-title-align: center; }
  .ha-on--featured-meta-side-left { --ha-meta-align: left; }

  .ha-on--featured-button-place-below-image { --ha-button-position: absolute; --ha-button-inset: auto 20px 20px auto; }
  .ha-on--featured-button-place-top-right { --ha-button-position: absolute; --ha-button-inset: 20px 20px auto auto; }

  .ha-on--featured-image-corner-all-corners { --ha-image-radius: var(--featured-image-radius); }
  .ha-on--featured-image-corner-top-arch { --ha-image-radius: var(--ha-arch) var(--ha-arch) var(--featured-image-radius) var(--featured-image-radius); }
}

/* ── Quote (docs/02 §3.3.2) ─────────────────────────────────────────────── */

.ha-quote {
  --ha-quote-style: italic;
  --ha-quote-align: center;

  margin: 0;
  padding: var(--quote-section-pad);
  border-radius: var(--quote-section-radius);
  background: var(--quote-section-bg);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  text-align: var(--ha-quote-align);
}

.ha-quote__text {
  max-width: 46ch;
  margin: 0 auto;
  font-size: var(--quote-text-size);
  font-style: var(--ha-quote-style);
  color: var(--quote-text-color);
  line-height: 1.4;
  text-wrap: pretty;
}

/*
 * The quotation marks are the template's decoration, not part of the stored
 * sentence — milestone 3.9's catalogue entry says so, which is why they are
 * drawn here and never concatenated onto an operator's own text.
 */
.ha-quote__text::before { content: "\201C"; }
.ha-quote__text::after { content: "\201D"; }

.ha-quote__attribution {
  display: block;
  margin: 12px 0 0;
  font-size: var(--quote-attrib-size);
  font-weight: var(--weight-label);
  font-style: normal;
  color: var(--quote-attrib-color);
  letter-spacing: .18em;
  text-transform: uppercase;
}

/* The em dash before the attribution, likewise decoration. */
.ha-quote__attribution::before { content: "\2014\00A0"; }

@media (min-width: 861px) {
  .ha-o--quote-text-style-regular { --ha-quote-style: normal; }
  .ha-o--quote-text-align-left { --ha-quote-align: left; }
}

@media (max-width: 860px) {
  .ha-on--quote-text-style-regular { --ha-quote-style: normal; }
  .ha-on--quote-text-align-left { --ha-quote-align: left; }
}

/*
 * The banner band — the owner's request (2026-09-28), a picture band that
 * sits where Quote used to sit alone (HA\Render\Sections\BannerRenderer's own
 * docblock has the full reasoning). `whole` (the default) is the plain
 * `<img>` at its own proportions — `design/banner.png` is never cropped;
 * `fill` gives it a fixed height and crops it to cover, for a plainer
 * picture with room to spare.
 */
.ha-banner {
  margin: 0;
  padding: var(--banner-section-pad);
  border-radius: var(--banner-section-radius);
  background: var(--banner-section-bg);
  overflow: hidden;
}

.ha-banner__img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: inherit;
}

@media (min-width: 861px) {
  .ha-o--banner-image-fit-fill .ha-banner__img {
    height: var(--banner-image-height);
    object-fit: cover;
  }
}

@media (max-width: 860px) {
  .ha-on--banner-image-fit-fill .ha-banner__img {
    height: var(--banner-image-height);
    object-fit: cover;
  }
}

/* ── Latest writings (docs/02 §3.3.3) ───────────────────────────────────── */

.ha-latest {
  background: var(--latest-section-bg);
  border-radius: var(--latest-section-radius);
}

.ha-latest__heading {
  margin: 0 0 22px;
  font-size: var(--latest-heading-size);
  font-weight: var(--weight-heading);
  color: var(--latest-heading-color);
  text-wrap: pretty;
}

.ha-latest__grid {
  display: grid;
  gap: 22px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}

/*
 * `latest.section.cols` is an explicit column count and only means anything
 * where there is room for it; below the config split the grid stays auto-fit,
 * which is what "fluid from ~360px" needs.
 */
@media (min-width: 861px) {
  .ha-latest__grid { grid-template-columns: repeat(var(--latest-section-cols), minmax(0, 1fr)); }
}

.ha-card {
  --ha-hover-bg: color-mix(in srgb, var(--ha-cat-tint) 50%, transparent);
  --ha-hover-border: color-mix(in srgb, var(--ha-cat-color) 50%, transparent);

  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding: 10px;
  border: 1px solid transparent;
  border-radius: var(--latest-card-radius);
  background: var(--latest-card-bg);
  /* docs/04: the client tested .6s, then .3s, and chose instant. */
  transition: none;
}

.ha-card:hover,
.ha-card:focus-within {
  background: var(--ha-hover-bg);
  border-color: var(--ha-hover-border);
}

.ha-card__media {
  overflow: hidden;
  height: var(--latest-card-img);
  border-radius: 14px;
  background-color: var(--ha-cat-tint);
}

.ha-card__image,
.ha-row__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.ha-card__body { padding: 0 10px; }

.ha-card__kicker {
  margin: 15px 0 7px;
  font-size: var(--type-size-eyebrow);
  font-weight: var(--weight-label);
  color: var(--ha-cat-color);
  letter-spacing: .16em;
  text-transform: uppercase;
}

.ha-card__title {
  margin: 0 0 7px;
  font-size: var(--latest-card-title-size);
  font-weight: var(--weight-card-title);
  line-height: 1.3;
  color: var(--latest-card-title-color);
  text-wrap: pretty;
}

.ha-card__link,
.ha-row__link {
  color: inherit;
  text-decoration: none;
}

.ha-card__link:hover,
.ha-row__link:hover { text-decoration: underline; }

.ha-card__meta {
  margin: 0;
  font-size: var(--latest-card-meta-size);
  color: var(--latest-card-meta-color);
}

/* `latest.card.hover` — the two values that are not `category-color`. */
@media (min-width: 861px) {
  .ha-o--latest-card-hover-neutral { --ha-hover-bg: var(--white); --ha-hover-border: var(--ha-hairline-color); }
  .ha-o--latest-card-hover-none { --ha-hover-bg: var(--latest-card-bg); --ha-hover-border: transparent; }
}

@media (max-width: 860px) {
  .ha-on--latest-card-hover-neutral { --ha-hover-bg: var(--white); --ha-hover-border: var(--ha-hairline-color); }
  .ha-on--latest-card-hover-none { --ha-hover-bg: var(--latest-card-bg); --ha-hover-border: transparent; }
}

/* ── Our Articles (docs/02 §3.3.4, §3.5) ────────────────────────────────── */

.ha-listing {
  padding: 34px 38px;
  border-radius: var(--articles-section-radius);
  background: var(--articles-section-bg);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
}

.ha-listing__header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 14px;
  margin: 0 0 20px;
}

.ha-listing__heading {
  margin: 0;
  font-size: var(--articles-heading-size);
  font-weight: var(--weight-heading);
  color: var(--articles-heading-color);
  text-wrap: pretty;
}

.ha-listing__subtitle {
  margin: 0;
  font-size: var(--type-size-meta);
  color: var(--text-muted);
}

/* The search results page's no-results / prompt line (milestone 5.7). */
.ha-listing__empty {
  margin: 18px 0 0;
  font-size: var(--type-size-body);
  color: var(--text-soft);
  text-wrap: pretty;
}

.ha-listing__rows {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.ha-row {
  --ha-hover-bg: color-mix(in srgb, var(--ha-cat-tint) 50%, transparent);
  --ha-hover-border: color-mix(in srgb, var(--ha-cat-color) 50%, transparent);
  --ha-row-media-display: block;
  --ha-row-marker: none;

  display: grid;
  grid-template-columns: var(--articles-row-img) 1fr auto;
  align-items: center;
  gap: 18px;
  padding: 10px;
  border: 1px solid transparent;
  border-radius: var(--articles-row-radius);
  transition: none;
}

.ha-row::before {
  content: var(--ha-row-marker);
  color: var(--ha-cat-color);
}

.ha-row:hover,
.ha-row:focus-within {
  background: var(--ha-hover-bg);
  border-color: var(--ha-hover-border);
}

.ha-row__media {
  display: var(--ha-row-media-display);
  overflow: hidden;
  height: 86px;
  border-radius: 11px;
  background-color: var(--ha-cat-tint);
}

.ha-row__body { min-width: 0; }

.ha-row__kicker {
  margin: 0 0 4px;
  font-size: var(--type-size-eyebrow);
  font-weight: var(--weight-label);
  color: var(--ha-cat-color);
  letter-spacing: .14em;
  text-transform: uppercase;
}

.ha-row__title {
  margin: 0 0 4px;
  font-size: var(--type-size-card-title);
  font-weight: var(--weight-card-title);
  line-height: 1.25;
  color: var(--ink);
  text-wrap: pretty;
}

.ha-row__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0;
}

.ha-chip {
  padding: 4px 10px;
  border-radius: var(--ha-pill);
  background: color-mix(in srgb, var(--teal) 12%, transparent);
  color: var(--articles-tags-color);
  font-size: var(--type-size-eyebrow);
  font-weight: var(--weight-ui);
}

/* milestone 5.6: a tag chip is a link to `/problem/<slug>` now that the route
   exists (docs/02 §3.5). It keeps the chip's look, loses the underline, and
   lifts a little on hover so it reads as reachable. */
.ha-chip--link {
  text-decoration: none;
  cursor: pointer;
}

.ha-chip--link:hover,
.ha-chip--link:focus-visible {
  background: color-mix(in srgb, var(--teal) 22%, transparent);
}

.ha-chip--link:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 2px;
}

/* docs/02 §3.5's `← Home` back link on a `list` route (a per-tag listing). */
.ha-listing__back {
  display: inline-block;
  margin: 0 0 12px;
  font-size: var(--type-size-meta);
  font-weight: var(--weight-label);
  color: var(--teal-deep);
  text-decoration: none;
}

.ha-listing__back:hover {
  text-decoration: underline;
}

.ha-listing__back:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 2px;
}

.ha-row__aside {
  display: flex;
  align-items: center;
  gap: 14px;
}

.ha-row__date {
  font-size: var(--type-size-meta);
  color: var(--text-muted);
  white-space: nowrap;
}

.ha-row__arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--sage) 55%, transparent);
  color: var(--ink);
}

/* `articles.row.layout` — text-only drops the picture, dotted-list adds a marker. */
@media (min-width: 861px) {
  .ha-o--articles-row-layout-text-only { --ha-row-media-display: none; grid-template-columns: 1fr auto; }
  .ha-o--articles-row-layout-dotted-list { --ha-row-media-display: none; --ha-row-marker: "\2022"; grid-template-columns: auto 1fr auto; }

  .ha-o--articles-row-hover-neutral { --ha-hover-bg: var(--white); --ha-hover-border: var(--ha-hairline-color); }
  .ha-o--articles-row-hover-none { --ha-hover-bg: transparent; --ha-hover-border: transparent; }
}

@media (max-width: 860px) {
  .ha-listing { padding: 22px 18px; }

  /* docs/02 §3.5's narrow row: 56px thumb, one meta line, no arrow column. */
  .ha-row { grid-template-columns: 56px 1fr; gap: 12px; }
  .ha-row__media { height: 56px; }
  .ha-row__aside { grid-column: 2; }

  .ha-on--articles-row-layout-text-only { --ha-row-media-display: none; grid-template-columns: 1fr; }
  .ha-on--articles-row-layout-dotted-list { --ha-row-media-display: none; --ha-row-marker: "\2022"; grid-template-columns: auto 1fr; }

  .ha-on--articles-row-hover-neutral { --ha-hover-bg: var(--white); --ha-hover-border: var(--ha-hairline-color); }
  .ha-on--articles-row-hover-none { --ha-hover-bg: transparent; --ha-hover-border: transparent; }
}

/* ── The By problem directory (docs/02 §3.4, milestone 5.6) ─────────────── */

/*
 * A grid of tag cards, each a link to `/problem/<slug>`. A tag's own colour
 * token rides `--ha-cat-color` (HA\Render\TokenClasses), so the placeholder box
 * tints to it; a tag with no colour keeps the neutral fallback below.
 */
.ha-problems {
  --ha-cat-color: var(--text-faint);

  padding: 8px 0 4px;
}

.ha-problems__heading {
  margin: 0 0 8px;
  font-size: var(--type-size-page-h1);
  font-weight: var(--weight-heading);
  color: var(--ink);
  text-wrap: pretty;
}

.ha-problems__intro {
  margin: 0 0 30px;
  font-size: var(--type-size-card-title);
  font-weight: var(--weight-body);
  line-height: 1.6;
  color: var(--text);
  text-wrap: pretty;
}

.ha-problems__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 16px;
}

.ha-problem-card {
  display: block;
  padding: 24px 22px;
  border: 1px solid var(--ha-hairline-color);
  border-radius: 20px;
  background: color-mix(in srgb, var(--white) 55%, transparent);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  text-decoration: none;
  transition: none;
}

.ha-problem-card:hover,
.ha-problem-card:focus-within {
  background: color-mix(in srgb, var(--white) 80%, transparent);
  border-color: color-mix(in srgb, var(--ha-cat-color) 40%, transparent);
}

.ha-problem-card:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 2px;
}

.ha-problem-card__media {
  display: block;
  overflow: hidden;
  height: 110px;
  margin: 0 0 14px;
  border-radius: 14px;
  background-color: color-mix(in srgb, var(--ha-cat-color) 22%, transparent);
}

.ha-problem-card__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.ha-problem-card__label {
  display: block;
  font-size: var(--type-size-card-title);
  font-weight: var(--weight-card-title);
  color: var(--ink);
}

.ha-problem-card__count {
  display: block;
  margin: 5px 0 0;
  font-size: var(--type-size-meta);
  font-weight: var(--weight-ui);
  color: var(--text-muted);
}

@media (max-width: 860px) {
  .ha-problems__heading { font-size: var(--type-size-h2); }
  .ha-problems__grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
}

/* ── The pager (docs/02 §3.5, milestone 5.4) ────────────────────────────── */

/*
 * One nav carrying every control the three `articles.pager.mode` values need,
 * with the two device configurations' values as classes on it
 * (`ha-o--articles-pager-mode-…` above 860px, `ha-on--…` at 860px and below).
 * The base state below is `pages`, which is why it needs no class of its own:
 * the numbered row is drawn and the other block is not.
 *
 * `.ha-pager__auto` is the sentinel `public/assets/site/js/listing.js` watches
 * in infinite-scroll mode. It is `display: none` at any width whose
 * configuration asked for another mode, and an element that is not drawn never
 * intersects anything — so this stylesheet, and not the script, is what turns
 * the automatic behaviour on and off.
 */
.ha-pager {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 26px 0 4px;
}

.ha-pager__pages,
.ha-pager__more {
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px;
}

.ha-pager__pages { display: flex; }
.ha-pager__more { display: none; }
.ha-pager__auto { display: none; }

.ha-pager__step,
.ha-pager__number,
.ha-pager__button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--ha-hairline-color);
  background: color-mix(in srgb, var(--white) 75%, transparent);
  color: var(--text-soft);
  font-size: var(--type-size-meta);
  font-weight: var(--weight-label);
  text-decoration: none;
  /* docs/04: the client chose instant. */
  transition: none;
}

.ha-pager__step,
.ha-pager__button {
  padding: 10px 16px;
  border-radius: var(--ha-pill);
}

/*
 * milestone 5.10: a circle for the ordinary one- and two-digit page number,
 * but `min-width` rather than a fixed `width` so a three-digit number
 * (~2000 articles, 5.4's flagged overflow) grows the control into a pill and
 * is never clipped. `border-radius` is the pill token, not `50%`: at 40×40 it
 * still reads as a circle, and past that it rounds the pill's ends cleanly
 * instead of drawing an ellipse.
 */
.ha-pager__number {
  min-width: 40px;
  height: 40px;
  padding: 0 4px;
  border-radius: var(--ha-pill);
}

/* docs/02 §3.5: "Current page is a filled `--teal` circle." */
.ha-pager__number--current {
  border-color: transparent;
  background: var(--teal);
  color: var(--paper);
}

.ha-pager__step:hover,
.ha-pager__number:hover,
.ha-pager__button:hover {
  background: var(--white);
}

/* The step's two shapes: the word at wide widths, the chevron at narrow ones. */
.ha-pager__chevron { display: none; }

@media (min-width: 861px) {
  .ha-o--articles-pager-mode-load-more .ha-pager__pages,
  .ha-o--articles-pager-mode-infinite-scroll .ha-pager__pages { display: none; }

  .ha-o--articles-pager-mode-load-more .ha-pager__more,
  .ha-o--articles-pager-mode-infinite-scroll .ha-pager__more { display: flex; }

  .ha-o--articles-pager-mode-infinite-scroll .ha-pager__auto { display: block; }
}

@media (max-width: 860px) {
  /*
   * docs/02 §3.5's narrow pager: `‹` / circles / `›`, at the 44px touch
   * minimum. The word and the chevron are two shapes of one control — the
   * accessible name is the `aria-label` and does not change with the width.
   */
  .ha-pager__chevron { display: inline; }
  .ha-pager__word { display: none; }

  .ha-pager__step,
  .ha-pager__number,
  .ha-pager__button {
    min-width: var(--ha-touch);
    min-height: var(--ha-touch);
  }

  .ha-pager__number {
    min-width: var(--ha-touch);
    height: var(--ha-touch);
  }

  .ha-pager__step { padding: 10px; }

  .ha-on--articles-pager-mode-load-more .ha-pager__pages,
  .ha-on--articles-pager-mode-infinite-scroll .ha-pager__pages { display: none; }

  .ha-on--articles-pager-mode-load-more .ha-pager__more,
  .ha-on--articles-pager-mode-infinite-scroll .ha-pager__more { display: flex; }

  .ha-on--articles-pager-mode-infinite-scroll .ha-pager__auto { display: block; }
}

/* ── Newsletter (docs/02 §3.7) ──────────────────────────────────────────── */

.ha-newsletter {
  padding: var(--newsletter-section-pad);
  border-radius: var(--newsletter-section-radius);
  background: color-mix(in srgb, var(--newsletter-section-bg) 16%, transparent);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  text-align: center;
}

.ha-newsletter__inner {
  max-width: 560px;
  margin: 0 auto;
}

.ha-newsletter__heading {
  margin: 0 0 8px;
  font-size: var(--newsletter-heading-size);
  font-weight: var(--weight-heading);
  color: var(--newsletter-heading-color);
  text-wrap: pretty;
}

.ha-newsletter__text {
  margin: 0 0 22px;
  font-size: var(--newsletter-desc-size);
  color: var(--text);
  line-height: 1.7;
  text-wrap: pretty;
}

.ha-newsletter__form {
  display: flex;
  gap: 10px;
  max-width: 440px;
  margin: 0 auto;
}

.ha-newsletter__input {
  flex: 1;
  min-width: 0;
  min-height: var(--ha-touch);
  padding: 0 18px;
  border: var(--ha-hairline);
  border-radius: var(--newsletter-input-radius);
  background: color-mix(in srgb, var(--white) 85%, transparent);
  color: var(--ink);
  font-family: inherit;
  font-size: var(--type-base-size);
  font-weight: var(--weight-ui);
}

/*
 * Placeholder contrast — milestone 7.6, closing the Phase 6 gate's own
 * observation: "site input placeholders use the browser default grey
 * (~2-3.4:1)". A browser's default placeholder is the input's colour at about
 * 54% opacity, which fails 4.5:1 against any of this site's input fills and is
 * the one piece of text on the public site that was never audited, because no
 * rule in this file had ever named it.
 *
 * `--text-soft` is a palette token every palette defines and every palette
 * resolves against the paper surfaces these inputs sit on, so the fix travels
 * with the palette instead of being one grey chosen once. `opacity: 1` is
 * required: Firefox applies its own opacity to ::placeholder on top of the
 * colour, so setting the colour alone would leave it washed out again.
 */
.ha-newsletter__input::placeholder,
.ha-search__input::placeholder {
  color: var(--text-soft);
  opacity: 1;
}

.ha-newsletter__button {
  min-height: var(--ha-touch);
  padding: 0 24px;
  border: 0;
  border-radius: var(--newsletter-button-radius);
  background: var(--newsletter-button-bg);
  color: var(--newsletter-button-color);
  font-family: inherit;
  font-size: var(--type-size-meta);
  font-weight: var(--weight-label);
  white-space: nowrap;
  cursor: pointer;
}

@media (max-width: 860px) {
  .ha-newsletter__form { flex-direction: column; }
}

/*
 * The consent line — milestone 6.2, docs/12-open-questions.md#q12. Owner-
 * authored, shown only when non-empty; a plain small note under the form,
 * on `.ha-newsletter__text`'s own measure rather than the 440px input row.
 */
.ha-newsletter__consent {
  max-width: 440px;
  margin: 12px auto 0;
  font-size: var(--type-size-meta);
  color: var(--text-muted);
  text-wrap: pretty;
}

/* ── Share (milestone 3.9's block; the page it sits on is 5.5's) ────────── */

.ha-share {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: 22px 26px;
  border-radius: 18px;
  background: color-mix(in srgb, var(--teal) 12%, transparent);
}

.ha-share__prompt {
  margin: 0;
  font-size: var(--type-size-card-title);
  font-style: italic;
  color: var(--ink);
  text-wrap: pretty;
}

.ha-share__pills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.ha-share__pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: var(--ha-touch);
  padding: 0 18px;
  border: 0;
  border-radius: var(--ha-pill);
  background: color-mix(in srgb, var(--white) 75%, transparent);
  color: var(--ink);
  font-family: inherit;
  font-size: var(--type-size-meta);
  font-weight: var(--weight-label);
  text-decoration: none;
  cursor: pointer;
}

.ha-share__pill:hover { background: var(--white); }

/*
 * The copy button is rendered `hidden` and revealed by
 * public/assets/site/js/share.js. The `hidden` attribute is a UA rule at
 * NORMAL weight, and the author `display: inline-flex` above beats it — so
 * without this companion a reader with no JavaScript would meet a copy button
 * that copies nothing. The Admin's own stylesheet pairs every `hidden` the
 * same way (public/assets/admin/css/dashboard.css).
 */
.ha-share__pill[hidden] { display: none; }

.ha-share__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ink);
}

.ha-share__dot--facebook { background: var(--lake); }
.ha-share__dot--x { background: var(--ink); }

/* ── The one-message page (HA\Http\Site\NewsletterController) ───────────── */

.ha-notice {
  padding: 34px 38px;
  border: var(--ha-hairline);
  border-radius: 28px;
  background: var(--ha-glass);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
}

.ha-notice__heading {
  margin: 0 0 10px;
  font-size: var(--type-size-page-h1);
  font-weight: var(--weight-heading);
  color: var(--ink);
  text-wrap: pretty;
}

.ha-notice__body {
  margin: 0 0 18px;
  color: var(--text);
  text-wrap: pretty;
}

.ha-notice__back {
  display: inline-flex;
  align-items: center;
  min-height: var(--ha-touch);
  color: var(--teal-deep);
  font-weight: var(--weight-label);
}

/* ═════════════════════════════════════════════════════════════════════════
 * MILESTONE 5.3 — the chrome
 *
 * The header (docs/02 §3.2), the narrow-width overlay, the dropdown and the
 * footer (§3.9). Same rules as everything above: no hex, no rgba literal — a
 * translucency is `color-mix(in srgb, var(--token) N%, transparent)` so a
 * palette change carries it — and every size, colour and on/off that the
 * Layout screen owns arrives as a custom property or a class.
 *
 * `--header-logo-size`, `--footer-logo-size` and `--footer-section-bg` are
 * `layout.css`'s, written from the `header` and `footer` scopes of the
 * cascade. Everything else here is structure.
 *
 * THREE STATES, not two. Above the 860px split and below it are the Admin's
 * two configurations; the third is **no JavaScript**, and it is the base
 * state of every rule below. `public/assets/site/js/chrome.js` announces
 * itself by putting `data-ha-menu` on the header, and only the rules that
 * name that attribute assume a script is running. Without it the menu is a
 * list, the dropdown opens on hover and focus, and the search box is simply
 * open — plainer, and working.
 * ═════════════════════════════════════════════════════════════════════════ */

/* ── The skip link — the first thing a keyboard reaches ─────────────────── */

.ha-skip {
  position: absolute;
  left: 12px;
  top: -64px;
  z-index: 200;
  padding: 10px 18px;
  border-radius: 12px;
  background: var(--ink);
  color: var(--paper);
  font-size: var(--type-size-meta);
  font-weight: var(--weight-label);
  text-decoration: none;
  transition: top .2s ease;
}

.ha-skip:focus {
  top: 12px;
}

/* ── The header band ────────────────────────────────────────────────────── */

.ha-header {
  z-index: 50;
  /* The design's own glass: .6 white over the wash, blurred at the sticky
     header's depth (docs/04 — "16px sticky header"). */
  background: color-mix(in srgb, var(--white) 60%, transparent);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  border-bottom: var(--ha-hairline);
}

/* `header.bar.sticky` — a toggle, so it is answered per configuration. */
@media (min-width: 861px) {
  .ha-o--header-bar-sticky-on { position: sticky; top: 0; }
}

@media (max-width: 860px) {
  .ha-on--header-bar-sticky-on { position: sticky; top: 0; }
}

.ha-header__bar {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 24px;
  max-width: 1240px;
  margin: 0 auto;
  padding: 16px 32px;
}

.ha-header__logo {
  order: 0;
  display: block;
  /*
   * What pushes the narrow row's icon buttons to the right edge, whichever of
   * them is drawn — the search one is only there while `header.search.show`
   * is on. An auto margin on the buttons themselves cannot do it: two items
   * each asking for the free space would split it between them and leave a
   * gap in the middle of the pair. Inert above the split, where the panel
   * grows into the free space before any auto margin is resolved.
   */
  margin-right: auto;
  color: var(--ink);
  text-decoration: none;
}

.ha-header__wordmark {
  display: block;
  font-size: var(--header-logo-size);
  font-weight: var(--weight-nav);
  line-height: 1.1;
}

/*
 * The design sets 9.5px here. docs/04's 13px floor is a RULE and the size is
 * a value, so the floor wins — `--type-size-eyebrow` is the token that
 * carries it.
 */
.ha-header__tagline {
  display: block;
  margin: 2px 0 0;
  font-size: var(--type-size-eyebrow);
  font-weight: var(--weight-label);
  letter-spacing: .24em;
  text-transform: uppercase;
  color: var(--teal-deep);
}

/* `header.logo.position` — centre takes the whole row and centres on it. */
@media (min-width: 861px) {
  .ha-o--header-logo-position-center { flex: 1 0 100%; text-align: center; }
}

@media (max-width: 860px) {
  .ha-on--header-logo-position-center { flex: 1 0 100%; text-align: center; }
}

/* ── The narrow-width icon buttons ──────────────────────────────────────── */

/*
 * docs/02 §3.2's narrow header: "logo left, two 44px icon buttons right:
 * search and a hamburger" (three lines since 2026-09-28). Both are rendered `hidden` and revealed by
 * chrome.js, and neither is shown above the split — there is nothing to open
 * there, where the nav is a row and the search box is a pill of its own.
 */
.ha-header__search-toggle,
.ha-header__burger {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--ha-touch);
  height: var(--ha-touch);
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink);
  cursor: pointer;
}

.ha-header__search-toggle { order: 1; }

/*
 * The traditional three-line menu icon — the owner's choice of 2026-09-28,
 * replacing the design's two-bar "20px over 14px, offset right" (docs/02 §2):
 * three equal 20px bars, 5px apart, centred in the 44px touch target.
 */
.ha-header__burger {
  order: 2;
  flex-direction: column;
  gap: 5px;
}

/*
 * THE COMPANION RULE, and it is not optional. The `hidden` attribute reaches
 * the browser as a UA stylesheet rule at NORMAL weight, and any author
 * `display` — the two above — beats it. Without this pair a reader with no
 * JavaScript (or one whose script was blocked, or had not arrived yet) would
 * meet two icon buttons that open nothing at all, above a menu that is already
 * a plain list. public/assets/admin/css/dashboard.css states the same rule for
 * every `hidden` the Admin writes; this is the site's half.
 */
.ha-header__search-toggle[hidden],
.ha-header__burger[hidden] { display: none; }

.ha-header__burger-bar {
  width: 20px;
  height: 2px;
  border-radius: 2px;
  background: var(--ink);
}

/* The design's own 4px between the pair, out of the bar's 12px column gap. */
.ha-header__search-toggle + .ha-header__burger { margin-left: -8px; }

@media (min-width: 861px) {
  .ha-header__search-toggle,
  .ha-header__burger { display: none; }
}

/* ── The panel: the wide nav row, and the narrow overlay ────────────────── */

/*
 * One element, two jobs — which is the whole of docs/02 §2. Above the split it
 * is the row that holds the nav, the search, the language switch and the
 * Newsletter pill; below it, it is the design's full-screen menu. There is no
 * second copy of any of them.
 */
.ha-header__panel {
  order: 3;
  display: flex;
  flex: 1 1 auto;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 20px;
  min-width: 0;
}

/*
 * The order the four panel children sit in, at both widths. `left-of-menu`
 * moves one of them in front of the nav; `right-of-menu` is the base. In the
 * narrow column the same numbers put the language switch above the Newsletter
 * pill, which is where docs/13 asks for it.
 */
.ha-nav { order: 0; min-width: 0; }
.ha-search { order: 1; }
.ha-lang { order: 2; }
.ha-header__newsletter { order: 3; }

@media (min-width: 861px) {
  .ha-nav { margin-right: auto; }

  .ha-o--header-search-position-left-of-menu,
  .ha-o--header-lang-position-left-of-menu,
  .ha-o--header-newsletter-position-left-of-menu { order: -1; }
}

@media (max-width: 860px) {
  .ha-on--header-search-position-left-of-menu,
  .ha-on--header-lang-position-left-of-menu,
  .ha-on--header-newsletter-position-left-of-menu { order: -1; }
}

/* The overlay's own "Menu / ✕" bar — drawn only where there is an overlay. */
.ha-header__menu-bar {
  display: none;
  order: -2;
  align-items: center;
  justify-content: space-between;
  margin: 0 0 18px;
}

.ha-header__menu-title {
  margin: 0;
  font-size: var(--type-size-card-title);
  font-weight: var(--weight-heading);
  color: var(--ink);
}

.ha-header__menu-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--ha-touch);
  height: var(--ha-touch);
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink);
  font-family: inherit;
  font-size: var(--type-size-card-title);
  cursor: pointer;
}

/* docs/04's icon set is text glyphs; drawing it here keeps it out of the
   accessibility tree, where the button's own label already says what it does. */
.ha-header__menu-close::before { content: "\2715"; }

/* ── The nav, and its animated underline ────────────────────────────────── */

.ha-nav {
  /*
   * The underline's default colour. Set on the nav rather than on the link,
   * so a menu item's own `ha-fg--…` class (its `menu_items.color_token`,
   * milestone 3.15) wins on the element itself whatever the rule order in this
   * file — HA\Render\SiteMenus documents `--teal` as the documented fallback
   * HA\Services\MenuService promises.
   */
  --ha-cat-color: var(--teal);
}

.ha-nav__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 26px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ha-nav__item { position: relative; }

/*
 * An `<a>` in every ordinary case, and a `<button>` for the one item that has
 * rows but no address of its own (app/Views/site/chrome/header.php says which)
 * — so the reset below is what makes the two look alike.
 */
.ha-nav__link {
  position: relative;
  display: inline-block;
  padding: 0 0 6px;
  border: 0;
  background: none;
  color: var(--text-soft);
  font-family: inherit;
  font-size: var(--type-size-meta);
  font-weight: var(--weight-nav);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

/*
 * The persistent state, and why it is a class rather than `[aria-current]`.
 * A parent whose CHILD is the current page is on the reader's trail and wears
 * the underline; it is NOT the current page, and `aria-current="page"` on it
 * would tell a screen-reader user they are somewhere they are not. So the
 * claim and the styling hook are two things: the attribute is on the one link
 * that is the page (app/Views/site/chrome/header.php), and this class is on
 * that link and on the ancestor that leads to it.
 */
.ha-nav__link--current { color: var(--ink); }

/*
 * The underline. docs/04's motion table: "transform: scaleX() from the left,
 * ~.22s ease (handoff). HTML animates `width` over .35s — use the handoff's."
 * The geometry is the HTML's, to its own numbers: a 4px bar, radius 2px,
 * starting 5px left of the label and ending 5px past it, 1px below the box.
 *
 * `::before`, not `::after`, so the dropdown chevron can have `::after` and
 * still follow the label in reading order.
 */
.ha-nav__link::before {
  content: "";
  position: absolute;
  left: -5px;
  bottom: -1px;
  width: calc(100% + 5px);
  height: 4px;
  border-radius: 2px;
  background: var(--ha-cat-color);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .22s ease;
  pointer-events: none;
}

.ha-nav__link:hover::before,
.ha-nav__link:focus-visible::before,
.ha-nav__link--current::before,
.ha-nav__item--group[data-ha-open] > .ha-nav__link::before {
  transform: scaleX(1);
}

/* docs/04: "Arrows, chevrons and controls are text: → ← ↑ ↓ ▾ ▴ ‹ › ✕ ✓ —". */
.ha-nav__link--dropdown::after { content: "\00A0\25BE"; }

/* ── The dropdown panel ─────────────────────────────────────────────────── */

.ha-dropdown {
  display: none;
  position: absolute;
  top: calc(100% + 8px);
  left: -20px;
  z-index: 60;
  width: 420px;
  max-width: calc(100vw - 32px);
  padding: 16px;
  border: var(--ha-hairline);
  border-radius: 18px;
  background: color-mix(in srgb, var(--white) 85%, transparent);
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
  box-shadow: 0 24px 60px -30px color-mix(in srgb, var(--ink) 55%, transparent);
}

.ha-dropdown__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ha-dropdown__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  min-height: var(--ha-touch);
  padding: 9px 12px;
  border-radius: 10px;
  color: var(--ink);
  font-size: var(--type-size-meta);
  font-weight: var(--weight-nav);
  text-decoration: none;
}

.ha-dropdown__row:hover,
.ha-dropdown__row[aria-current] {
  background: color-mix(in srgb, var(--teal) 12%, transparent);
}

/* A long label shrinks rather than pushing the count off the row. */
.ha-dropdown__label {
  min-width: 0;
  overflow-wrap: anywhere;
}

/*
 * milestone 5.10 — the count is meta text, not a caption: `--text-faint`
 * (the palette's "captions" token) falls to 3.7–4.2:1 on this near-white panel
 * across every light palette, under the 4.5:1 a small string needs, so it wears
 * `--text-muted` instead — the adjacent, darker meta token, which clears the
 * floor on all five. The audit's owner note records that `--text-faint` itself
 * is sub-AA as text on light surfaces.
 */
.ha-dropdown__count {
  color: var(--text-muted);
  font-size: var(--type-size-eyebrow);
  white-space: nowrap;
}

.ha-dropdown__all {
  display: block;
  margin: 10px 0 0;
  padding: 12px 0 4px;
  border-top: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
  color: var(--teal-deep);
  font-size: var(--type-size-eyebrow);
  font-weight: var(--weight-label);
  text-align: center;
  text-decoration: none;
}

/*
 * Opening it. Every conditional rule lives above the split, because below it
 * the panel is an expanded list inside the overlay and there is nothing to
 * toggle.
 *
 *   - with no script, hover and `:focus-within` open it — keyboard reachable
 *     with no JavaScript at all;
 *   - once chrome.js has taken over (`data-ha-menu` on the header), those two
 *     stop opening it and `data-ha-open` does, so that the `aria-expanded` the
 *     script maintains is a claim something actually keeps, and so the design's
 *     two-state click (docs/02 §3.2 — "first click opens, second navigates")
 *     is not pre-empted by a panel hover had already opened.
 */
@media (min-width: 861px) {
  .ha-nav__item--group:hover > .ha-dropdown,
  .ha-nav__item--group:focus-within > .ha-dropdown { display: block; }

  .ha-header[data-ha-menu] .ha-nav__item--group:hover > .ha-dropdown,
  .ha-header[data-ha-menu] .ha-nav__item--group:focus-within > .ha-dropdown { display: none; }

  .ha-header[data-ha-menu] .ha-nav__item--group[data-ha-open] > .ha-dropdown { display: block; }
}

/* ── Search ─────────────────────────────────────────────────────────────── */

.ha-search {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.ha-search__input {
  flex: 1 1 auto;
  min-width: 0;
  max-width: 230px;
  min-height: var(--ha-touch);
  padding: 0 16px;
  border: var(--ha-hairline);
  border-radius: var(--ha-pill);
  background: color-mix(in srgb, var(--white) 80%, transparent);
  color: var(--ink);
  font-family: inherit;
  font-size: var(--type-size-meta);
  font-weight: var(--weight-ui);
}

.ha-search__submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-height: var(--ha-touch);
  min-width: var(--ha-touch);
  padding: 0 14px;
  border: 0;
  border-radius: var(--ha-pill);
  background: none;
  color: var(--text-soft);
  font-family: inherit;
  font-size: var(--type-size-meta);
  font-weight: var(--weight-ui);
  cursor: pointer;
}

.ha-search__icon { flex: none; }

/*
 * `header.search.position: icon-only` hides the word and leaves the glyph. The
 * button keeps its accessible name from its own `aria-label`, which no
 * stylesheet can take away — which is why the label is written twice in
 * app/Views/site/chrome/header.php and only one of the two is visible.
 */
@media (min-width: 861px) {
  .ha-o--header-search-position-icon-only .ha-search__label { display: none; }

  /*
   * The design's collapsed pill: icon and label until it is clicked. Only
   * where the script can open it again.
   */
  .ha-header[data-ha-search-state="closed"] .ha-search__input {
    width: 0;
    max-width: 0;
    min-width: 0;
    padding: 0;
    border-width: 0;
    visibility: hidden;
  }

  /*
   * docs/02 §3.2: "The Newsletter button hides when search is open and the
   * viewport is under 1100px."
   */
  @media (max-width: 1100px) {
    .ha-header[data-ha-search-state="open"] .ha-header__newsletter { display: none; }
  }
}

@media (max-width: 860px) {
  .ha-on--header-search-position-icon-only .ha-search__label { display: none; }
}

/* ── The Newsletter pill and the language switch ────────────────────────── */

.ha-header__newsletter {
  display: inline-flex;
  align-items: center;
  min-height: var(--ha-touch);
  padding: 0 20px;
  border-radius: var(--ha-pill);
  background: var(--teal);
  color: var(--paper);
  font-size: var(--type-size-eyebrow);
  font-weight: var(--weight-label);
  text-decoration: none;
}

.ha-header__newsletter:hover { background: var(--teal-deep); }

/* docs/13: "a two-item segmented control SK / EN … Not a flag." */
.ha-lang {
  display: inline-flex;
  align-items: center;
  border: var(--ha-hairline);
  border-radius: var(--ha-pill);
  background: color-mix(in srgb, var(--white) 60%, transparent);
}

/* Each pill is its own 44px target, not half of one — docs/02 §7. */
.ha-lang__pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--ha-touch);
  min-width: var(--ha-touch);
  padding: 0 12px;
  border-radius: var(--ha-pill);
  color: var(--text-soft);
  font-size: var(--type-size-eyebrow);
  font-weight: var(--weight-label);
  letter-spacing: .06em;
  text-decoration: none;
}

.ha-lang__pill[aria-current] {
  background: var(--teal);
  color: var(--paper);
}

/* ── Narrow widths: the overlay ─────────────────────────────────────────── */

@media (max-width: 860px) {
  .ha-header__bar { padding: 10px 16px; gap: 8px 12px; }

  /*
   * The wordmark is NOT rescaled here. `--header-logo-size` is
   * `header.logo.size`, and `layout.css` already carries the Mobile tab's own
   * answer inside this same 860px split — a factor here would multiply the
   * operator's second value by a number they never chose.
   */

  /*
   * With no script the panel is simply the next row: a stacked list under the
   * wordmark. Everything the overlay adds is behind `data-ha-menu`.
   */
  .ha-header__panel {
    flex: 1 0 100%;
    flex-direction: column;
    /*
     * One column, however long. The wide row's `wrap` must not survive into
     * the overlay: a menu taller than the screen (the By problem tag list
     * open, a phone held sideways) wrapped into a SECOND column beside the
     * "Menu ×" bar and ran off the right edge (owner, 2026-09-28). The overlay
     * scrolls instead (`overflow: auto` below).
     */
    flex-wrap: nowrap;
    align-items: stretch;
    gap: 0;
  }

  .ha-nav { margin-right: 0; }

  .ha-nav__list {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
  }

  .ha-nav__link {
    display: block;
    padding: 15px 0;
    border-bottom: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
    font-size: var(--type-size-card-title);
    color: var(--ink);
  }

  /* The bar would sit under the row's own hairline; the label carries it. */
  .ha-nav__link::before { display: none; }

  /* Inside the overlay the dropdown is an expanded list, not a floating panel:
     one DOM, so its rows have to be reachable at this width too. */
  .ha-dropdown {
    display: block;
    position: static;
    width: auto;
    max-width: none;
    padding: 4px 0 8px 16px;
    border: 0;
    border-radius: 0;
    background: none;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    box-shadow: none;
  }

  .ha-dropdown__grid { grid-template-columns: 1fr; }

  .ha-search { padding: 14px 0; }
  .ha-search__input { max-width: none; }

  /*
   * docs/02 §3.2's narrow submit: "input + a **Go** pill". Filled, so it reads
   * as the row's action rather than as a second piece of chrome.
   *
   * TEXT ONLY, which is the shape design/MainSite.dc.html draws at this width:
   * a teal pill carrying a word and no glyph. Round 1 left the magnifier beside
   * the word, which put a second magnifier directly under the bar's magnifier
   * button — two identical glyphs, 44px apart, meaning two different things.
   * The word itself stays Admin-free and Slovak (`site.search.label`); only its
   * shape is the design's.
   */
  .ha-search__submit {
    flex: none;
    background: var(--teal);
    color: var(--paper);
  }

  .ha-search__icon { display: none; }

  /*
   * …unless `header.search.position: icon-only` has taken the word away, and
   * then the glyph is all the pill has left. A button with nothing in it is
   * not a smaller button, it is an invisible one.
   */
  .ha-on--header-search-position-icon-only .ha-search__icon { display: inline; }

  .ha-header__newsletter { justify-content: center; margin: 14px 0 0; }
  .ha-lang { align-self: flex-start; margin: 14px 0 0; }

  /*
   * Once the script is running (`data-ha-menu` on the header), the panel does
   * two jobs at this width, and never both at once:
   *
   *   - with the menu CLOSED it is docs/02 §3.2's "row beneath the header":
   *     the search box, and nothing else, drawn only while the magnifier has
   *     opened it. One form, repositioned — not a second one (docs/02 §2);
   *   - with the menu OPEN it is the design's full-screen overlay, which
   *     carries the nav, the language switch and the Newsletter pill and does
   *     NOT carry the search box, exactly as the prototype's mobile header
   *     draws it.
   *
   * With no script none of these rules apply, the attribute never appears, and
   * the panel is the plain stacked list above — search included, open.
   */
  .ha-header[data-ha-menu] .ha-header__panel { display: none; }

  .ha-header[data-ha-menu="closed"][data-ha-search-state="open"] .ha-header__panel {
    display: flex;
    padding: 0;
  }

  .ha-header[data-ha-menu="closed"] .ha-header__panel > :not(.ha-search) { display: none; }
  .ha-header[data-ha-menu="closed"] .ha-search { padding: 2px 0 10px; }

  /*
   * The overlay is position: fixed, and a backdrop-filter on an ancestor turns
   * that ancestor into the fixed box's containing block (Filter Effects 2) —
   * so with the header's own glass still on, the "full-screen" overlay was
   * the header's 70px, scrolling inside it (owner, v0_2 on the host,
   * 2026-09-16). While the menu is open the overlay covers the header anyway,
   * so its glass is not missed.
   */
  .ha-header[data-ha-menu="open"] {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }

  .ha-header[data-ha-menu="open"] .ha-header__panel {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: 100;
    overflow: auto;
    padding: 12px 24px;
    background: color-mix(in srgb, var(--base-a) 92%, transparent);
    -webkit-backdrop-filter: blur(20px);
    backdrop-filter: blur(20px);
  }

  .ha-header[data-ha-menu="open"] .ha-search { display: none; }
  .ha-header[data-ha-menu="open"] .ha-header__menu-bar { display: flex; }
}

/* ── The footer ─────────────────────────────────────────────────────────── */

/*
 * docs/02 §3.9 asks for a glass footer with the HTML's visual values, and its
 * fill is the Admin's own `footer.section.bg` (`--ink` by default, which is
 * the HTML's band). The glass is the construction around it — the hairline
 * above and the blur behind — so a palette or a Custom-CSS change that makes
 * the fill translucent reads as glass rather than as a flat panel.
 *
 * `--ink` carrying `--paper` is docs/04's own pair, and it is the one that
 * survives Night Practice: both invert together, so the footer stays legible
 * where a fixed dark-on-light would not.
 */
.ha-footer {
  flex: none;
  margin: 44px 0 0;
  border-top: var(--ha-hairline);
  background: var(--footer-section-bg);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  color: var(--paper);
}

/*
 * The owner's single line (docs/02 §3.9): the three flex children — wordmark,
 * the footer nav, the copyright — sit on ONE `space-between` row, which puts the
 * wordmark at the start, the copyright at the end and the links in the middle.
 * `flex-wrap` is the graceful-degradation half of the one fluid build: between
 * the wide line and the narrow stack a child that no longer fits drops to the
 * next row rather than overflowing or clipping. Below 860px it stops being a
 * line at all — see the narrow rule at the foot of this block.
 */
/*
 * The footer newsletter placement — milestone 6.2. The card
 * (`.ha-newsletter`) already carries its own background and padding; this
 * only gives it room above the wordmark/links/copyright row and keeps it
 * inside the same 1240px measure that row uses.
 */
.ha-footer__newsletter {
  max-width: 1240px;
  margin: 0 auto;
  padding: 26px 32px 0;
}

/*
 * Fix round 1 — the footer placement inherits `.ha-newsletter`'s ordinary
 * colour set (`--newsletter-heading-color`, `--text`, `--text-muted`,
 * `--newsletter-button-bg`/`.-color`), which are tuned for the card on the
 * PAGE background. `newsletter.heading.color` and `footer.section.bg` share
 * the same code default (`--ink`), so on an unedited install the heading sits
 * in the same colour as the footer band it is drawn on — invisible under
 * every palette, not only Night Practice — and the reassurance/consent text
 * (`--text`/`--text-muted`, tuned against `--paper`-on-light) and the button
 * (`--ink` on `--ink`) fail the same way for the same reason.
 *
 * The footer already has its own guaranteed-legible pair: `--paper` on
 * `--footer-section-bg`, the one docs/04 calls out as "the pair that
 * survives Night Practice: both invert together" — every other footer string
 * (`.ha-footer__tagline`, `.ha-footer__link`, `.ha-footer__copyright`) is
 * already `--paper` at a dimmed mix. This gives the placement the SAME
 * resolution, scoped to inside `.ha-footer__newsletter` only — the homepage
 * and end-of-article placements are untouched, and an owner's own
 * `newsletter.heading.color` edit still wins everywhere else.
 */
.ha-footer__newsletter .ha-newsletter__heading { color: var(--paper); }

.ha-footer__newsletter .ha-newsletter__text {
  color: color-mix(in srgb, var(--paper) 82%, transparent);
}

.ha-footer__newsletter .ha-newsletter__consent {
  color: color-mix(in srgb, var(--paper) 72%, transparent);
}

.ha-footer__newsletter .ha-newsletter__button {
  background: var(--paper);
  color: var(--footer-section-bg);
}

.ha-footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 14px 22px;
  max-width: 1240px;
  margin: 0 auto;
  padding: 26px 32px;
}

.ha-footer__logo {
  color: inherit;
  font-size: var(--footer-logo-size);
  font-weight: var(--weight-nav);
  text-decoration: none;
}

/*
 * milestone 5.10: the dimmed footer strings ride `--paper` over `--ink`, the
 * pair docs/04 guarantees inverts together — but dimmed too far they fall under
 * 4.5:1 (the tagline at 60% reached 4.22:1 under Night Practice, the copyright
 * at 55% reached 3.63:1). Both are 13px, so they need the full text floor; the
 * dim now stops at 72%, which clears it on all five palettes while staying
 * quieter than the 78% footer links so the hierarchy the design draws survives.
 */
.ha-footer__tagline {
  margin-left: 6px;
  font-size: var(--type-size-eyebrow);
  color: color-mix(in srgb, var(--paper) 72%, transparent);
}

.ha-footer__nav {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
}

.ha-footer__link {
  display: inline-flex;
  align-items: center;
  min-height: var(--ha-touch);
  color: color-mix(in srgb, var(--paper) 78%, transparent);
  font-size: var(--type-size-eyebrow);
  font-weight: var(--weight-nav);
  text-decoration: none;
}

.ha-footer__link:hover,
.ha-footer__link[aria-current] { color: var(--paper); }

.ha-footer__copyright {
  margin: 0;
  color: color-mix(in srgb, var(--paper) 72%, transparent);
  font-size: var(--type-size-eyebrow);
  font-weight: var(--weight-nav);
}

/*
 * milestone 5.10 — the focus ring on the dark footer. `--teal` on `--ink`
 * falls to 2.2:1 under Night Practice and ~2.9:1 on the light palettes, below
 * the 3:1 a focus indicator needs (WCAG 1.4.11) — the exact problem docs/04
 * §Palette records for the Admin's side panel, solved the same way its
 * `--admin-focus-panel` is: the ring is lifted half of the way from `--teal`
 * toward `--paper`, which clears the floor (4.9–5.9:1) on every palette because
 * `--paper` is always the footer's own legible foreground. Every other surface
 * keeps the plain `--teal` ring CLAUDE.md names.
 */
.ha-footer :focus-visible {
  outline-color: color-mix(in srgb, var(--teal) 55%, var(--paper));
}

/*
 * docs/02 §3.9: "At narrow widths it stacks and centres." A wordmark, three
 * Slovak link labels and a copyright sentence cannot share one line on a ~380px
 * phone, so below 860px the single wide line becomes a centred column —
 * wordmark, then the three links, then the copyright — which is the "or stacks"
 * half of the graceful degradation the wide `flex-wrap` begins.
 */
@media (max-width: 860px) {
  .ha-footer__inner {
    flex-direction: column;
    justify-content: center;
    text-align: center;
    padding: 22px 20px;
  }

  .ha-footer__nav { justify-content: center; }
}

/* docs/10-conventions.md: wrap animation in a reduced-motion query. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}
