/*
Theme Name:   Millburn Green
Theme URI:    https://millburngreen.org
Description:  Millburn Green. A classic theme -- PHP templates, real nav menus,
              and no Site Editor. Design tokens, typography and CSS foundation
              from the Web & Brand Style Guide v1.0, all defined in files:
              style.css, styleguide-style.css, functions.php, theme.json.
Author:       Net Theory
Version:      1.0.0
Text Domain:  millburn-green
*/

/* ==========================================================================
   THIS FILE IS THE ADDITIONS ONLY.
   --------------------------------------------------------------------------
   The tokens, base reset, heading scale, containers, header, buttons, cards,
   metrics, map and breakpoints all live in styleguide-style.css, which is §21
   of the style guide verbatim and is enqueued ahead of this file.

   Keeping it untouched means a style guide revision can be dropped in whole,
   and any difference between the guide and the site is a real difference
   rather than a transcription error. Everything below is something §21 does
   not cover.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Fonts -- self-hosted, not Google Fonts

   One less third-party dependency at handoff, no visitor IPs sent to Google
   (this is a township-adjacent site), and faster on a cold cache than a second
   DNS lookup plus TLS handshake.

   Both families are VARIABLE: one file covers Montserrat 300-700, so the five
   weights the guide asks for are one download, not five. latin and latin-ext
   are separate faces with unicode-range, so a reader of English never fetches
   latin-ext.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("assets/fonts/montserrat-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("assets/fonts/montserrat-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("assets/fonts/newsreader-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("assets/fonts/newsreader-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --------------------------------------------------------------------------
   Additions
   -------------------------------------------------------------------------- */

:root {
  /* §12 gives the dashboard its own ground; §21 has no token for it. */
  --dashboard-bg: #F2F4F0;

  /* THE GREEN THAT CARRIES WHITE TEXT -- the event and meeting tags.
     --green reads 3.28:1 under white, short of the 4.5 an 11px label needs;
     --green-deep passes at 9.34 but is nearly black and reads as a different
     colour rather than a darker one. This is --green taken down until white
     clears the bar, at 4.85:1. */
  --green-solid: #527C46;

  /* THE ORANGE THAT CARRIES WHITE TEXT -- the calendar tag.
     Same bar as --green-solid and found the same way: white on an 11px
     uppercase label needs 4.5:1, and every orange bright enough to look like
     one fails it. #E8792B reads 2.92, #CC6415 reads 3.89. This is the most
     saturated orange that clears the bar, at 4.80 -- within a whisker of the
     event green's 4.85, so the three tags sit at the same weight beside each
     other rather than one looking heavier than the rest.
     NOT --energy (#C69A45), which is the charts' gold and reads 2.31. */
  --orange-solid: #B8560E;

  /* THE PRIMARY BUTTON.
     KNOWN CONTRAST MISS, chosen deliberately and recorded here rather than
     discovered later. White on this is 3.76:1 against the 4.5 that WCAG AA
     asks for a 14px label. It is not a near miss that a nudge fixes either:
     this green sits in the middle of the range, too dark for dark text
     (--green-deep is 2.48, --ink 3.58) and too light for white, so no label
     colour clears the bar on it.
     #627F4B -- the same hue at 90% of the lightness, near enough
     indistinguishable -- takes white to 4.51:1 if this ever needs to pass.
     The hover already does, at 4.70.
     Its own token rather than sharing the event tags' green, which is a
     different colour now; one variable for both would make the next change to
     either silently change both. */
  --btn-primary-bg: #6D8D53;
  --btn-primary-bg-hover: #5F7C49;   /* white 4.70:1 */
}

/* THE FOOTER SITS ON THE BOTTOM OF THE WINDOW WHEN THE PAGE IS SHORT.
   Not position:fixed -- that would pin it over the content on every page and
   take a band of every screen. A column flex body with the content growing
   does it: on a short page <main> stretches and pushes the footer down, and on
   a long one it is simply the last thing, scrolled to as always.

   dvh after vh, so a phone with a retracting address bar measures the window
   it actually has rather than the one it has while the bar is hidden; the vh
   line stands where dvh is not understood. */
body {
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}
/* Grows to fill, never shrinks below its content. `auto` and not 0 for the
   basis: a page taller than the window must keep its own height rather than
   be squeezed into one screen. */
#mg-content { flex: 1 0 auto; }
/* Belt and braces for a page that somehow renders without <main>: the footer
   still goes to the bottom rather than floating mid-screen. */
.site-footer { margin-top: auto; }

/* §11 uses an uppercase eyebrow in several places but defines no class. */
.eyebrow {
  font: 600 12px/1.2 var(--font-ui);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Page heading and its standfirst, as one unit.
   ---------------------------------------------------------------------------
   Alignment is set ONCE here and inherited by both, so they cannot drift apart.
   Add .is-centered to centre the pair; nothing else needs changing.

   The margin-inline: 0 is load-bearing. WordPress's constrained layout gives
   every child auto inline margins, so the moment .subtitle gained a max-width
   it floated to the centre of the column while its text stayed left-aligned --
   which is why it looked centred under a left-aligned H1. */
.page-head { text-align: left; }

/* Standfirst. Body size (16px, §3) rather than the Intro step, so it supports
   the heading instead of competing with it. */
.subtitle {
  margin-top: 10px;
  margin-inline: 0;
  max-width: 62ch;                 /* a measure, so it does not run the full 1360px */
  font: 400 16px/1.6 var(--font-ui);
  color: var(--muted);
}

.page-head.is-centered { text-align: center; }
.page-head.is-centered .subtitle { margin-inline: auto; }

/* §17 defines the resting state of the buttons but no interaction. */
.button { cursor: pointer; transition: background-color .15s ease, color .15s ease; }
/* §17 puts .button-primary on --green-deep with white on it. Only the
   background is overridden; the white label is the styleguide's own and stays.
   See the token for what that costs in contrast. */
.button-primary { background: var(--btn-primary-bg); }
.button-primary:hover { background: var(--btn-primary-bg-hover); }
.button-secondary:hover { background: rgba(23, 79, 72, .06); }

/* Numbers in a column read wrong without aligned figures. §3 sets the size of
   the dashboard number but not its figures. */
.metric-value { font-variant-numeric: tabular-nums; }

/* Twenty Twenty-Five sets its own root padding and background through
   theme.json; both need to yield to the guide's containers and Warm Cream. */
body .wp-site-blocks { padding-inline: 0; }

/* Skip link, for keyboard users. Not a design question, so not in the guide. */
.mg-skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 1000;
  padding: 12px 18px;
  background: var(--green-deep);
  color: var(--white);
  font: 600 14px/1 var(--font-ui);
}
.mg-skip-link:focus { left: 0; }

/* --------------------------------------------------------------------------
   Header and footer -- §9, §19

   §21 styles .site-header, .nav and .nav-links; everything below is the parts
   the guide describes in prose but does not provide CSS for.
   -------------------------------------------------------------------------- */

/* The lockup is sized by HEIGHT, not width. The header is a fixed 88px (§9)
   and collapses to 72px when stuck, so height is the constraint; width follows
   from the artwork's 3.34:1 and lands at ~194px, near the ~210px §9 asks for.
   Sizing by width instead would let a future lockup with a different aspect
   ratio silently overflow the header. */
