/* =========================================================================
   Capitalist Watch - the collapsing header menu.

   ONE copy, loaded by index.html, about.html and resources.html. It is a
   separate file rather than a fourth, fifth and sixth copy pasted into the
   three inline <style> blocks because those blocks have already drifted:
   135 lines are byte-identical in all three, and 66 lines in about.html and
   34 in resources.html have quietly diverged from index.html. A menu that is
   right on two pages and subtly wrong on the third is the failure mode that
   gets found weeks later. One file fails loudly instead.

   It is linked AFTER each page's inline <style>, so where a selector here has
   the same specificity as one in there - .nav-link, .header-inner - this file
   wins. Nothing here needs !important.

   Everything in this file is either inside a narrow-viewport query, inside
   @media (pointer: coarse), or a no-op on a desktop: the only base-level
   rules are the look of a button that is display:none until the panel tier
   turns it on, and one override of the `hidden` attribute that restores
   exactly the behaviour the pages have today. Verified rather than asserted -
   every element rect, colour, font size, padding, margin, border and display
   on all three pages at 1920x1080 with a mouse, 2,580 of them, is identical
   to the version before this file existed. The eight new elements are the
   button and its two icons, which generate no boxes there.
   ========================================================================= */


/* --- The menu button ------------------------------------------------------
   Deliberately the .nav-link idiom - 1px solid var(--border) on var(--panel-2),
   0.5rem radius, var(--text) - so it reads as one of the site's own controls
   and not as a transplant. The one thing it does NOT borrow is the nav link's
   hover colour: .nav-link uses var(--surface-strong) for both :hover and
   .active, so an open menu needed a third value. --hover-bg is already defined
   in both themes and was not being used by the nav, so the open state uses it.
   It is a tint and not the signal - in light mode it is rgba(37,99,235,0.08),
   which is close to nothing over #f8fafc. What actually says "open" is the
   icon swapping to an X, which is why that swap is driven by aria-expanded
   below and not by anything that can disagree with it.

   display: none here is what keeps the desktop header pixel-identical: an
   element with display: none generates no box and is not a flex item, so
   .header-inner's space-between and 0.75rem gap see exactly the two children
   they see today. Only the panel tier and the landscape tier turn it on. */
.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 2.75rem;   /* 44px */
  height: 2.75rem;  /* 44px */
  flex: 0 0 auto;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  background: var(--panel-2);
  color: var(--text);
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
}

/* :hover sits behind @media (hover: hover) because iOS applies :hover on tap
   and then leaves it applied until you tap somewhere else - without the guard
   the menu button would stay lit for the rest of the visit. :focus-visible is
   deliberately NOT behind the guard; keyboard focus has to work everywhere.
   (The rest of this site has no hover guard anywhere - the menu is not going
   to repeat that.) */
@media (hover: hover) {
  .nav-toggle:hover { background: var(--surface-strong); }
}

/* The site has no focus styling of its own and leans on the UA ring. That is
   left alone for the existing links - restyling them would be a visible change
   on a desktop this job is not allowed to touch - but this button is new and
   gets an explicit ring. --accent measures 7.17:1 against the dark header and
   6.47:1 against the light one, so it is the one token that works in both.
   outline-offset puts the ring on the header behind the button rather than on
   the button itself, which is what keeps it readable on a filled control, and
   no border-radius is set here because an outline already follows the
   element's own radius.

   For the record, since the button borrows the site's border: --border is
   2.35:1 against the dark header and 1.48:1 against the light one, under the
   3:1 that a control boundary wants. It is not raised here because every
   .nav-link on the site has exactly that border and a menu button with a
   stronger one would read as a different kind of control. What identifies the
   button is the icon inside it, at 16.1:1 in dark and 20.1:1 in light. */
.nav-toggle:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

/* The icon is drawn from aria-expanded, which nav.js is already keeping
   correct, so there is no second copy of the state to drift out of step with
   the first. No is-open class, no JS icon swapping. */
