:root { --rope: #C2622C; }
html { -webkit-text-size-adjust: 100%; }
  body { margin: 0; background: #E7E5DC; font-family: Archivo, Helvetica, Arial, sans-serif; color: #151812; }
  * { box-sizing: border-box; }
  p { text-wrap: pretty; }
  a { color: #22362B; text-decoration-color: var(--rope, #C2622C); text-underline-offset: 3px; }
  a:hover { color: var(--rope, #C2622C); }
  button { font: inherit; }
  img { max-width: 100%; }
main { display: block; }
[hidden] { display: none !important; }
nav::-webkit-scrollbar { display: none; }
.nav-link { text-decoration: none; }
a:focus-visible, button:focus-visible, input:focus-visible, textarea:focus-visible { outline: 2px solid var(--rope, #C2622C); outline-offset: 2px; }
input:focus, textarea:focus { border-color: #22362B; }

/* ---- header: sticky, hamburger on phones, inline row once the column fits ---- */
.site-header { position: sticky; top: 0; z-index: 20; background: #F1F0EA; border-bottom: 1px solid #DCDAD0; }
.site-nav { display: flex; gap: 2px; padding: 0 14px; }
.site-nav .nav-link { flex: 0 0 auto; position: relative; display: inline-block; padding: 9px 8px 10px; font-size: 14px; font-weight: 500; color: #151812; text-decoration: none; }
.site-nav .nav-link[aria-current="page"]::after { content: ""; position: absolute; left: 8px; right: 8px; bottom: 0; height: 2px; background: var(--rope, #C2622C); }
.menu-toggle { display: none; margin-left: 12px; width: 42px; height: 42px; padding: 0; border: 0; border-radius: 2px; background: none; cursor: pointer; flex-direction: column; align-items: center; justify-content: center; gap: 5px; }
.menu-toggle span { display: block; width: 22px; height: 2px; background: #151812; border-radius: 1px; transition: transform .18s ease, opacity .18s ease; }
.site-header.is-open .menu-toggle span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.site-header.is-open .menu-toggle span:nth-child(2) { opacity: 0; }
.site-header.is-open .menu-toggle span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
@media (max-width: 719px) {
  .menu-toggle { display: flex; }
  .site-nav { display: none; position: absolute; top: 100%; left: 0; right: 0; flex-direction: column; gap: 0; padding: 6px 0 10px; background: #F1F0EA; border-bottom: 1px solid #DCDAD0; box-shadow: 0 14px 30px rgba(12,18,13,0.14); }
  .site-header.is-open .site-nav { display: flex; }
  .site-nav .nav-link { padding: 13px 22px 13px 19px; font-size: 17px; border-left: 3px solid transparent; }
  .site-nav .nav-link[aria-current="page"] { border-left-color: var(--rope, #C2622C); font-weight: 600; }
  .site-nav .nav-link[aria-current="page"]::after { display: none; }
}

/* ======================================================================
   DESKTOP LAYOUT — everything below 900px is untouched.
   ----------------------------------------------------------------------
   The pages are hand-written inline styles built for a 720px column. This
   block widens that column above 900px and pairs up the content that has a
   natural pair, WITHOUT rewriting the pages: each page carries a handful of
   layout classes and the rules live here.

   THE SHELL IS THE WINDOW; THE CAP IS ON THE CONTENT INSIDE IT.
   Above 900px `.site-shell` has no max-width at all. Every child of `.site`
   - the GrantOps preview strip, the sticky header, the
   footer, the sticky call bar - is therefore already as wide as the window,
   and its background reaches both edges with no bleed rule of any kind. The
   reading column is capped further in, by `--page`, and each of those bars
   pads its own inner content back out to the same centred edges, so the
   logo, the nav, the bar text and the footer columns line up with the body
   copy above and below them.

   It is deliberately NOT `width: 100vw; margin-left: calc(50% - 50vw)` on
   each bar. 100vw counts the classic scrollbar that Windows and Linux draw
   inside the window, so every bar would be ~15px wider than the page and
   would sit ~7px left of centre. `.site` has `overflow-x: clip`, so that
   would clip rather than scroll - but the furniture would be misaligned
   with the content on exactly the machines the client's customers use.
   Percentages of a full-width ancestor have no such problem, and grid `1fr`
   gutters do the one thing percentages cannot. There is no `vw` in here.

   FIVE THINGS TO KNOW BEFORE EDITING.

   1. `.site-shell` replaces the inline `max-width:720px` that used to sit on
      every page's `.site` wrapper. Below 900px it is still 720px, so phones
      and tablets render exactly what they rendered before - that is the
      whole guarantee, and it is why every other rule here is inside the
      media query.

   2. `--page` IS THE ONLY WIDTH KNOB. 1240px, measured the way the shell's
      old max-width was: the rail's 54px/22px padding sits INSIDE it, so the
      reading column is 1164px and the content edges land 54px and 22px in
      from a notional 1240px page centred in the window.

      There are TWO SPELLINGS OF THE SAME GUTTER and which one you need
      depends only on what `100%` means where you use it. A percentage
      resolves against the containing block, and `var()` is a textual
      substitution, so one variable is different numbers in different
      places. `--gutter` (and `--edge-start` / `--edge-end`, built from it)
      is written for a containing block that is the whole window: anything
      parented by `.site`, plus anything positioned against `.ds-rail` or
      `.ds-hero`, both of which are now full width. `--gutter-rail` is
      written for a containing block that is the rail's CONTENT box, 76px
      narrower - which is the two dark bands, whose padding is measured
      against their own `<section>`. Both evaluate to the same number.
      Use the wrong one and the furniture lands 38px out.

   3. THE RAIL'S PADDING IS NEVER CHANGED. `.ds-rail` is `padding: 0 22px 0
      54px`, and every full-bleed block on every page reaches the rail's edge
      with an inline `margin: 0 -22px 0 -54px`. The two numbers are one
      mechanism. Change the padding and twenty images across six pages go out
      of alignment. Where a bleed block has to stop bleeding because it is
      now a grid column, its margin is zeroed instead.

      Above 900px the rail is the whole window, so the three blocks that
      still carry that margin - the home hero and the two dark bands - reach
      the window's edges rather than a column's, and no rule was needed to
      make them. That is precisely why the cap is NOT on `.ds-rail`: give the
      rail a max-width and those three need a 100vw escape hatch, which is
      the bug described above.

   4. THE ESCAPE FROM THE CAP IS A GRID COLUMN, NOT A NEGATIVE MARGIN.
      `main` holds exactly one wrapper element on every page; that wrapper
      becomes a grid with a capped centre track and a flexible gutter track
      each side. Sections land in the centre; the hero and the bands are
      placed across the whole span instead, and their existing inline
      margins carry them the last 76px out. `1fr` is the only mechanism that
      lets a child escape a centred cap without being told the window width,
      which is what keeps `vw` out of this file. It is safe vertically
      because the wrapper's children space themselves with PADDING, not
      margins, so there is no margin collapsing to lose.

   5. `!important` appears only where a rule has to beat an inline style.
      Inline styles outrank every selector, so the alternative is lifting
      hundreds of inline declarations out of the pages - a far bigger change
      than this one, and one that would touch the mobile rendering.

   Grid is used rather than flex throughout because these sections are flat
   lists of children (heading, paragraph, image, image, caption) with no
   wrapper elements to grab, and grid can place a flat list into columns.
   `row-gap: 0` everywhere: the pages space themselves with their own
   margins, and a row-gap would double it.
   ====================================================================== */
.site-shell { max-width: 720px; }

@media (min-width: 900px) {

  /* ---- the shell, and the furniture that spans it -------------------- */
  /* No cap: `.site` IS the window, so every bar below spans it for free and
     the page background reaches both edges. See the header note. */
  .site-shell {
    max-width: none;

    --page: 1240px;
    /* For a containing block that is the whole window. */
    --gutter: max(0px, (100% - var(--page)) / 2);
    --edge-start: calc(54px + var(--gutter));
    --edge-end: calc(22px + var(--gutter));
    /* Same number, for a containing block that is the rail's content box
       (76px narrower). Note 2 in the header explains why both exist. */
    --gutter-rail: max(0px, (100% + 76px - var(--page)) / 2);
    /* The GrantOps preview strip's own stylesheet is shared furniture with
       its own --pb-* knobs; this is where this site hands it the page width.
       Its filename is deliberately not spelled out here: styles.css ships at
       PUBLIC=1 and deploy.sh refuses a go-live build containing that token
       anywhere in the tree. Do not "helpfully" add it back. */
    --pb-page: var(--page);
  }

  /* Ribbon, sticky header and footer all sit inside .site, so they are the
     full width of the window on their own. What they need is for their
     content to line up with the rail's content edges - the 54px rope inset
     on the left, 22px on the right - wherever the reading column has been
     centred to. */
  .ds-ribbon { padding-left: var(--edge-start) !important; padding-right: var(--edge-end) !important; }
  .site-header > div:first-child { padding: 16px var(--edge-end) 14px var(--edge-start) !important; }
  /* The nav links carry 8px of side padding of their own, so the bar starts
     8px short and the first link's TEXT lands on the content edge. */
  .site-nav { padding-left: calc(var(--edge-start) - 8px); padding-right: var(--edge-end); }

  /* Footer: brand and links on the left, the contact strip beside them,
     the preview note across the foot of both. */
  .ds-footer {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: 48px;
    row-gap: 0;
    align-items: start;
    padding: 40px var(--edge-end) 44px var(--edge-start) !important;
  }
  .ds-footer > div { grid-column: 1; }
  .ds-footer > p { grid-column: 2; }
  .ds-footer > p:last-child { grid-column: 1 / -1; margin-top: 22px; }

  /* The call bar is a phone affordance. It stays - it is the page's one
     standing ask - but two 550px buttons look like a mistake, so it
     centres at a sensible size. The gutters put its centre on the reading
     column's centre rather than the window's; they differ by 16px, which is
     half the rail's 54/22 asymmetry. */
  .ds-callbar { justify-content: center; padding-left: var(--edge-start) !important; padding-right: var(--edge-end) !important; }
  .ds-callbar > a:first-child { flex: 0 0 300px !important; }
  .ds-callbar > a:last-child { flex: 0 0 180px !important; }

  /* ---- the reading column, and the three blocks that escape it -------
     The rope rail is positioned against `.ds-rail`, which is now the whole
     window, so it needs the gutter that the rail's own padding cannot give
     it. 26px is 28px left of the content edge, which is exactly where the
     sections' `left:-38px` rope dots sit. */
  .ds-rail > div[aria-hidden="true"] { left: calc(26px + var(--gutter)) !important; }

  /* `main` holds exactly one wrapper element on every page. That wrapper is
     the grid that caps the reading column: a centred track of --page less
     the rail's padding, with a flexible gutter each side. Children go in the
     centre; the hero and the charity band span the whole grid, and their
     inline `margin: 0 -22px 0 -54px` carries them the last 76px out to the
     window's edges. (.ds-pair is the same grid, split in two - see below.) */
  main > div {
    display: grid;
    grid-template-columns:
      [bleed-start] minmax(0, 1fr)
      [content-start] minmax(0, calc(var(--page) - 76px)) [content-end]
      minmax(0, 1fr) [bleed-end];
    row-gap: 0;
  }
  main > div > * { grid-column: content-start / content-end; }
  main > div > .ds-hero,
  main > div > .ds-band { grid-column: bleed-start / bleed-end; }
  /* 404 is the one page with no wrapper: its single section IS the column. */
  main > section { max-width: calc(var(--page) - 76px); margin-left: var(--gutter-rail); }

  /* ---- home hero: headline over the image, bottom left ---------------
     The hero spans the window, so its image and its gradient do too. The
     headline block is positioned against the hero, which means its inline
     `left:54px` is now 54px from the WINDOW's edge; it needs the gutter to
     land back on the content edge with everything else. */
  .ds-hero > img { height: 560px !important; }
  /* The rope rail is a positioned line at the content edge; with the hero
     spanning the window it would run straight across the photo. The hero
     paints above it (the sticky header is z-index 20, so it still wins)
     and the rail resumes under the hero with the first section dot. */
  .ds-hero { z-index: 6; }
  .ds-hero > div:last-child {
    top: auto !important;
    right: auto !important;
    left: var(--edge-start) !important;
    bottom: 46px;
    max-width: 640px;
  }
  .ds-hero h1 { font-size: 56px !important; margin-bottom: 16px !important; }
  .ds-hero p { font-size: 18px !important; max-width: 44ch !important; }

  /* ---- a block of copy that should not run the full 1084px ---------- */
  .ds-cta { max-width: 680px; }
  .ds-measure { max-width: 66ch; }

  /* ---- text one side, one block the other ---------------------------
     The flat-list case. Every direct child goes to column one except the
     one marked .ds-side, which takes column two and spans down the rows
     beside them. `span 8` is simply more rows than any section here has;
     with row-gap 0 the unused ones are zero-height. */
  .ds-aside {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: 36px;
    row-gap: 0;
    align-items: start;
  }
  .ds-aside > * { grid-column: 1; }
  .ds-aside > .ds-side { grid-column: 2; grid-row: 1 / span 8; margin: 0 !important; }
  /* .ds-side is sometimes the wrapper around the photo and sometimes the
     photo itself, depending on how that page was written. */
  .ds-aside > .ds-side img,
  .ds-aside > img.ds-side { height: 380px !important; }
  /* Mirrored: the block on the left, the words on the right. Same
     specificity as the rules above, so source order is what makes these
     win - keep them last. */
  .ds-aside--flip > * { grid-column: 2; }
  .ds-aside--flip > .ds-side { grid-column: 1; grid-row: 1 / span 8; }

  /* ---- before and after, side by side -------------------------------
     On a phone the after image overlaps the before with a shadow, pulled up
     by an inline -52px. Side by side that device has nothing to do, so the
     margins are zeroed and both images sit in their own column. The rope
     tick that pointed from the rail into the after image becomes a short
     rope rule above it, so the accent survives the change. */
  .ds-ba, .ds-ba3 {
    display: grid;
    column-gap: 22px;
    row-gap: 0;
    align-items: start;
  }
  .ds-ba { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ds-ba3 { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: 16px; }
  .ds-ba > h2, .ds-ba > p, .ds-ba3 > h2, .ds-ba3 > p { grid-column: 1 / -1; }
  .ds-ba > div, .ds-ba3 > div { margin: 0 !important; }
  .ds-ba > div img { height: 360px !important; box-shadow: none !important; }
  .ds-ba3 > div img { height: 300px !important; box-shadow: none !important; }
  /* Last child is always the Before/After caption; a first child that is
     not also the last is the rope tick, which only the after block has. */
  .ds-ba > div > span:last-child,
  .ds-ba3 > div > span:last-child { left: 14px !important; }
  .ds-ba > div > span:first-child:not(:last-child),
  .ds-ba3 > div > span:first-child:not(:last-child) {
    left: 0 !important; top: -16px !important; width: 46px !important;
  }

  /* ---- three service cards ------------------------------------------ */
  .ds-cards {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: 26px;
    row-gap: 0;
  }
  .ds-cards > h2, .ds-cards > p { grid-column: 1 / -1; }
  .ds-cards > a { margin-bottom: 0 !important; }
  .ds-cards > a img { height: 230px !important; }

  /* ---- three reviews, three columns --------------------------------- */
  .ds-quotes {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: 24px;
    row-gap: 0;
    align-items: start;
  }
  .ds-quotes > h2, .ds-quotes > p { grid-column: 1 / -1; }
  .ds-quotes > blockquote { margin-bottom: 0 !important; }

  /* ---- a photo grid built from repeating image/heading/copy trios ---- */
  .ds-tiles {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 30px;
    row-gap: 6px;
  }
  .ds-tiles > h2 { grid-column: 1 / -1; }
  .ds-tile > div { margin: 0 !important; }
  .ds-tile img { height: 250px !important; }
  .ds-tile > p { margin-bottom: 0 !important; }

  /* ---- a list of short lines, in two columns ------------------------ */
  .ds-cols2 > div { display: block !important; column-count: 2; column-gap: 40px; }
  .ds-cols2 > div > div { margin-bottom: 13px; break-inside: avoid; }

  /* ---- the full-bleed dark bands ------------------------------------
     Both bands' dark surface runs the full width of the window (they are
     placed in the grid's bleed span above). Their inner content is padded
     back onto the reading column's edges, so the band headline starts on the
     same line as the body copy above it and the band copy ends where the
     body copy ends.

     `--gutter-rail`, not `--gutter`: this padding is a percentage of the
     band's own <section>, which is the rail's content box, not the window.
     See note 2 in the header - the two are 38px apart. */

  /* A band's <section> is placed in the bleed span, so its box starts at the
     rail's edge rather than the content edge, and the rope dot its `left:
     -38px` hangs off the wrong one. The gutter puts it back on the rail with
     every other dot on the page - which is where it sat before this change,
     because the band's dark surface always started left of the rail. */
  .ds-band > span:first-child,
  .ds-band-media > span:first-child { left: calc(-38px + var(--gutter-rail)) !important; }

  /* Charity band: the headline holds column one, the copy runs beside it. */
  .ds-band > div {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
    column-gap: 44px;
    row-gap: 0;
    align-items: start;
    padding: 44px calc(22px + var(--gutter-rail)) 44px calc(54px + var(--gutter-rail)) !important;
  }
  .ds-band > div > h2 { grid-row: 1 / span 2; margin-bottom: 0 !important; }
  /* Storm band: the photo becomes the left half and stretches to whatever
     height the copy needs. `height: auto` is what lets align-self stretch a
     replaced element; object-fit keeps it from distorting. The photo starts
     at the content edge rather than the window's, so the two halves of the
     band sit inside the same column as everything else on the page. */
  .ds-band-media > div {
    display: grid;
    grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
    min-height: 400px;
    padding-left: calc(54px + var(--gutter-rail));
    padding-right: calc(22px + var(--gutter-rail));
  }
  .ds-band-media > div > img { height: auto !important; align-self: stretch; }
  /* No right padding of its own: the band's own padding already stops this
     copy on the content edge. */
  .ds-band-media > div > div { padding: 36px 0 38px 40px !important; }

  /* ---- two neighbouring sections, side by side -----------------------
     For a pair that lives in two <section>s rather than one. Everything
     else in the flow still spans. Consecutive a/b pairs land on their own
     row, so a page can use it more than once.

     `.ds-pair` is the wrapper `main` holds on those pages, so it is also
     the grid that caps the reading column: the same bleed gutter each side,
     with the centre split in two. The 36px between the halves is a TRACK,
     not a `column-gap` - a column-gap would also open 36px either side of
     the centre, pushing the content off the rope rail at narrow widths. */
  .ds-pair {
    display: grid;
    grid-template-columns:
      [bleed-start] minmax(0, 1fr)
      [content-start] minmax(0, calc((var(--page) - 112px) / 2)) [gap-start]
      36px
      [gap-end] minmax(0, calc((var(--page) - 112px) / 2)) [content-end]
      minmax(0, 1fr) [bleed-end];
    column-gap: 0;
    row-gap: 0;
    align-items: start;
  }
  .ds-pair > section { grid-column: content-start / content-end; }
  .ds-pair > .ds-pair-a { grid-column: content-start / gap-start; }
  .ds-pair > .ds-pair-b { grid-column: gap-end / content-end; }
  /* The storm band is the one child of a pair that spans the window. */
  .ds-pair > .ds-band-media { grid-column: bleed-start / bleed-end; }
  /* The rope dot is positioned at left:-38px against its own section, which
     puts it on the rail for a full-width section and in the middle of the
     page for one in column two. The rail is a left-hand-edge device, so the
     right-hand section of a pair gives its dot up; every section still in
     the flow keeps one, and the rail still reads. */
  .ds-pair > .ds-pair-b > span:first-child { display: none; }

  /* The annotated felling photo: out of the bleed, into its column, with
     the three annotation rules pulled in off the old bleed edges. */
  .ds-annot { padding-top: 30px !important; }
  .ds-annot > div { margin: 0 !important; }
  .ds-annot > div > div { left: 16px !important; right: 16px !important; }
  .ds-annot img { height: 440px !important; }
}