/* One number drives the lockup's height, the separator's height and the
   strapline's leading, so the three cannot fall out of step. */
.site-header { --mark-h: 58px; }
.mg-logo { display: inline-flex; align-items: center; text-decoration: none; }
.mg-logo img { height: var(--mark-h, 58px); width: auto; display: block; }

/* Lockup, hairline, strapline. */
.mg-brand { display: flex; align-items: center; }
.mg-strapline {
  margin: 0 0 0 20px;
  padding-left: 20px;
  /* --green at half strength: a muted green that is still part of the palette
     rather than a fourth grey. The rule is this element's own left border, so
     its height IS the strapline's height -- three lines that total the mark's
     height, which is why it lines up with the lockup exactly and keeps doing
     so if --mark-h changes. */
  border-left: 2px solid rgba(103, 155, 88, .5);
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 400;
  line-height: calc(var(--mark-h, 58px) / 3);
  color: var(--muted);
}

/* The sticky header must clear the WordPress admin bar, which only exists for
   logged-in editors -- so this breaks for exactly the people who maintain the
   site and for nobody else, which is why it is easy to miss. The bar is 32px
   above 782px and 46px below it. */
.site-header { position: sticky; top: 0; z-index: 100; }
.admin-bar .site-header { top: 32px; }
@media (max-width: 782px) { .admin-bar .site-header { top: 46px; } }

/* §9: the sticky variant collapses to 72px.
   NOTHING APPLIES .is-stuck. These two lines have been here since the header
   was built and have never fired -- there is no JavaScript in the theme that
   adds the class. Left as they are rather than quietly switched on: whether
   the header should shrink on scroll is a design decision, not a side effect
   of wanting a shadow. */
.site-header.is-stuck { height: 72px; }
.site-header.is-stuck { --mark-h: 46px; }   /* the strapline follows */

/* Separation once the content is passing underneath.
   The header is cream on a cream page, so at rest its 1px border is the only
   thing marking it and that is enough -- there is nothing behind it yet. Once
   the page scrolls there is, and a border alone reads as a line drawn on the
   content rather than as an edge the content is going under.
   Off at the top, so it arrives as the page moves rather than sitting there
   claiming a depth the header does not have when it is part of the page. */
.site-header { transition: box-shadow .18s ease-out; }
.site-header.is-scrolled { box-shadow: 0 5px 8px -3px rgba(38, 49, 44, .18); }
@media (prefers-reduced-motion: reduce) {
  .site-header { transition: none; }
}

/* Today's weather in the header -- homepage only ------------------------- */
/* Three short lines on the right of the header: what it is now beside the sky
   icon, the day's range, and the sky in words. Right-aligned so the numbers
   sit against the edge rather than against whatever length the condition
   happens to be. */
.mg-hw {
  margin-left: auto;
  text-align: right;
  line-height: 1.25;
}
.mg-hw p { margin: 0; }
/* The block is one link. display:block so the three lines stay stacked inside
   it, and the colours are the ones the lines already had -- a link that
   recoloured them would announce itself as a link and the header does not need
   a second blue thing in it. The hover is the whole block lifting slightly,
   which is what says it is clickable. */
.mg-hw-link {
  display: block;
  color: inherit;
  text-decoration: none;
  transition: opacity .15s ease;
}
.mg-hw-link:hover { opacity: .75; }
/* Visible focus, because this is a link with no underline and no colour of its
   own: without it a keyboard user has nothing at all to go on. */
.mg-hw-link:focus-visible {
  outline: 2px solid var(--green-deep);
  outline-offset: 3px;
  border-radius: 4px;
}
.mg-hw-now {
  font: 400 22px/1 var(--font-editorial);
  color: var(--green-deep);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
}
/* The icon is drawn for a 38px forecast column; here it sits beside a 22px
   numeral, so it takes the inline treatment at a size to match. */
.mg-hw .mg-wx.is-inline { width: 22px; height: 22px; margin: 0; vertical-align: 0; }
.mg-hw-range { font: 600 11px/1.4 var(--font-ui); letter-spacing: .04em; color: var(--muted); }
.mg-hw-sky   { font: 400 12px/1.3 var(--font-editorial); color: var(--muted); }

/* A phone has room for the strapline or the weather, not both. The strapline
   says the same thing on every page; the weather is the one line here that
   changes, so it is the one worth the space. Homepage only -- .has-weather is
   set in header.php and is not on any other page. */
@media (max-width: 640px) {
  .site-header.has-weather .mg-strapline { display: none; }
  .mg-hw-now { font-size: 19px; }
  .mg-hw .mg-wx.is-inline { width: 19px; height: 19px; }
  .mg-hw-range { font-size: 10px; }
  .mg-hw-sky { font-size: 11px; }
}

/* Footer -- §19 ------------------------------------------------------------ */
/* The band, at last. .site-footer a has been set to --cream since the header
   work, which only ever made sense on a dark ground -- there was no ground, so
   the rule sat there describing a footer that did not exist yet. */
.site-footer {
  padding-block: 30px;
  background: var(--green-deep);
  color: var(--white);
}
.site-footer a { color: var(--cream); text-decoration: none; opacity: .88; }
.site-footer a:hover { opacity: 1; text-decoration: underline; }
.mg-footer-legal {
  /* No top rule, margin or padding any more: this is the whole footer, not a
     strip sitting under the brand and the link columns, so the divider had
     nothing left above it to divide from. */
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 16px 48px;
  justify-content: space-between;
  /* centre, not end: the lockup is 20px and the copyright a line of 13px
     text, so aligning their baselines left the mark sitting low. */
  align-items: center;
}

/* Wordmark, a hairline, then the tagline -- one line on the left. */
.mg-footer-brand { display: flex; align-items: center; gap: 12px; }
.mg-footer-brand .mg-logo img { height: 24px; width: auto; }
/* The rule is a border on the tagline rather than an element of its own: it is
   decoration, and a screen reader should not have to read a pipe character. */
.mg-footer-tagline {
  padding-left: 12px;
  /* Both of these were mixed for a cream ground: a dark hairline and --muted
     text. On deep green the rule vanished and the tagline came within 1.5:1 of
     its background. White at low alpha instead, which keeps them quiet without
     making them invisible. */
  border-left: 1px solid rgba(255, 255, 255, .28);
  font: 400 13px/1.4 var(--font-editorial, Georgia, serif);
  color: rgba(255, 255, 255, .82);
}
.mg-copyright { margin: 0; font-size: 13px; opacity: .68; }

/* The right-hand group: the mark, then the copyright.
   Three items under space-between would have put the mark in the middle of
   the band, between the lockup and the copyright, belonging to neither. */
.mg-footer-end { display: flex; align-items: center; gap: 16px; }
/* Scoped to .site-footer so it outranks `.site-footer a` above, which would
   otherwise win on specificity.
   --white, NOT --cream. The rule above asks for --cream, but that is a
   theme.json palette slug: WordPress emits it as --wp--preset--color--cream
   and nothing defines a bare --cream, so footer links have been falling back
   to the band's inherited white all along. Asking for a token that does not
   exist works only by accident, and the accident is invisible until someone
   defines it. currentColor in the SVG carries the value, so there is no
   second copy of the asset to keep in step with the band. */