.nav-toggle svg { width: 1.4rem; height: 1.4rem; display: block; }
.nav-toggle .icon-close { display: none; }
.nav-toggle[aria-expanded="true"] .icon-menu { display: none; }
.nav-toggle[aria-expanded="true"] .icon-close { display: block; }
.nav-toggle[aria-expanded="true"] { background: var(--hover-bg); }

/* --- `hidden` on the nav --------------------------------------------------
   nav.js sets the `hidden` attribute on the nav at every width, including a
   desktop. `hidden` works only through a UA rule setting display: none, so
   this author rule outranks it and the nav stays visible wherever the button
   that would reopen it is not on screen. No resize listener is involved: widen
   the window from a phone and the nav is back, for screen readers too, because
   assistive tech computes hidden-ness from the used display and not from the
   attribute.

   This is a BASE rule rather than a min-width one on purpose. The obvious
   shape is a complementary pair - max-width: 713px hides, min-width: 714px
   shows - but a viewport can be 713.5px wide after a browser zoom, and that
   width matches neither query: the nav would be display: none with no button
   anywhere to bring it back. Turning the override on everywhere and switching
   it off inside the two tiers that actually draw a button cannot produce that
   state at any width. */
.site-nav[hidden] { display: flex; }


/* --- The panel tier -------------------------------------------------------
   713px, not 640px and not the 760px the pages already use for container
   padding. Measured in Edge at DPR 1, not guessed:

     714px   header  92.2px   title cluster and all five links on one row
     713px   header 142.2px   the nav wraps to its own row
     460px   header 188.2px   the nav itself wraps onto two rows
     390px   header 188.2px   22.3% of the viewport, sticky, every page

   So 713px is the first width at which the header stops being one row, which
   is the width at which a panel starts being worth it. Collapsing earlier
   would take the nav away from an iPad held upright (768px) that is showing it
   perfectly well on one 92.2px row; collapsing later would be collapsing a bar
   that had not broken yet.

   THE ARITHMETIC, at 390x844:
     before   188.2px = 16px padding + 59.2px title cluster + 12px row gap
                        + 84px two-row nav + 16px padding + 1px border
     after     74.6px = 7.2px padding + 59.2px title cluster
                        + 7.2px padding + 1px border
   The bar is the title cluster now, not the button: the cluster is 59.2px
   (a 36px h1 over a 20px report count with a 3.2px gap) and the button is
   44px, so the cluster sets the height and the button has 7.6px of air above
   and below it before the padding even starts. That is why the padding can
   come down from 1rem to 0.45rem without crowding anything.

   188.2px -> 74.6px is 113.6px back, 13.5% of a 390px viewport, on every page
   and on every scroll. */
