@layer components {
  /* The fixed application chrome. Scoped to .app-header so the bare <header>
     element stays free for use inside components and dialogs.
     `top` follows the blitz staging banner (0rem when no banner is shown). */
  .app-header {
    position: fixed;
    top: var(--blitz-staging-banner-height, 0rem);
    left: 0;
    width: 100%;
    z-index: 100;
    background-color: var(--rose);
    border-bottom: 1px solid var(--rose-border);
  }

  /* Exact height — driven by --app-header-height (tokens) including the bottom
     border. The body offset (base.css), sidebar top and the note's sticky
     formatting bar (notes.css) all derive from the same variable so they stay
     in sync; a content-driven height rendered a few px taller on iOS and the
     sticky bar tucked underneath it. */
  .app-header > div {
    display: flex;
    align-items: center;
    gap: 1rem;
    box-sizing: border-box;
    height: calc(var(--app-header-height) - 1px);
    padding: 0 1.25rem;
  }

  .header-logo {
    text-decoration: none;
    font-size: 1.5rem;
    display: flex;
    gap: 5px;
    align-items: center;
    & a:hover {
      text-decoration: none;
    }
  }

  /* Brand lock-up (logo + Fredoka wordmark), pinned top-left. While the sidebar
     is open it is width-locked to the sidebar column so it sits above it, with
     the archive card (a sibling) starting at the content column edge. */
  .header-brand-group {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    min-width: 0;
    flex: 0 1 auto;
  }

  .header-brand {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-shrink: 0;
    text-decoration: none;
  }

  .header-brand:hover {
    text-decoration: none;
  }

  .header-brand-logo {
    height: 2rem;
    width: 2rem;
    object-fit: contain;
    display: block;
  }

  .header-brand-name {
    font-family: var(--font-brand);
    font-weight: 600;
    font-size: 1.4rem;
    letter-spacing: 0.03em;
    line-height: 1;
    text-transform: uppercase;
    color: var(--wine);
  }

  /* The archive card, a direct header child. Vertically centred by default (on
     the compact header the card nearly fills its height, so centring keeps even
     spacing). On the taller desktop header it is bottom-aligned instead — see
     the min-width: 900px rule below. While the sidebar is open it is pushed to
     start at the content column's left edge (the brand group ahead of it is
     width-locked to the sidebar column). */
  .header-archive-slot {
    display: flex;
    min-width: 0;
    /* Never shrink: if the slot could shrink below the card's width the card
       would overflow it and overlap the nav that follows. */
    flex: 0 0 auto;
  }

  /* The archive switcher rendered as the wine card from the design: gold book
     icon, "MEINE ARCHIVE" eyebrow above the archive name, caret pushed to the
     far right. Single-line on compact headers; the eyebrow appears on desktop
     (see the min-width: 900px rule) where the header is tall enough. */
  .header-archive {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    width: 100%;
    min-width: 0;
    padding: 0.45rem 0.8rem;
    background: var(--wine);
    color: #fff;
    border-radius: var(--radius-control);
    box-shadow: var(--shadow-raised);
    white-space: nowrap;
    overflow: hidden;
    cursor: pointer;
    --icon-size: 1.4rem;
  }

  /* Book glyph in gold, echoing the design. */
  .header-archive-icon {
    flex-shrink: 0;
    color: var(--gold-bright);
  }

  .header-archive-labels {
    display: flex;
    flex-direction: column;
    justify-content: center;
    line-height: 1.15;
    min-width: 0;
    flex: 1;
  }

  .header-archive-eyebrow {
    display: none; /* shown on desktop where the header is tall enough */
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.72);
  }

  /* Caret pinned to the far right of the card. */
  .header-archive-caret {
    flex-shrink: 0;
    margin-left: auto;
    color: rgba(255, 255, 255, 0.9);
    --icon-size: 1.25rem;
  }

  .header-resource-nav {
    --icon-size: 1.65rem;
    display: flex;
    align-items: center;
    gap: 0.25rem;
    flex-shrink: 0;
    /* Sit right after the archive card (small gap). Bottom alignment (to share
       the card's baseline) is applied only on the taller desktop header — see
       the min-width: 900px rule below. */
    margin-left: 0.75rem;
  }

  /* The search trigger follows the nav in the DOM; hand it the right-align duty
     so the search + avatar cluster hugs the header's right edge while the nav
     stays anchored beside the archive card. */
  .header-resource-nav + div {
    margin-left: auto;
  }

  .header-resource-nav > a,
  .app-header .global-search-trigger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    min-height: 2.2rem;
    padding: 0.3rem 0.5rem;
    border: 1px solid transparent;
    border-radius: 10px;
    color: #7c5560;
    font-size: 0.95rem;
    font-weight: 600;
    line-height: 1;
    text-decoration: none;
    white-space: nowrap;
    background: transparent;
    cursor: pointer;
    font-family: inherit;
    --icon-size: 1.65rem;
    transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
  }

  .header-resource-nav > a:hover,
  .app-header .global-search-trigger:hover {
    background: rgba(255, 255, 255, 0.55);
    color: var(--wine);
    text-decoration: none;
  }

  .header-resource-nav > a.selected {
    background: var(--gold);
    border-color: var(--wine);
    color: var(--wine-deep);
    font-weight: 700;
  }

  .header-resource-nav > a.selected:hover {
    background: var(--gold-hover);
    color: var(--wine-deep);
  }

  .header-resource-text {
    min-width: 0;
  }

  /* The nav-bar search needs no visible label — the magnifier icon says it all.
     Keep the text in the DOM for screen readers (also the button's aria-label). */
  header .global-search-trigger-label {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }

  /* Roomier nav pills in the taller desktop header, matching the design. */
  @media (min-width: 900px) {
    /* The taller desktop header (4.25rem) has room to bottom-align the archive
       card and nav so they sit just above the content title / category column
       that begins right below the header, sharing one baseline. */
    .header-archive-slot,
    .header-resource-nav {
      align-self: flex-end;
      margin-bottom: 0.5rem;
    }

    .header-resource-nav {
      gap: 0.3rem;
    }

    .header-resource-nav > a,
    .app-header .global-search-trigger {
      min-height: 2.5rem;
      padding: 0.5rem 0.9rem;
      font-size: 1rem;
      gap: 0.45rem;
    }
  }

  /* On the desktop header the archive card, the nav, search and avatar no
     longer all fit on a narrow window. Instead of guessing a viewport
     breakpoint, the nav itself is a size container that flexes with the truly
     available space: it degrades in stages (roomy pills → compact pills →
     icon-only) exactly when the space runs out — however long the archive
     name (capped at 20rem, see below) or the current locale's labels are.

     Mechanics: with container-type: inline-size the nav no longer has an
     intrinsic width, so it gets an explicit flex-basis wide enough for the
     longest locale's full-text nav (~38rem for mk/sr) and may shrink from
     there (min-width fits the four icon-only pills). While free space exists
     the pills simply cluster at the box's left edge — indistinguishable from
     the free space that follows. Once the header runs out of room, flex
     shrinks ONLY this box (everything else is flex: 0 0 auto), and the
     container queries below react to its real width.

     The stage thresholds are the rendered full-text nav widths per locale
     (roomy: Σlabels + 4 pills × (2×0.9rem padding + 1.65rem icon + 0.45rem
     gap + 2px border) + 3×0.3rem gaps; compact: same at 0.95rem font,
     0.55rem padding, 1.5rem icon), grouped into three bands and expressed in
     rem so they track root font scaling. Unknown future locales fall into
     the widest (safest) band. When labels change, re-measure. */
  @supports (container-type: inline-size) {
    @media (min-width: 900px) {
      .header-resource-nav {
        container: resource-nav / inline-size;
        flex: 0 1 39rem;
        min-width: 11rem;
      }

      /* ——— Stage: compact pills (smaller font, icon and padding) ——— */

      /* de/en ("Notizen … Fotos" / "Notes … Photos"): full roomy nav ≈ 33rem */
      @container resource-nav (max-width: 33rem) {
        html:where(:lang(de), :lang(en)) .header-resource-nav > a {
          padding: 0.5rem 0.55rem;
          gap: 0.3rem;
          font-size: 0.95rem;
          --icon-size: 1.5rem;
        }
      }

      /* hu/sv/hr/bs (longest: "Bilješke … Fotografije"): roomy nav ≈ 36rem */
      @container resource-nav (max-width: 36rem) {
        html:where(:lang(hu), :lang(sv), :lang(hr), :lang(bs)) .header-resource-nav > a {
          padding: 0.5rem 0.55rem;
          gap: 0.3rem;
          font-size: 0.95rem;
          --icon-size: 1.5rem;
        }
      }

      /* mk/sr and any future locale: roomy nav ≈ 38rem ("Обележувачи") */
      @container resource-nav (max-width: 38.25rem) {
        html:not(:lang(de)):not(:lang(en)):not(:lang(hu)):not(:lang(sv)):not(:lang(hr)):not(:lang(bs)) .header-resource-nav > a {
          padding: 0.5rem 0.55rem;
          gap: 0.3rem;
          font-size: 0.95rem;
          --icon-size: 1.5rem;
        }
      }

      /* ——— Stage: icon-only, when even the compact pills no longer fit ——— */

      /* de/en: compact nav ≈ 28.5rem */
      @container resource-nav (max-width: 28.5rem) {
        html:where(:lang(de), :lang(en)) .header-resource-nav > a {
          width: 2.5rem;
          padding-inline: 0;
          --icon-size: 1.65rem;
        }

        html:where(:lang(de), :lang(en)) .header-resource-text {
          position: absolute;
          inline-size: 1px;
          block-size: 1px;
          margin: -1px;
          padding: 0;
          overflow: hidden;
          clip: rect(0 0 0 0);
          white-space: nowrap;
          border: 0;
        }
      }

      /* hu/sv/hr/bs: compact nav ≈ 31rem */
      @container resource-nav (max-width: 31.25rem) {
        html:where(:lang(hu), :lang(sv), :lang(hr), :lang(bs)) .header-resource-nav > a {
          width: 2.5rem;
          padding-inline: 0;
          --icon-size: 1.65rem;
        }

        html:where(:lang(hu), :lang(sv), :lang(hr), :lang(bs)) .header-resource-text {
          position: absolute;
          inline-size: 1px;
          block-size: 1px;
          margin: -1px;
          padding: 0;
          overflow: hidden;
          clip: rect(0 0 0 0);
          white-space: nowrap;
          border: 0;
        }
      }

      /* mk/sr and any future locale: compact nav ≈ 33rem */
      @container resource-nav (max-width: 33.5rem) {
        html:not(:lang(de)):not(:lang(en)):not(:lang(hu)):not(:lang(sv)):not(:lang(hr)):not(:lang(bs)) .header-resource-nav > a {
          width: 2.5rem;
          padding-inline: 0;
          --icon-size: 1.65rem;
        }

        html:not(:lang(de)):not(:lang(en)):not(:lang(hu)):not(:lang(sv)):not(:lang(hr)):not(:lang(bs)) .header-resource-text {
          position: absolute;
          inline-size: 1px;
          block-size: 1px;
          margin: -1px;
          padding: 0;
          overflow: hidden;
          clip: rect(0 0 0 0);
          white-space: nowrap;
          border: 0;
        }
      }
    }
  }

  /* Browsers without container queries (Safari < 16, Chrome < 105) fall back
     to the old fixed band: icon-only nav on every narrow desktop window. */
  @supports not (container-type: inline-size) {
    @media (min-width: 900px) and (max-width: 1179px) {
      .header-resource-nav > a {
        width: 2.5rem;
        padding-inline: 0;
      }

      .header-resource-text {
        position: absolute;
        inline-size: 1px;
        block-size: 1px;
        margin: -1px;
        padding: 0;
        overflow: hidden;
        clip: rect(0 0 0 0);
        white-space: nowrap;
        border: 0;
      }
    }
  }

  @media (max-width: 760px) {
    .header-resource-nav > a,
    .app-header .global-search-trigger {
      width: 2.2rem;
      padding-inline: 0;
    }

    .header-resource-text {
      position: absolute;
      inline-size: 1px;
      block-size: 1px;
      margin: -1px;
      padding: 0;
      overflow: hidden;
      clip: rect(0 0 0 0);
      white-space: nowrap;
      border: 0;
    }
  }

  /* Below the footer-nav breakpoint, the resource links live in the footer.
     Hide the top nav to make room; the search trigger stays in the header. */
  @media (max-width: 640px) {
    .header-resource-nav {
      display: none;
    }

    /* On phones the logo alone identifies the app; hide the wordmark so longer
       archive names have room in the header. */
    .header-brand-name {
      display: none;
    }

    .app-header > div {
      gap: 0.5rem;
      padding-inline: 0.5rem;
    }

    /* Match the header's tighter horizontal padding so the title, filters and
       cards all share the same left / right edges on phones. */
    .app-main {
      padding-inline: 0.5rem;
    }
  }

  .header-category {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 1.2rem;
    font-weight: bold;
    white-space: nowrap;
    overflow: clip;
    --icon-size: .9rem;
  }

  /* Desktop shows the full "Notizen in …" phrase; mobile shows only the
     bare category name to save space. */
  .header-category-name {
    display: none;
  }

  /* The pinned "Express-…" bucket shows its brand name on every breakpoint,
     followed by its paper-plane icon. */
  .header-category-express {
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .header-category-icon {
    flex-shrink: 0;
    color: var(--wine);
  }

  /* Allowed to shrink (flex: 0 1 auto via the default) so a long archive name
     truncates via the inner .header-archive-text ellipsis instead of pushing
     the search icon and avatar off the right edge. min-width: 0 lets it shrink
     below its content width. */
  .header-archive-group {
    display: flex;
    align-items: center;
    gap: 1rem;
    min-width: 0;
  }

  /* Books icon stays full size; only the archive name shrinks. */
  .header-archive-group > .icon {
    flex-shrink: 0;
  }

  .header-archive-dropdown {
    /* Default placement (top:100%, left:0) so Blitz uses CSS anchor
       positioning — which lets the menu size itself to the card via
       anchor-size() below. The menu drops right beneath the card. */
    --dropdown-menu-top: 100%;
    --dropdown-menu-left: 0;
    flex: 1 1 0;
    min-width: 0;
  }

  /* Override Blitz's .dropdown-toggle width: fit-content so the card can span
     the full width of the group. */
  .header-archive-dropdown .dropdown-toggle {
    width: 100%;
    min-width: 0;
  }

  /* The archive list. overflow: visible lets each row's ⋮ menu escape on
     browsers without the popover top layer; padding gives the rows room. */
  .header-archive-dropdown > .dropdown-menu {
    box-sizing: border-box;
    overflow: visible;
    padding: 0.3rem;
    min-width: 13rem; /* fallback width where anchor-size() is unsupported */
  }

  /* Match the menu to the card width (the toggle is the position-anchor). */
  @supports (width: anchor-size(width)) {
    .header-archive-dropdown > .dropdown-menu {
      width: anchor-size(width);
      min-width: 0;
    }
  }

  /* One archive per row: the switch link fills the row, the ⋮ menu sits right
     and only appears on hover (or while its own menu is open). */
  .header-archive-dropdown .archive-menu-item {
    display: flex;
    align-items: center;
    border-radius: 8px;
  }

  .header-archive-dropdown .archive-menu-item:hover,
  .header-archive-dropdown .archive-menu-item.current {
    background: #f5e9e3;
  }

  .header-archive-dropdown .archive-menu-item-link {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.45rem 0.35rem 0.45rem 0.7rem;
    color: var(--wine);
    text-decoration: none;
    white-space: nowrap;
  }

  .header-archive-dropdown .archive-menu-item-link:hover {
    background: transparent;
    text-decoration: none;
  }

  .header-archive-dropdown .archive-menu-item-name {
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .header-archive-dropdown .archive-menu-item-check {
    flex-shrink: 0;
    color: var(--wine);
    --icon-size: 1rem;
  }

  .header-archive-dropdown .archive-menu-item-actions {
    position: relative;
    flex-shrink: 0;
    margin-right: 0.25rem;
  }

  /* The ⋮ is a <summary>; strip the native disclosure marker. */
  .header-archive-dropdown .archive-menu-item-trigger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.7rem;
    height: 1.7rem;
    border-radius: 6px;
    color: var(--muted-2);
    cursor: pointer;
    list-style: none;
    --icon-size: 1.1rem;
  }

  .header-archive-dropdown .archive-menu-item-trigger::-webkit-details-marker {
    display: none;
  }

  /* Reveal-on-hover only where hover actually exists (desktop). On touch
     (iPhone/iPad) there is no hover: gating the ⋮ behind :hover made the first
     tap on a row register merely as a hover — it revealed the ⋮ and swallowed
     the click, so activating a row took two taps. Keeping the ⋮ permanently
     visible on touch avoids that sticky-hover double-tap. */
  @media (hover: hover) {
    .header-archive-dropdown .archive-menu-item-trigger {
      visibility: hidden;
    }

    .header-archive-dropdown .archive-menu-item:hover .archive-menu-item-trigger,
    .header-archive-dropdown .archive-menu-item-actions[open] .archive-menu-item-trigger {
      visibility: visible;
    }
  }

  .header-archive-dropdown .archive-menu-item-trigger:hover {
    background: rgba(138, 39, 64, 0.1);
    color: var(--wine);
  }

  /* Per-row actions menu, right-aligned under the ⋮. Escapes the archive list
     via the list's overflow: visible. */
  .header-archive-dropdown .archive-menu-item-actions-list {
    position: absolute;
    top: 100%;
    right: 0;
    z-index: 10;
    min-width: 12rem;
    margin-top: 2px;
    padding: 0.25rem;
    background: #fff;
    border: 1px solid #ccc;
    border-radius: 8px;
    box-shadow: 0 10px 20px -6px rgba(58, 22, 32, 0.28);
  }

  .header-archive-dropdown .archive-menu-item-actions-list a {
    display: block;
    padding: 0.4rem 0.7rem;
    border-radius: 6px;
    white-space: nowrap;
    color: var(--ink);
    text-decoration: none;
  }

  .header-archive-dropdown .archive-menu-item-actions-list a:hover {
    background: #f5e9e3;
    color: var(--wine);
    text-decoration: none;
  }

  /* "+ Neues Archiv" action pinned below the list. */
  .header-archive-dropdown .archive-menu-add {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: 0.25rem;
    padding: 0.5rem 0.7rem;
    border-top: 1px solid var(--hairline);
    color: var(--wine);
    font-weight: 600;
    text-decoration: none;
    --icon-size: 1.1rem;
  }

  .header-archive-dropdown .archive-menu-add:hover {
    background: #f5e9e3;
    text-decoration: none;
  }

  .header-archive-text {
    font-size: 1.05rem;
    font-weight: 800;
    letter-spacing: 0.01em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
  }

  /* On desktop the header is tall enough for the two-line card: reveal the
     "Meine Archive" eyebrow above the name. */
  @media (min-width: 900px) {
    /* Cap the card so a long archive name ellipsizes (via the
       .header-archive-text overflow rules) instead of squeezing the nav
       beside it. 20rem keeps the header overflow-free down to the 900px
       sidebar floor even with the icon-only nav: 1.25rem padding + 17.75rem
       brand + 1rem gap + 20rem card + 1.75rem gap + 11rem nav minimum +
       search, avatar and trailing gaps ≈ 63rem ≈ 882px < 900px. */
    .header-archive-slot {
      max-width: 20rem;
    }

    .header-archive {
      /* Match the exact height of the nav pill beside it. That pill is sized by
         its 1.65rem icon plus 0.5rem vertical padding and a 1px border on each
         side — the two label lines here are shorter, so mirror the same box so
         both share a baseline top and bottom. */
      min-height: calc(1.65rem + 1rem + 2px);
      padding: 0.25rem 0.95rem;
      min-width: 13rem;
      --icon-size: 1.6rem;
    }

    .header-archive-eyebrow {
      display: block;
    }

    .header-brand-logo {
      height: 2.4rem;
      width: 2.4rem;
    }

    .header-brand-name {
      font-size: 1.6rem;
    }
  }


  .resource-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    margin-left: 5px;
    margin-bottom: 1rem;
  }

  /* The view / sort / favorites / search controls sit on their own full-width
     row below the category dropdown. flex-basis: 100% forces the wrap. */
  .resource-filters {
    flex: 1 1 100%;
    display: flex;
    align-items: center;
  }

  .header-actions {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-shrink: 0;
  }

  .btn-category {
    background-color: var(--gold);
    color: var(--gold-ink);
    padding: 0.35rem 0.6rem;
    font-size: 0.8rem;
    font-weight: 700;
    white-space: nowrap;
    border: 1px solid var(--wine);
    border-radius: 10px;

    &:hover {
      background-color: var(--gold-hover);
    }
  }

  .btn-reorder {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.35rem 0.6rem;
    font-size: 0.8rem;
    white-space: nowrap;
    border: 1px solid var(--wine);
    background-color: var(--gold);
    color: var(--gold-ink);
    border-radius: 10px;
    --icon-size: 1rem;

    &:hover {
      background-color: var(--gold-hover);
    }
  }

  .btn-reorder .reorder-start,
  .btn-reorder .reorder-done {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
  }

  .btn-reorder .reorder-done {
    display: none;
  }

  [data-image-reorder-active-value="true"] .btn-reorder {
    background-color: #8a2740;
    color: white;
  }

  [data-image-reorder-active-value="true"] .btn-reorder .reorder-start {
    display: none;
  }

  [data-image-reorder-active-value="true"] .btn-reorder .reorder-done {
    display: inline-flex;
  }

  .category-dropdown-container {
    display: flex;
    align-items: center;
    gap: 0.5rem;
  }

  .category-dropdown {
    --dropdown-menu-left: -15px;
  }

  /* With the sidebar open, the full category list already lives in the sidebar,
     so the header's category picker would just duplicate it. Render it as a
     plain, inert title instead: drop the caret and disable the toggle (its slot
     is a non-focusable <div>, so pointer-events: none fully prevents opening).
     Keyed on [data-sidebar] so it reacts live to the client-side toggle.
     Scoped to the sidebar breakpoint (see sidebar.css): below 900px the sidebar
     is force-collapsed regardless of the saved [data-sidebar] state, so the
     header dropdown must stay interactive — it is the only category picker. */
  @media (min-width: 900px) {
    body:has(.app-main[data-sidebar="visible"]) .category-dropdown .dropdown-toggle {
      pointer-events: none;
      cursor: default;
    }

    body:has(.app-main[data-sidebar="visible"]) .category-dropdown .header-category .icon {
      display: none;
    }
  }

  /* Separator between the pinned "Express-…" entry and the real categories in
     the header's category dropdown. */
  .category-dropdown .dropdown-menu .dropdown-divider {
    border: none;
    border-top: 1px solid var(--hairline);
    margin: 4px 0.5rem;
  }

  /* The "Express-…" dropdown entry carries its paper-plane icon to its right. */
  .category-dropdown-link.is-default {
    display: flex;
    align-items: center;
    gap: 0.35rem;
  }

  /* Flex row so the item count can sit at the link's right edge. The name span
     truncates first; the count stays visible. Written as a compound selector
     to out-rank blitz's `.dropdown-menu a { display: block }`. */
  .dropdown-menu a.category-dropdown-link {
    display: flex;
    align-items: center;
  }

  .category-dropdown-link > span:first-child {
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    /* Fade out overlong names instead of "…" — same treatment as
       .app-sidebar-item-label in the sidebar. */
    padding-right: 1.25rem;
    -webkit-mask-image: linear-gradient(to right, black calc(100% - 1.25rem), transparent);
    mask-image: linear-gradient(to right, black calc(100% - 1.25rem), transparent);
  }

  /* Item count in the header's category dropdown — same quiet treatment as
     .app-sidebar-item-count. Ends at the link's 1.5rem right padding, clear of
     the ✓ that .dropdown-menu a.selected paints in that gutter. */
  .category-dropdown-count {
    margin-left: auto;
    padding-left: 0.75rem;
    flex-shrink: 0;
    font-size: 0.8rem;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    color: var(--muted-2);
  }

  .category-dropdown-count.is-zero {
    color: var(--muted-3);
  }

  .category-dropdown-link-icon {
    flex-shrink: 0;
    --icon-size: 1rem;
    color: var(--wine);
  }

  .global-search-dialog {
    max-height: min(80dvh, calc(100dvh - var(--global-search-top) - 2rem));
  }

  .global-search-panel {
    max-height: inherit;
  }

  .header-profile-dropdown {
    --dropdown-menu-left: auto;
    --dropdown-menu-right: -15px;
    --icon-size: 2rem;
    /* margin-left:auto now lives on .header-resource-nav so the whole nav
       cluster is right-aligned; the avatar just trails the search trigger. */
  }

  .header-profile-dropdown a {
    --icon-size: 1.5rem;
    display: flex;
    gap: .5rem;
  }

  /* Wine avatar with a wine ring, echoing the mockup. The blitz avatar sets a
     per-user palette colour via an inline style, so !important is required to
     override it. */
  .header-profile-dropdown .blitz-avatar {
    background: var(--wine) !important;
    box-shadow: 0 0 0 2px var(--rose), 0 0 0 3px var(--wine);
  }

  @media (max-width: 760px) {
    .resource-header {
      align-items: flex-start;
      flex-wrap: wrap;
      gap: 0.35rem;
      /* Drop the desktop 5px indent so the title row lines up flush with the
         cards below it. */
      margin-left: 0;
      margin-bottom: 0.45rem;
    }

    /* Keep the stacking order: dropdown, then filters, then action buttons. */
    .resource-filters {
      order: 2;
    }

    .header-actions {
      order: 3;
    }

    .category-dropdown-container {
      flex: 1 1 100%;
      min-width: 0;
      flex-wrap: wrap;
      gap: 0.25rem;
    }

    /* Let the title dropdown take all the row's free space so the name uses
       the full available width and the actions dropdown sits at the right
       edge (mirrors the .header-archive-dropdown pattern). */
    .category-dropdown {
      flex: 1 1 0;
      min-width: 0;
      --dropdown-menu-left: 0;
      --dropdown-menu-right: auto;
      --dropdown-menu-max-height: calc(100dvh - 9rem);
    }

    .category-dropdown .dropdown-toggle {
      width: 100%;
      min-width: 0;
    }

    .category-actions-dropdown {
      flex-shrink: 0;
    }

    .category-dropdown .dropdown-menu {
      width: calc(100vw - 2rem);
      max-width: calc(100vw - 2rem);
    }

    .category-dropdown .dropdown-filter {
      min-width: 0;
    }

    .category-dropdown .dropdown-menu a {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    /* No hard cap — the flex chain above constrains the width, so the name
       only truncates when it genuinely runs out of room next to the actions
       button (the caret stays pinned right after the name). */
    .header-category {
      max-width: none;
      min-width: 0;
      overflow: hidden;
      text-overflow: ellipsis;
    }

    .header-category-text {
      display: none;
    }

    .header-category-name {
      display: block;
      min-width: 0;
      overflow: hidden;
      text-overflow: ellipsis;
    }
  }

  /* Below the footer-nav breakpoint, the footer "+Neu" dropdown takes over
     all resource-creation actions, so the resource-specific +Notiz /
     +Kategorie / +Foto buttons in the header are redundant. Hide them to
     give the view / sort / search row the full width. The reorder button
     (only present on the images grid) stays visible. */
  @media (max-width: 640px) {
    .header-actions .btn-primary,
    .header-actions .btn-category {
      display: none;
    }

    .global-search-dialog {
      width: calc(100vw - 1rem);
      max-height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px) - 1rem);
      margin-block-start: calc(env(safe-area-inset-top, 0px) + 0.5rem);
    }

    .global-search-panel {
      max-height: inherit;
    }

    .global-search-results {
      min-height: 0;
    }
  }
}