.site-footer .mg-social {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--white);
  opacity: .72;
  transition: opacity .15s ease;
  /* 44px of target around a 22px mark: this is a footer, and a thumb on a
     phone does not get a second attempt at a link it has scrolled to. */
  padding: 11px;
  margin: -11px;
}
/* The underline inherited from .site-footer a:hover has nothing to underline
   here -- the link's only content is a graphic. */
.site-footer .mg-social:hover,
.site-footer .mg-social:focus-visible { opacity: 1; text-decoration: none; }
.site-footer .mg-social:focus-visible {
  outline: 2px solid var(--white); outline-offset: 0; border-radius: 4px;
}
/* "Follow us on", beside the mark rather than under it. Sized to the
   copyright line below it: this is footer small print that happens to be an
   invitation, not a heading. nowrap so it never breaks between "us" and "on"
   and leaves a word sitting alone above the icon. */
.site-footer .mg-social-label {
  font: 500 13px/1 var(--font-ui);
  white-space: nowrap;
}
@media (prefers-reduced-motion: reduce) { .site-footer .mg-social { transition: none; } }

/* ON A PHONE THE FOOTER'S SECOND ROW SPANS THE BAND.
   .mg-footer-legal wraps at this width, so the brand takes the first line and
   this group the second -- but the group is only as wide as its own contents,
   so "Follow us on" and the copyright ended up huddled together at the left
   with the rest of the band empty beside them. Full width and space-between
   puts the invitation under the wordmark and the copyright out at the right
   margin, which is the arrangement the wide footer already has. */
@media (max-width: 640px) {
  .mg-footer-end {
    width: 100%;
    justify-content: space-between;
    /* NO COMPENSATING MARGIN HERE. The link carries padding:11px with
       margin:-11px, so its 44px target hangs outside the container while the
       WORDS inside it still start exactly on the margin -- which is the whole
       point of that pair. Nudging the group 11px right to "correct" for the
       negative margin moves the text off the margin instead of onto it. */
  }
}

@media (max-width: 640px) {
  .site-header { --mark-h: 46px; }
  /* The strapline stays, scaled down with the mark rather than dropped. Its
     leading is already calc(--mark-h / 3), so the three lines still total the
     lockup's height; only the type and the spacing come in. At 10.5px
     "Environmentally Conscious" measures about 125px, which is what the
     narrowest phone leaves once the lockup and the rule have had theirs. */
  .mg-strapline {
    margin-left: 14px;
    padding-left: 14px;
    font-size: 10.5px;
  }
}

/* The smallest phones still in use -- 320px, an SE or an older Android. At the
   sizes above, "Environmentally Conscious" runs about 125px and the lockup
   leaves it only ~102px, so it wrapped to a fourth line: taller than the mark
   beside it, and taller than the header itself. The mark gives up 6px and the
   spacing 4px either side, which buys the line back. */
@media (max-width: 360px) {
  .site-header { --mark-h: 40px; }
  .mg-strapline {
    margin-left: 10px;
    padding-left: 10px;
    font-size: 10px;
  }
}

/* --------------------------------------------------------------------------
   Mobile navigation -- §21 hides .nav-links below 900px but does not say what
   replaces it.

   A <details> disclosure: no framework, no plugin, and it still opens with
   JavaScript disabled. §23 has five top-level items with children, so the
   panel handles one level of nesting.
   -------------------------------------------------------------------------- */

.mg-nav { display: flex; align-items: center; }
.mg-nav-mobile { display: none; position: relative; }

.nav-links { list-style: none; margin: 0; padding: 0; }
.nav-links li { position: relative; }
.nav-links .sub-menu {
  position: absolute; top: calc(100% + 10px); left: 0;
  display: none; min-width: 210px;
  list-style: none; margin: 0; padding: 8px 0;
  background: var(--white); border: 1px solid var(--border);
  border-radius: var(--radius-md); box-shadow: 0 6px 20px rgba(0,0,0,.09);
  z-index: 50;
}
.nav-links li:hover > .sub-menu,
.nav-links li:focus-within > .sub-menu { display: block; }
.nav-links .sub-menu a { display: block; padding: 8px 18px; font-size: 14px; }

@media (max-width: 900px) {
  .mg-nav-mobile { display: block; }

  .mg-nav-mobile summary {
    list-style: none; cursor: pointer;
    width: 40px; height: 40px;
    display: flex; flex-direction: column; justify-content: center; gap: 5px;
  }
  .mg-nav-mobile summary::-webkit-details-marker { display: none; }
  .mg-nav-mobile summary span {
    display: block; height: 2px; width: 24px;
    background: var(--green-deep); border-radius: 2px;
  }

  .mg-nav-panel {
    position: absolute; right: 0; top: calc(100% + 12px);
    min-width: 232px; z-index: 200;
    list-style: none; margin: 0; padding: 10px 0;
    background: var(--white); border: 1px solid var(--border);
    border-radius: var(--radius-md); box-shadow: 0 10px 28px rgba(0,0,0,.12);
  }
  .mg-nav-panel a {
    display: block; padding: 10px 20px;
    font: 500 15px/1.3 var(--font-ui); color: var(--ink); text-decoration: none;
  }
  .mg-nav-panel a:hover { color: var(--green-deep); background: var(--cream); }
  .mg-nav-panel .sub-menu {
    position: static; display: block; border: 0; box-shadow: none;
    padding: 0 0 4px; margin: 0; background: transparent; min-width: 0;
  }
  .mg-nav-panel .sub-menu a { padding-left: 36px; font-size: 14px; color: var(--muted); }
}

/* --------------------------------------------------------------------------
   Editorial templates
   -------------------------------------------------------------------------- */

/* A page opens on its own H1, which carries its own space above it, so it sits
   tighter under the header than an article or an archive does. */
.mg-page { padding-block: 20px 72px; }
/*
   A MEASURE FOR THE ARTICLE, NOT THE PAGE'S WIDTH.

   .container is 1280px, which is right for a dashboard of cards read by
   glancing and wrong for prose read line by line. A line that long makes the
   eye travel further than it wants to and then hunt for the start of the next
   one -- the reader loses their place on the return sweep, which is the whole
   reason typography has a measure at all.

   PIXELS, NOT ch, AND THE REASON IS ANATOMY RATHER THAN TYPOGRAPHY. This was
   60ch first, on the usual argument that the column should follow the
   typeface. It should not. The return sweep is a physical movement of the
   eyes across a physical distance, and that distance does not change because
   the type got bigger. A ch column raised to 20px body text would grow WIDER
   in pixels -- a longer sweep, at the exact moment the larger type was chosen
   to make reading easier. Backwards.

   540px, chosen by measuring rather than by taste. Counting the characters
   that actually land on the first line of a real paragraph at 16px Montserrat:

       605px = 81 characters   -- past the end of the range
       570px = 73 characters
       534px = 67 characters

   45 to 75 is the long-standing range and 66 the usual optimum, so 540 sits
   just inside it with a little room. Worth noting that the ch arithmetic is
   also a trap in its own right: most letters are narrower than a digit, so
   68ch sets 81 characters, not 68. Trusting the number would have left the
   line a sixth too long and feeling, correctly, like nothing had changed.

   On .container so the gutters, the auto margins and the mobile behaviour all
   still come from there -- this only takes the ceiling down, and below 540
   the percentage width takes over as before. Two classes (0,2,0) so it beats
   .container's own max-width whatever the load order.
   -------------------------------------------------------------------------- */