@media (max-width: 713px) {
  /* padding-BLOCK, not the `padding: 0.45rem 0` shorthand that first stood
     here. This element is <div class="container header-inner">, and .container
     sets its side padding as longhands - 1.15rem inside the page's own 760px
     query. A shorthand from this file, which loads after the inline <style>,
     resets those longhands to 0 and puts the whole header hard against both
     edges of a phone. Measured: 18.4px of side padding became 0, the wordmark
     started at x=0 and the menu button ended at x=390. padding-block touches
     only the two sides this rule is actually about. */
  .header-inner {
    flex-wrap: wrap;
    gap: 0.6rem;
    padding-block: 0.45rem;
  }

  /* EVERY rule that turns the nav into a panel is scoped to .has-js-nav, which
     nav.js puts on <html> while the parser is still in the <head>. That single
     scope does three jobs at once:

       - the nav can be authored WITH the hidden attribute, so a phone never
         paints the expanded 188px header and then snaps it shut;
       - with JavaScript off the class never arrives, the base override above
         wins, and the page renders the nav exactly as it does today - a
         wrapped two-row list - instead of a column that nothing can collapse;
       - the menu button stays display:none in that case too, so there is never
         a button on screen that cannot do anything.

     It also takes source order out of it: 0-3-0 against the base rule's 0-2-0,
     so specificity settles which one wins. */

  /* No `order` anywhere in this file. The markup is title -> button -> nav,
     which is already the order the panel needs: the button comes before the
     panel it opens, so Tab walks from the button INTO the menu instead of
     stepping over it and landing in the page. The earlier arrangement - nav
     before the button in the markup, pushed after it with order - looks
     identical and silently skips every link the button just revealed. */
  .has-js-nav .site-nav {
    width: 100%;
    flex-direction: column;
    gap: 0.4rem;
    padding-bottom: 0.3rem;
  }
  .has-js-nav .site-nav[hidden] { display: none; }

  .has-js-nav .nav-toggle { display: inline-flex; }

  /* Full-width rows, and 44px of height each. 2.75rem is 44px. This is inside
     the query rather than at the base because .nav-link is a bordered pill
     whose 38px height is part of the desktop header's look, and the desktop
     header is not allowed to move. @media (pointer: coarse) below covers the
     touch device that is WIDER than this query - a tablet, a phone on its
     side - so the only case left at 38px is a mouse, which does not need 44. */
  .has-js-nav .site-nav .nav-link {
    display: flex;
    align-items: center;
    min-height: 2.75rem;
    width: 100%;
    padding: 0.5rem 0.75rem;
  }

  /* The open panel is 250.4px - five 44px rows, four 0.4rem gaps and the
     0.3rem of bottom padding - which puts the open header at 334.5px. That
     fits an 844px screen and a 640px one. On anything shorter it would run off
     the bottom with no way to reach About, because the panel GROWS THE HEADER
     rather than scrolling. So cap it just under the viewport and let it scroll
     itself; the 75px is exactly the collapsed bar measured above.
     overscroll-behavior stops that scroll chaining on into the page.

     The 75px only describes the space below the bar if the bar is at the top
     of the viewport, and on this site it usually is not - the disclaimer and
     the banner sit above the header. nav.js scrolls the header up to meet this
     assumption, and only when the panel would otherwise overflow; see
     bringHeaderIntoView there for the measurements that made it necessary. */
  .has-js-nav .site-nav {
    max-height: calc(100vh - 75px);
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  /* The report count is min-width: 30ch and nowrap, which is 238px at 390px
     wide. The button needs 44 of the remaining 115. Nothing overflows, but the
     cluster must be allowed to shrink before the button does. */
  .site-title-wrap { min-width: 0; flex: 1 1 auto; }
}

/* The 30ch floor on the report count is what decides whether the button fits
   beside the title at all, and below 328px it does not: 238px of report count
   plus a 9.6px gap plus the 44px button needs 291.6px, and 328px of viewport
   leaves 291.2px inside the container gutters. Measured - 329px keeps the bar
   at 74.56px, 328px wraps the button onto a row of its own and the bar becomes
   128.16px.

   So let the count give up its floor, but only here, and only on the two or
   three px where it is the difference between one row and two. 30ch exists to
   stop the bar jumping when "Loading reports and counting..." is replaced by
   a short number, and that is worth keeping at every width where it is not
   costing the menu a whole row. What gets clipped at 320px is the tail of the
   loading placeholder; the number that replaces it is far shorter than the
   space left.

   340px and not 328px: the floor has to be gone BEFORE the width where it
   starts costing a row, or the two rules trade places for a few pixels. Above
   340px nothing on this site is narrow enough for it to matter. */
@media (max-width: 340px) {
  .has-js-nav .report-count {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}


/* --- Touch targets --------------------------------------------------------
   pointer: coarse, not another width breakpoint. A phone held on its side is
   844px wide and clears the panel tier entirely, and a mouse in a 500px window
   does not need 44px of anything. This is the one query that means a finger is
   doing the pointing.

   The known gap, stated rather than hidden: a touchscreen laptop reports
   pointer: fine while still being tapped, so it keeps the 38px links. The
   reference site fixed that by putting the 44px floor at the base, which costs
   the desktop header 6px - and this job's hard requirement is that the desktop
   header does not move at all. Coarse-only is the price of that. */
@media (pointer: coarse) {
  .nav-link {
    display: inline-flex;
    align-items: center;
    min-height: 2.75rem;   /* 44px, up from 38px */
  }

  /* The wordmark is the link home and is a 36px target. HIT AREA, not box
     height: the <a> is inline, and vertical padding on an inline box does not
     push the lines around it apart - but it does enlarge what the browser
     hit-tests, which is the whole point. The h1 stays exactly 36px tall and the
     title cluster stays 59.2px, so the 74.6px bar measured above still holds on
     a touch device.

     0.35rem, not the 0.25rem that a symmetric 4px + 36px + 4px would suggest,
     because the padding BELOW is not all reachable: the report count is the
     next block in the cluster and paints over whatever the h1 overflows into
     it, so only the gap between the two is hit-testable.

     Probed with elementFromPoint at 0.05px steps rather than derived, which is
     the only reason the number is right - the arithmetic says 44px at 0.25rem
     and the browser says 43.2px:

       0.25rem  ->  43.2px     0.3rem  ->  43.85px     0.35rem  ->  44.7px

     The first two look like a pass in a screenshot and neither is one. */
  .site-title-link {
    padding-block: 0.35rem;
  }
}


/* --- Landscape phones -----------------------------------------------------
   max-height, because on a phone on its side the short dimension is the
   problem and the wide one is the disguise. A 390px-tall landscape phone is
   844px wide, which clears the 713px panel tier and gets the full desktop nav
   back - a 92.2px sticky bar under a banner that has grown to 212px, because
   .hero-banner is clamp(8rem, 25vw, 18rem) and 25vw of 844 is 211. Together
   that is 350px of chrome above a 390px viewport before a single report.

   So this tier does two things. It brings the panel back at a width the panel
   tier cannot see - which is why the nav rules below repeat the panel tier's
   rather than being redundant; an iPhone 15 Pro Max on its side is 932x430 and
   clears 713px by a long way. And it UN-STICKS the header, because a sticky
   bar on a screen this short is eating the only thing the reader came for.
   Reaching the menu button then costs a scroll back to the top, which is a
   fair trade for the 73px it stops taking from a 390px screen.

   Measured at 844x390: the bar goes from 92.19px and sticky to 72.97px and
   static. It is 72.97 and not 74.56 because this tier uses 0.4rem of block
   padding rather than 0.45rem.

   Last in the file on purpose, though nothing here now depends on that: the
   [hidden] rules are scoped by .has-js-nav and outrank the base override on
   specificity alone. */
@media (max-height: 480px) and (orientation: landscape) {
  header.site-header { position: static; }

  /* padding-block for the same reason as the panel tier: a padding shorthand
     here would wipe .container's side padding longhands. */
  .header-inner {
    flex-wrap: wrap;
    gap: 0.5rem;
    padding-block: 0.4rem;
  }

  /* Scoped to .has-js-nav for the same three reasons as the panel tier above.
     Un-sticking the header, by contrast, is right with or without JavaScript,
     so that rule is not scoped. */
  .has-js-nav .nav-toggle { display: inline-flex; }

  .has-js-nav .site-nav {
    width: 100%;
    flex-direction: column;
    gap: 0.4rem;
    padding-bottom: 0.3rem;
    max-height: 70vh;
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  /* A landscape phone can be wider than the panel tier, so without this the
     base override further up would hold the panel permanently open here. */
  .has-js-nav .site-nav[hidden] { display: none; }

  .has-js-nav .site-nav .nav-link {
    display: flex;
    align-items: center;
    min-height: 2.75rem;
    width: 100%;
    padding: 0.5rem 0.75rem;
  }

  .site-title-wrap { min-width: 0; flex: 1 1 auto; }
}