.container.mg-measure { max-width: 540px; }

/*
   THE ARTICLE IN TWO COLUMNS: the photograph held, the writing scrolling.

   A lead image that scrolls away is seen once, at the moment the reader is
   least ready to look at it -- before they know what the piece is about.
   Held in a column of its own it is still there for the paragraph that
   finally explains it.

   1fr for the picture and a fixed 540 for the prose, not two fractions. The
   measure is a physical distance (see above) and must not stretch because a
   window did; the picture has no such rule and can take whatever is left.
   The container's own max-width is what decides how much that is.

   .has-figure, applied in single.php only when there is a photograph.
   Without the guard an article with none would still reserve the left column
   and the text would sit in the right two fifths of the page for no reason a
   reader could see.
   -------------------------------------------------------------------------- */
/* The full page width, the same 1280 as every other container, so the article
   lines up with the header and the maps rather than sitting in a narrower
   well of its own. */
.container.mg-single-layout { max-width: var(--content-width); }
/*
   TWO EQUAL COLUMNS. Written as fractions rather than as two 620s: at 1280
   they resolve to exactly 620 + 40 + 620, and between 900 and 1280 they
   share what there is instead of overflowing the container. A pair of fixed
   620s would have been correct at one window size and broken at every other.
*/
.mg-single-layout.has-figure {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 40px;
  align-items: start;              /* or the aside stretches and sticky dies */
}
/* The column IS the measure here -- the body fills its half. */
.mg-single-layout.has-figure .mg-single-body { max-width: none; }
/* With no picture the body keeps the measure and the centring it had. */
.mg-single-layout:not(.has-figure) .mg-single-body { max-width: 540px; margin-inline: auto; }

/*
   STUCK UNDER THE HEADER, not at the top of the window. .site-header is
   sticky at 88px and shrinks to 72 once it has been scrolled past, which is
   the state the page is in whenever this matters. 24px of air under it so the
   picture is not wearing the header as a hat. The admin bar moves everything
   down for a logged-in editor.
   -------------------------------------------------------------------------- */
.mg-single-layout.has-figure .mg-single-aside {
  position: sticky;
  top: 96px;
}
.admin-bar .mg-single-layout.has-figure .mg-single-aside { top: 128px; }

/* Zero, not the 32px it has when it is the lead image in a single column:
   in its own column there is nothing underneath it to be separated from. */
.mg-single-layout.has-figure .mg-single-media { margin-bottom: 0; }

/*
   ONE COLUMN BELOW 900. Two columns need about 1000px to hold a 540 measure
   beside a picture worth looking at; under that the picture would be a
   thumbnail and the prose would be narrower than its own measure. Stacked,
   the image leads as it did before and sticky comes off -- a sticky element
   in a single column just covers the thing underneath it.
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
  .container.mg-single-layout { max-width: 620px; }
  .mg-single-layout.has-figure { display: block; }
  .mg-single-layout.has-figure .mg-single-aside { position: static; }
  .mg-single-layout.has-figure .mg-single-media { margin-bottom: 32px; }
}

/* The lead photograph. Full column width, and NOT cropped: the teaser on
   /news forces 3:2 because a column of them needs one shape to read down, and
   with one image and no column that argument does not carry over. */
.mg-single-media { margin: 0 0 32px; }
.mg-single-media img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-md);
}
.mg-single-media figcaption {
  margin-top: 8px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}

.mg-single, .mg-archive, .mg-404 { padding-block: 0 72px; }
/* The posts page's heading, which index.php builds rather than the editor,
   so it does not arrive inside a .page-head block with its own margins. */
.mg-archive > .page-head { margin-bottom: 36px; margin-top: 20px; }
/* 10 above, 8 below: the title's own space above it comes from the eyebrow,
   and what follows now is the byline, which belongs to it. */
.mg-single h1 { margin-block: 10px 8px; }
/* The byline sits close under the title -- they are one unit -- and then the
   article starts at a clear distance. More space below than above, which is
   the inverse of a section title and right for the same reason: this line
   belongs to the title above it, not to the paragraph below. */
.mg-byline {
  margin: 0 0 32px;
  font-size: 15px;
  color: var(--muted);
}
.mg-byline a { color: var(--ink); text-decoration: none; }
.mg-byline a:hover { color: var(--green-deep); text-decoration: underline; text-underline-offset: 3px; }

/* One biography per person, under the article. A rule above it rather than a
   filled panel: it is a note about the writer, not a second article, and a
   tinted box at the foot of a page competes with the footer underneath it. */
.mg-authorbox {
  margin-top: 56px;
  padding-top: 24px;
  border-top: 1px solid var(--border);
}
/* The same mark as .eyebrow, because it does the same job -- a small label
   naming what follows. Composed rather than copied: single.php gives the
   heading .eyebrow as well, so this rule carries only the spacing. */
.mg-authorbox-head { margin: 0 0 12px; }
.mg-authorbox-name { margin: 0 0 6px; font-size: 17px; font-weight: 600; }
.mg-authorbox-name a { color: var(--ink); text-decoration: none; }
.mg-authorbox-name a:hover { color: var(--green-deep); text-decoration: underline; text-underline-offset: 3px; }
.mg-authorbox-bio { max-width: 68ch; color: var(--muted); }
.mg-authorbox-bio p { margin: 0 0 10px; }
.mg-authorbox-bio p:last-child { margin-bottom: 0; }
/* The author's biography at the head of their own archive. Sits between the
   label and the list of pieces, with more room under it than over it: it
   belongs to the name above, and the articles below are a separate thing. */
.mg-archive-intro {
  margin: 8px 0 28px;
  max-width: 68ch;
  color: var(--muted);
}
.mg-archive-intro p { margin: 0 0 10px; }
.mg-archive-intro p:last-child { margin-bottom: 0; }

/*
   TWO ARTICLES TO A ROW.

   A list read down a single column is one article at a time; two to a row is
   a page you scan. The teasers each carry a picture now, which is what makes
   the second column worth having -- a column of text alone would just be a
   narrower version of the same list.

   Fractions, not halves of a known number: the container is 1280 at full
   width and narrower whenever the window is, and the columns should follow it
   rather than be told what they are.
   -------------------------------------------------------------------------- */
.mg-teasers {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 48px 40px;
}
/* NO RULE UNDER EACH ONE. A border-bottom reads as a divider between items in
   a list; in a grid it draws a line under one cell of a pair and looks like a
   mistake, and the two cells are rarely the same height so the lines do not
   even align. The gap does the separating. */
.mg-teaser { padding-block: 0; border-bottom: 0; }

@media (max-width: 760px) {
  /* One column on a phone. Two columns of 160px are not two articles, they
     are two columns of broken words. */
  .mg-teasers { grid-template-columns: minmax(0, 1fr); gap: 40px; }
}
/* §16 feature title.

   A FLOOR OF TWO LINES, so the pictures line up across a row.

   Measured: the line box is 33px and a one-line headline next to a two-line
   one started its picture exactly 33px higher -- the row read as two cards
   that had slipped. Reserving the second line costs a band of white under
   the short headlines and buys an aligned row, which is the better trade on
   a page meant to be scanned.

   Two lines and not three: of the headlines on this site almost all are one
   or two, and a floor of three would hold a lot of white open for the rare
   long one. A three-line headline still pushes its own card down, and that
   is the right failure -- it is the exception paying, not every card.

   Written twice on purpose. `lh` is exactly one line box and needs no
   assumption; `em` is the fallback for anything that does not know it, and
   2.2em is two lines at the 1.1 line-height theme.json sets for h2. A
   browser that understands both takes the second. */
.mg-teaser h2 {
  font-size: 30px;
  margin-block: 8px 4px;
  min-height: 2.2em;
  min-height: 2lh;
}
/* On a teaser the byline sits tight under the title and lighter than on the
   article itself -- here it is one field of a summary, not the line that
   opens a piece. */
.mg-byline-teaser { margin: 0 0 8px; font-size: 13px; }
/* The featured image on a teaser.

   A MEASURE, NOT THE FULL COLUMN. The list runs to 1280px and a picture that
   wide per article turns a page of five into a slideshow you scroll past to
   reach the writing. 560 is about the width of the excerpt underneath it, so
   the two read as one block.

   3:2 with object-fit: cover, so a column of teasers keeps one shape whatever
   was uploaded -- portrait, panorama or square. A list whose images each set
   their own height has no rhythm down the page, and the eye reads the ragged
   edge instead of the headlines. */
.mg-teaser-media {
  display: block;
  margin: 0 0 14px;
}
/*
   SQUARE, CENTRE-CROPPED, THE FULL WIDTH OF ITS COLUMN.

   object-fit: cover with the default centre origin takes the middle of
   whatever was uploaded and fills the square, rather than squashing a
   landscape photograph into one. A column of teasers then keeps a single
   shape down the page, which is the whole argument for cropping a thumbnail
   at all -- the eye reads the headlines instead of a ragged edge of images.

   The 560px cap is gone: in a two-column grid the column IS the width, and a
   cap would have left a gutter inside one cell and not the other.

   This is a THUMBNAIL and is cropped on purpose. The lead image on the
   article itself is not -- there is one of it and no column to line up with,
   so the photographer's frame is left alone. See .mg-single-media.
   -------------------------------------------------------------------------- */
.mg-teaser-media img {
  display: block;
  width: 100%;
  /*
   * height: auto IS LOAD-BEARING, and leaving it out is why these were not
   * square on the live page while they were square in a test harness.
   *
   * the_post_thumbnail() emits width="1024" height="683" on the <img>. Those
   * attributes are presentational hints -- real CSS, at a specificity below
   * any author rule but above nothing at all. With no author rule for height
   * the hint applies, the box gets a definite 683px, and a definite height
   * BEATS aspect-ratio: the ratio only computes a height when the height is
   * auto. Measured on the live page: aspect-ratio reported 1 / 1, object-fit
   * reported cover, and the box was 620x683.
   *
   * The harness missed it because I hand-wrote the <img> without the width
   * and height attributes WordPress adds. Same shape of mistake as testing
   * CSS in the wrong stylesheet order: the harness was not wrong about what
   * it tested, it was testing markup the site does not serve.
   */
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  object-position: center;
  border-radius: var(--radius-md);
}
.mg-teaser h2 a { color: var(--ink); text-decoration: none; }
.mg-teaser h2 a:hover { color: var(--green-deep); }
.mg-excerpt { color: var(--muted); }
/* The way in to the article, under its excerpt. Quiet and in the site's
   green, like every other forward link here -- the headline is the loud way
   in and this is the one for a reader who has just finished the summary. */
.mg-teaser-more { margin: 10px 0 0; }
.mg-teaser-more a {
  font: 600 13px/1.3 var(--font-ui);
  color: var(--green-deep);
  text-decoration: none;
}
.mg-teaser-more a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* Read aloud, not drawn. The theme had no helper for this and WordPress only
   ships one in its own stylesheets, which the front end does not load.
   clip-path over display:none, because a hidden element is not announced --
   the whole point here is that it IS. */
.mg-sr {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.mg-404 { text-align: center; padding-block: 96px 120px; }
.mg-404 h1 { margin-block: 8px 16px; }

@media (max-width: 640px) {
  /* .mg-page keeps its 20px top here -- only the bottom comes in. */
  .mg-page { padding-block: 20px 48px; }
  .mg-single, .mg-archive { padding-block: 36px 48px; }
}

/* --------------------------------------------------------------------------
   Container gutters -- the width scale from the design spec.

     viewport   gutter/side   content
     >= 1440px      40px      1360px  (the max-width takes over here)
        1280px      32px      1216px
        1024px      24px       976px
         768px      20px       728px
        < 768px     16px      100% - 32px

   The containers themselves are §21's and are not touched: they already read
   --gutter and --dashboard-width, so redefining the token is enough. These are
   min-width queries and this file loads after §21, so they win over its
   max-width steps (24px at 900, 16px at 640) wherever the two overlap.

   At 1440 no rule is needed: calc(100% - 64px) is 1376, which max-width clamps
   to 1360, leaving exactly the 40px the spec asks for.
   -------------------------------------------------------------------------- */
:root { --gutter: 16px; }
@media (min-width: 768px)  { :root { --gutter: 20px; } }
@media (min-width: 1024px) { :root { --gutter: 24px; } }
@media (min-width: 1280px) { :root { --gutter: 32px; } }

/* On a dashboard page the header and footer follow the dashboard's width.
   .container is 1280 and .dashboard-container is 1360, so left as they were
   the wordmark sat 40px inside the left edge of the content beneath it --
   close enough to look like a mistake rather than a margin. Scoped to
   body.mg-dashboard, which mg_body_class() adds only to those pages, so
   editorial pages keep the 1280 measure they are set at. */
body.mg-dashboard .site-header .container,
body.mg-dashboard .site-footer .container {
  max-width: var(--dashboard-width);
}

/* --------------------------------------------------------------------------
   Primary navigation

   The nav sits beside the strapline rather than at the far end of the header.
   .nav is space-between, so with only the brand and the menu in it the menu
   was thrown to the right edge and the header read as two things at opposite
   ends of a wide empty bar. An auto margin on the right pulls it back against
   the brand and hands the leftover space to whatever follows -- which on the
   homepage is the weather, and that DOES belong at the far end.
   -------------------------------------------------------------------------- */
.mg-nav { margin-left: 32px; margin-right: auto; }

/* The page you are on, in the menu. WordPress marks it with a class and the
   theme's own fallback marks it with aria-current; both are honoured so the
   two kinds of menu behave the same. */
.nav-links .current-menu-item > a,
.nav-links a[aria-current="page"] {
  color: var(--green-deep);
  /* An underline offset clear of the descenders, not a bolder weight: weight
     would shift the item's width and move its neighbours as you browse. */
  text-decoration: underline;
  text-underline-offset: 5px;
  text-decoration-thickness: 2px;
}

/* --------------------------------------------------------------------------
   Home page -- template-home.php
   -------------------------------------------------------------------------- */

/* Full-bleed band, content on the same 1360 measure as everything else.

   CAREFUL: this is (0,1,1) and the per-band rules below it -- .mg-home-body,
   .mg-home-map, .mg-home-cards -- are all (0,1,0), so it beats every one of
   them and their padding-block never applies. Measured on the live page, all
   three bands compute to 0 top and bottom; the only band rule that survives
   is .mg-home > section:last-child, which out-specifies this one.

   The card bands are spaced by .mg-cards-head's own margin instead. If this
   reset is ever narrowed or those rules given the specificity to win, that
   margin and the band padding will BOTH apply and the gaps will double. */
.mg-home > section { padding-block: 0; }
.mg-hero {
  /* Height is a choice of three, each a clamp rather than a fixed box: the
     proportion holds from a laptop to a large monitor, with a floor that stops
     it collapsing on a short window and a ceiling that stops it swallowing a
     tall one. Set in Appearance -> Customize -> Homepage. */
  position: relative;
  display: flex;
  align-items: center;
  min-height: clamp(300px, 40vh, 500px);
  background: var(--green-deep);
  background-size: cover;
  /* Horizontal centre, vertical from the focal control -- a photograph has a
     subject somewhere up or down the frame, and cover crops whichever edge the
     shape does not need. */
  background-position: 50% var(--hero-focal, 50%);
  color: var(--cream);
  padding-block: 72px;
}
.mg-hero.is-short  { min-height: clamp(240px, 32vh, 380px); padding-block: 56px; }
.mg-hero.is-medium { min-height: clamp(300px, 40vh, 500px); }
.mg-hero.is-tall   { min-height: clamp(420px, 58vh, 680px); padding-block: 88px; }

/* The wash that keeps the text readable, as its own layer so the strength is
   one number. It is a gradient, not a flat tint: the words are on the left, so
   the right of the photograph can stay much clearer than the left. */
.mg-hero.has-image::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg,
    rgba(23, 79, 72, var(--hero-overlay, .45)) 0%,
    rgba(23, 79, 72, calc(var(--hero-overlay, .45) * 0.35)) 100%);
}
/* Above the wash. Without this the whole hero would be behind it. */
.mg-hero > .dashboard-container { position: relative; z-index: 1; }

.mg-hero-eyebrow {
  margin: 0 0 14px;
  font: 700 12px/1.2 var(--font-ui);
  letter-spacing: .14em;
  text-transform: uppercase;
  opacity: .82;
}
/* The one place a heading is allowed to run this large: it is the first thing
   on the site and has nothing above it to crowd. */
.mg-hero-title {
  margin: 0;
  font: 400 clamp(44px, 6vw, 76px)/1.05 var(--font-editorial);
  color: var(--cream);
}
.mg-hero-sub {
  margin-top: 16px;
  max-width: 46ch;
  font: 500 12px/1.4 var(--font-ui);
  opacity: .92;
}
.mg-hero-sub p { margin: 0; }
/* Flex so two buttons sit on one line and wrap together on a narrow screen,
   rather than the second dropping half under the first. */
.mg-hero-actions { display: flex; flex-wrap: wrap; gap: 12px; margin: 28px 0 0; }
/* One width for both, so they read as a pair rather than as two buttons whose
   labels happen to differ in length -- "View the dashboard" and whatever sits
   beside it would otherwise size themselves independently and line up ragged
   once they stack on a phone.
   box-sizing is border-box from §2, so 210px is the button, padding included.
   max-width keeps it honest below about 250px of viewport, where a fixed
   210 plus the gutters would start pushing at the edge. */
.mg-hero .button {
  /* 220, up from 210: the 10px gap below comes out of the label's room, so
     the button has to grow by what the gap took or the longest label loses a
     word to it. */
  width: 220px;
  max-width: 100%;
  /* The markup has a space between the label and the arrow, and it does
     nothing: .button is inline-flex, so the label and the arrow are separate
     flex items and whitespace between them is stripped. The gap is what puts
     air there. */
  gap: 10px;
}
/* NO PRIMARY BUTTON OVERRIDE HERE ANY MORE.
   The hero used to be a solid deep-green panel, where a deep-green button
   would have vanished into its own background, so it was inverted to cream.
   The hero is a photograph now and that reason has expired: the primary
   button is the site's primary button here as everywhere else, green with
   white on it, which is what the styleguide has always said at §17.

   The secondary stays inverted. Its outline is the only thing drawing it, and
   a deep-green line on a photograph is a line nobody can see. */
.mg-hero .button-secondary {
  border-color: rgba(247, 246, 241, .6);
  color: var(--cream);
  /* Frosted, not transparent. An outlined button on a photograph has only its
     own 1px line holding it together, and whatever the photograph is doing
     underneath goes on doing it straight through the label -- foliage behind
     cream text is the worst of it. A tenth of cream lifts the panel off the
     image and the blur takes the detail out from under the words, so the
     button reads as a button rather than as text lying on a picture. */
  background: rgba(247, 246, 241, .1);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
/* Hover has to move now that the resting state is where hover used to be. */
.mg-hero .button-secondary:hover {
  border-color: var(--cream);
  background: rgba(247, 246, 241, .22);
}

/* The page's own blocks, when it has any. */
.mg-home-body { padding-block: 48px 0; }

.mg-home-map { padding-block: 56px 0; }
/* One rule for every card band, because the bands are generated from whatever
   sections exist rather than written out one at a time. The last band carries
   the bottom padding, whichever band that turns out to be. */
.mg-home-cards { padding-block: 64px 0; }
.mg-home > section:last-child { padding-bottom: 72px; }

@media (max-width: 640px) {
  /* Shorter on a phone: 60vh of a tall narrow screen is most of it, and the
     content below should be visible without scrolling to find out it exists. */
  /* Every height comes down on a phone: a tall hero there is the whole screen,
     and what follows should be visible without scrolling to learn it exists.
     Written per class so the three stay in the same order they are on desktop. */
  .mg-hero.is-short  { min-height: clamp(220px, 30vh, 300px); padding-block: 36px; }
  .mg-hero,
  .mg-hero.is-medium { min-height: clamp(260px, 36vh, 380px); padding-block: 44px; }
  .mg-hero.is-tall   { min-height: clamp(320px, 46vh, 460px); padding-block: 52px; }
  .mg-home-map, .mg-home-cards { padding-block: 40px 0; }
  .mg-home > section:last-child { padding-bottom: 48px; }
}

/* Cards -- [mg_glance] and [mg_initiatives], one renderer, two styles ------ */
/* The space above a card band comes from here, not from the band's own
   padding. .mg-home-cards asks for padding-block: 64px 0 and never gets it --
   .mg-home > section { padding-block: 0 } is (0,1,1) and beats it, so every
   band on the home page computes to zero and the sections ran 16-28px apart.
   See the note on that reset. This is the heading's own margin, which nothing
   is competing for, and it is the only rule separating the bands. */
.mg-cards-head {
  display: flex;
  align-items: baseline;
  gap: 24px;
  flex-wrap: wrap;
  margin: 60px 0 22px;
}
.mg-cards-title { margin: 0; font-size: 38px; }
.mg-cards-sub { margin: 0; color: var(--muted); font-size: 15px; }

/* The column count comes from the renderer, not from auto-fit.
   auto-fit packs in as many as the width allows and stops thinking, so the
   same six cards came out four-and-two on one iPad and five-and-one on
   another, the orphan wandering about being the only sign of the rule.
   mgd_card_columns() picks the fewest rows and then the evenest split, and
   sends one number per breakpoint as a custom property. The fallbacks here
   are what fits at each width, so a grid rendered without them still behaves.
   The card's own min-width is gone with it: the columns are equal fractions
   now, and a minmax floor could only fight the count it was given. */
.mg-cards-grid {
  display: grid;
  grid-template-columns: repeat(var(--c-sm, 2), minmax(0, 1fr));
  gap: 18px;
  /* Clear beneath the last row. .mg-home-cards pads 64px above a section and
     nothing below, so a grid ran straight into whatever followed it -- on the
     homepage the map, and on any section the next one's heading. */
  margin-bottom: 20px;
}
@media (min-width: 640px) {
  .mg-cards-grid { grid-template-columns: repeat(var(--c-md, 3), minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .mg-cards-grid { grid-template-columns: repeat(var(--c-lg, 4), minmax(0, 1fr)); }
}
/*
 * 1180, AND IT IS AN iPad NUMBER RATHER THAN A ROUND ONE.
 *
 * The full row used to start at 1280, where .container stops growing and six
 * cards come out 188px each. That left every 11-inch iPad on 3+3, which is
 * what this breakpoint was moved for -- and the obvious 1200 would have
 * missed them all. Landscape CSS widths:
 *
 *   iPad 10.9" (10th/11th gen)   1080   was 4+2, still is
 *   iPad mini                    1133   was 4+2, still is
 *   iPad Air 11"                 1180   3+3 -> one row
 *   iPad Pro 11"                 1194   3+3 -> one row
 *   iPad Air 13" / Pro 13"       1366   already one row
 *
 * The two that needed it sit at 1180 and 1194. A breakpoint at 1200 clears
 * neither by 6 and 20 pixels respectively, and would have changed nothing on
 * any iPad in the lineup.
 *
 * WHAT IT COSTS: at 1180 the container is 1132, so six cards are 174px with
 * 130 inside their padding, against 188/144 at 1280. The longest title the
 * homepage carries, "Sustainable Transportation", already wraps to two lines
 * at 1280 -- measured, 31px against a 16px single line -- so the narrower
 * card asks nothing of it that the wider one did not.
 *
 * THE FIVE-CARD STEP IS GONE, not overridden. It lived at this same 1180, so
 * leaving it would have made it unreachable -- a rule that looks live and
 * never fires. One rule now, reading --c-2xl. cards.php still emits --c-xl
 * and nothing reads it; harmless, and worth removing when that file is next
 * open.
 */
@media (min-width: 1180px) {
  .mg-cards-grid { grid-template-columns: repeat(var(--c-2xl, 6), minmax(0, 1fr)); }
}
.mg-card {
  display: flex;
  flex-direction: column;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 22px;
}
.mg-card-icon { width: 48px; height: 48px; display: block; margin-bottom: 16px; }
/* The icon, pointing where the card's link points.
   display:contents so the anchor draws no box: .mg-card-icon is a flex item
   with its own size and margin, and at two breakpoints and in the feature
   variant those change. An anchor around it would become the flex item and
   every one of those rules would be describing the wrong element. This way
   the image keeps them all. */
.mg-card-iconlink { display: contents; cursor: pointer; }
.mg-card-title {
  margin: 0;
  font: 600 12px/1.3 var(--font-ui);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--green);
}
.mg-card-value {
  margin: 8px 0 0;
  font: 400 34px/1.05 var(--font-editorial);
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.mg-card-caption { margin: 6px 0 0; font-size: 13px; line-height: 1.45; color: var(--muted); }
/* margin-top:auto pins the link to the bottom, so the CTAs line up across a row
   whose captions run to different lengths. ONLY when the link is last, though:
   two items in one column both asking for auto share the free space between
   them, which is why a tag above a link floated somewhere in the middle. */
.mg-card-cta { margin: 18px 0 0; padding-top: 14px; border-top: 1px solid var(--border); }
.mg-card-cta a {
  font: 600 13px/1.2 var(--font-ui);
  color: var(--green-deep);
  text-decoration: none;
}
.mg-card-cta a:hover { text-decoration: underline; }
/* The same line with no address behind it. Same size and weight, so the row of
   cards keeps its rhythm whether or not each one links somewhere -- but --ink
   rather than the link green, because green here means "this goes somewhere"
   and this does not. It carries no arrow either; that is the part that means
   go. */
.mg-card-cta.is-plain {
  /* Body text, matching .mg-card-caption above -- same size, same weight, same
     --muted. It was 600 and --ink, which is the link's own weight and nearly
     the link's own colour: quieter than a link but still set like one, so it
     read as a link that had stopped working rather than as a line of text. */
  font: 400 13px/1.45 var(--font-ui);
  color: var(--muted);
}
/* And it follows the caption into the editorial face on a card with no figure,
   for the same reason the caption does: there the card is read, not scanned. */
.mg-card.is-prose .mg-card-cta.is-plain {
  font-family: var(--font-editorial);
  line-height: 1.5;
}
.mg-card > .mg-card-cta:last-child { margin-top: auto; }

/* A LONE CARD SHOULD NOT SPAN THE PAGE.
   mgd_card_columns() gives one card one column, and one column of a 1360px
   container is a 1360px card: a sentence of caption stretched across the whole
   width with a border round it.

   vw, not a percentage. A percentage resolves against the grid AREA, so 50%
   would halve every card in a three-column row as well -- 441px each becoming
   220. Against the viewport it only bites when a card is already absurdly
   wide: in a three-column row at 1360 a card is 441px and 50vw is 720, so the
   rule does nothing there and everything to the lone one.

   Desktop only. Below 1024 a single card is close to the screen width anyway,
   which is what a card should be on a phone. */
@media (min-width: 1024px) {
  .mg-card { max-width: 50vw; }
}

@media (max-width: 640px) {
  .mg-cards-title { font-size: 30px; }
  /* Columns come from --c-sm on the base rule; only the gap changes here. */
  .mg-cards-grid { gap: 12px; }
  .mg-card { padding: 16px; }
  .mg-card-icon { width: 40px; height: 40px; margin-bottom: 12px; }
  .mg-card-value { font-size: 28px; }
}

/* The feature style: the same card, larger, with the title doing the work the
   figure does in a stat card. Only the sizes change -- the markup is identical,
   which is the point of there being one renderer. */
.mg-cards.is-feature .mg-card { padding: 26px; }
.mg-cards.is-feature .mg-cards-grid { gap: 20px; }
.mg-cards.is-feature .mg-card-icon { width: 64px; height: 64px; margin-bottom: 20px; }
.mg-cards.is-feature .mg-card-caption { margin-top: 10px; font-size: 14px; }
/* No rule above the link here: a feature card is a paragraph and a link, and a
   divider would cut a short card almost in half. */
.mg-cards.is-feature .mg-card-cta { border-top: 0; padding-top: 0; margin-top: 16px; }

@media (max-width: 640px) {
  .mg-cards.is-feature .mg-card { padding: 18px; }
  .mg-cards.is-feature .mg-card-icon { width: 52px; height: 52px; margin-bottom: 14px; }
}

/* ---------------------------------------------------------------------------
   Per-card icon size.

   The row sets the size -- 48px in a stat row, 64px in a feature row -- and a
   card can say otherwise. Three sizes, because the site has three: 40 and 64
   on the dashboard's widgets, 48 on the homepage. A fourth would be a fourth
   thing to keep in step for no reason anyone could see.

   Written after the .is-feature rules and with one more class than them, so a
   card's own choice beats its row's whichever order they are read in.

   The margin travels with the size. They were chosen together -- a 64px icon
   with a 16px gap under it reads as a mistake rather than a large icon -- so
   letting one be set without the other would only produce the combinations
   nobody wants.
   --------------------------------------------------------------------------- */
.mg-cards .mg-card.is-icon-s .mg-card-icon { width: 40px; height: 40px; margin-bottom: 12px; }
.mg-cards .mg-card.is-icon-m .mg-card-icon { width: 48px; height: 48px; margin-bottom: 16px; }
.mg-cards .mg-card.is-icon-l .mg-card-icon { width: 64px; height: 64px; margin-bottom: 20px; }

/* Shrunk at phone width in the same proportion the row's own sizes are, so a
   Large icon stays large relative to its card rather than swallowing it. */
@media (max-width: 640px) {
  .mg-cards .mg-card.is-icon-s .mg-card-icon { width: 32px; height: 32px; margin-bottom: 10px; }
  .mg-cards .mg-card.is-icon-m .mg-card-icon { width: 40px; height: 40px; margin-bottom: 12px; }
  .mg-cards .mg-card.is-icon-l .mg-card-icon { width: 52px; height: 52px; margin-bottom: 14px; }
}

/* ---------------------------------------------------------------------------
   A card with no figure is read, not scanned.

   The typography follows the CARD'S CONTENT, not the row it sits in. A card
   with a figure is a statistic: a small green label over a large number, where
   the label is signposting and the number is the point. A card without one is
   a short piece of writing, where the title IS the thing being read -- so it
   is set in the editorial face, large enough to be the first thing on the
   card, and the text under it matches.

   This was tied to .is-feature, which let the row decide, and that was wrong
   in both directions: a written card in a stat row got a 12px uppercase label
   for a headline, and a figure dropped into a feature row was given serif
   treatment it has no use for.

   Only the family changes on the caption, not the size. The row still decides
   how big its text is -- 13px in a stat row, 14px in a feature row -- and this
   rule has no business overruling that. The extra leading comes with the
   serif: Newsreader at 13px is tighter than Montserrat at the same measure.
   --------------------------------------------------------------------------- */
.mg-card.is-prose .mg-card-title {
  font: 400 21px/1.25 var(--font-editorial);
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
}
.mg-card.is-prose .mg-card-caption {
  font-family: var(--font-editorial);
  line-height: 1.5;
}

@media (max-width: 640px) {
  .mg-card.is-prose .mg-card-title { font-size: 19px; }
}

/* ---------------------------------------------------------------------------
   News & Events: the date, the time, the tag.

   Date and time are one line at the top of the card -- on a row of events that
   is the column the eye runs down, and the title answers it. They were two
   lines, with the time down beside the location; one answer to one question
   reads better than the same answer in two places. The tag is last and pinned
   to the foot, so a row of them lines up however long the cards above run.

   The two colours are the palette's, darkened until white sits on them safely.
   The site green behind white is about 3.0:1 and --water about 3.4:1, both
   under the 4.5:1 an 11px label needs; these are the same two hues taken down
   until they pass at roughly 4.8 and 5.7. Inventing a green would have put a
   third one on the page.
   --------------------------------------------------------------------------- */
.mg-card-date {
  margin: 0 0 10px;
  font: 600 11px/1.3 var(--font-ui);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The tag is the last thing in the card, so it is the one that takes the free
   space: margin-top:auto drops it to the foot and every tag in a row lines up,
   whatever the cards above them carry. The link then sits directly on top of
   it, so those line up too.

   padding-top rather than margin-top for the gap, because the tallest card in
   a row has no free space left for a margin to take. */
.mg-card-tag { margin: 16px 0 0; }
.mg-card > .mg-card-tag { margin-top: auto; padding-top: 16px; }
.mg-tag {
  display: inline-block;
  padding: 4px 10px;
  border-radius: var(--radius-sm);
  font: 600 11px/1.3 var(--font-ui);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--white);
}
.mg-tag.is-event   { background: var(--green-solid); }
.mg-tag.is-meeting { background: #256F88; }
.mg-tag.is-calendar { background: var(--orange-solid); }
/* A PLUM, AND NOT A BLUE. The first pick was #2A6F86, which measured 5.65:1
   against white and looked fine on its own -- and sat two points of hue from
   is-meeting's #256F88. Two near-identical blues on one row, reading Meeting
   and Article, is a distinction the colour claims to make and does not. The
   tag exists to say which KIND of thing a card is, so it has to differ from
   its siblings more than it has to match the palette.

   White on this is 6.8:1, against 4.85, 5.66 and 4.80 for the three above it,
   so it is the most legible of the four rather than the exception. */
.mg-tag.is-article { background: #7A4A7E; }

/* A card title that is a link. Hand-made cards on the homepage point at
   articles, and the headline should be the target as well as the CTA under
   it -- a reader aims at the headline first. */
.mg-card-title a { color: inherit; text-decoration: none; }
.mg-card-title a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* Coming Up, on the homepage.
   Almost nothing here: the band uses .mg-home-cards and .mg-cards, so its
   width, heading size and spacing are whatever a card section's are. That is
   the point -- it sat 40px narrower with a 42px heading while every section
   around it was 1360 wide with 38, because it had its own layout instead. */
.mg-cards-head .mg-cards-more { margin-left: auto; }
/* The location, above the summary and quieter than it: where it is matters
   less than what it is, and both are .mg-card-caption. */
/* The location. Breaks at its commas -- see mgd_address_html -- and
   break-word only as a last resort, for the one part too long for the card on
   its own, which would otherwise run out the side. */
.mg-card-where { color: var(--muted); overflow-wrap: break-word; }
/* The pin, riding on the end of the address. Inline-block with a nudge down so
   it sits on the text's baseline rather than hanging off the cap height, and
   nowrap on the last comma part so the glyph never lands alone on a line
   below the address it belongs to. */
/*
 * THE ADDRESS IS THE LINK, pin included -- see mgd_address_with_pin().
 *
 * NO UNDERLINE, and inherit the caption's colour. A card can already carry an
 * underlined title and a link under the summary; a third underline down the
 * middle would stop distinguishing anything and start reading as a card that
 * is entirely link. The pin is the affordance, and the whole line turning
 * green on hover confirms what the pointer is over.
 *
 * Block, so the target is the full width of the card rather than the width of
 * the words -- the point of the change was a finger, and a thumb aiming at a
 * two-line address should not have to land between the lines.
 */
.mg-address-link {
  display: block;
  color: inherit;
  text-decoration: none;
}
.mg-address-link:hover { color: var(--green-deep); }
.mg-address-link:hover .mg-address-pin { color: var(--green-deep); }
.mg-address-link:focus-visible {
  outline: 2px solid var(--green-deep); outline-offset: 2px; border-radius: 3px;
}
.mg-address-pin {
  display: inline-block;
  vertical-align: -2px;
  margin-left: 4px;
  color: var(--green);
  line-height: 0;
}

.mg-card-event .mg-card-date { color: var(--green); }
