/* ==========================================================================
   Ping News — site chrome
   The page CONTENT is styled by css/squarespace.css (the original stylesheet,
   pruned by tools/extract_css.py). This file only covers the parts that were
   rebuilt by hand — header, menu, footer, news index and article pages —
   plus anything we deliberately override.
   ========================================================================== */

:root{
  --mint:#00ffbe;          /* accent      */
  --purple:#7300fa;        /* brand black */
  --ink:#1e1e1e;
  --paper:#f9f9f9;
  --site-gutter:4vw;
  --site-max-width:1500px;
  --font-display:"IBM Plex Sans Condensed","DM Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
  --font-body:"DM Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
}

img{max-width:100%;height:auto}

/* --- header ---------------------------------------------------------------
   Measured off the live site at 1280px: logo 256x106, social 45px,
   burger 47x37, 51px side gutter (~4vw), header 185px tall, not sticky.
   -------------------------------------------------------------------------- */
.site-header{
  /* the original header is absolutely positioned and adds no flow height;
     the first section carries top padding equal to the header height instead
     (Squarespace sets that with JS — see the script in base.html) */
  position:absolute; top:0; left:0; right:0; z-index:100;
  background:#fff;
  display:flex; align-items:center; justify-content:space-between;
  padding:40px var(--site-gutter);
}
body{position:relative}
.site-header .logo img{width:min(256px,42vw);height:auto;display:block}
.header-right{display:flex;align-items:center;gap:1.4rem}
.social-icon{
  box-sizing:border-box;            /* so the 1px border stays inside 45px */
  width:45px;height:45px;border-radius:50%;
  border:1px solid var(--purple); color:var(--purple);
  display:grid;place-items:center;flex:none;
}
.social-icon svg{width:21px;height:21px;fill:currentColor}
.burger{
  width:47px;height:37px;background:none;border:0;cursor:pointer;
  display:grid;align-content:center;gap:8px;padding:0;flex:none;
}
.burger span{display:block;height:3px;background:var(--purple);border-radius:2px}

@media(max-width:767px){
  .site-header{padding:20px var(--site-gutter)}
}

/* --- menu -----------------------------------------------------------------
   Two levels, matching the original: a root list plus a panel for the
   "Products and Services" folder. Measured off the live site at 1024px:
   right-aligned, Stratos 400 upright (NOT italic), 1.7rem, #1e1e1e, and
   173px of top padding so the first item clears the header.

   The header is 186px tall and sits above the menu, so the top padding is
   driven by --header-h, which site.js measures. A fixed value would clip
   "Home" behind the logo at other viewport sizes.
   -------------------------------------------------------------------------- */
.site-menu{
  position:fixed; inset:0; z-index:99; background:#fff;
  padding:calc(var(--header-h, 186px) + .4rem) var(--site-gutter) 3rem;
  overflow-y:auto; overscroll-behavior:contain;
  text-align:right;
  opacity:0; visibility:hidden; transition:opacity .25s ease;
}
.site-menu[data-open="true"]{opacity:1; visibility:visible}

.menu-level,
.menu-panel{display:flex; flex-direction:column; align-items:flex-end}
.menu-level[data-open="false"],
.menu-panel[data-open="false"]{display:none}

.menu-item{
  display:flex; align-items:center; justify-content:flex-end; gap:.35em;
  font-family:var(--font-display);
  font-size:1.7rem; font-weight:400; font-style:normal; text-transform:none;
  line-height:1.35; color:var(--ink); text-decoration:none;
  background:none; border:0; padding:.34em 0; margin:0;
  cursor:pointer; text-align:right;
}
.menu-item:hover{color:var(--purple)}
.menu-chevron{width:.62em; height:.62em; flex:none; opacity:.5}
.menu-back{opacity:.55; font-size:1.4rem}
.menu-back:hover{opacity:1}

@media (max-width:767px){
  .menu-item{font-size:1.4rem}
  .menu-back{font-size:1.15rem}
}

/* --- footer --------------------------------------------------------------- */
.site-footer{background:var(--ink);color:#fff;padding:3.5rem var(--site-gutter) 2.5rem}
.site-footer a{color:var(--mint);text-decoration:none}
.site-footer a:hover{text-decoration:underline}
.footer-inner{
  max-width:var(--site-max-width);margin:0 auto;
  display:flex;flex-wrap:wrap;gap:2rem 3rem;justify-content:space-between;
}
.footer-nav{display:flex;flex-wrap:wrap;gap:.4rem 1.6rem;list-style:none;margin:0;padding:0}
.footer-note{font-size:.85rem;opacity:.75;width:100%}

/* --- news index + articles (rebuilt by hand) ------------------------------ */
.news-list{
  max-width:var(--site-max-width);margin:0 auto;
  padding:4rem var(--site-gutter);display:grid;gap:3.5rem;
}
.news-card{display:grid;grid-template-columns:1fr;gap:1.5rem;align-items:center}
@media(min-width:768px){
  .news-card{grid-template-columns:1fr 1fr;gap:3rem}
  .news-card:nth-child(even) .news-card-media{order:2}
}
.news-card-media img{width:100%;aspect-ratio:1/1;object-fit:cover}
.news-card h2{margin-bottom:.3em}
.news-card a{text-decoration:none}
.news-meta{font-size:.8rem;text-transform:uppercase;letter-spacing:.08em;opacity:.7;margin-bottom:.6em}

.post{max-width:820px;margin:0 auto;padding:4rem var(--site-gutter)}
.post-header{margin-bottom:2.5rem}
.post-header h1{margin-bottom:.3em}
.post img{margin:2rem 0}
.post a{color:var(--purple)}
.back-link{
  display:inline-block;margin-bottom:2rem;text-decoration:none;
  font-size:.85rem;text-transform:uppercase;letter-spacing:.08em;
}

/* --- accessibility -------------------------------------------------------- */
.skip{position:absolute;left:-9999px}
.skip:focus{left:1rem;top:1rem;background:var(--mint);padding:.6rem 1rem;z-index:999}

/* --- contact form ---------------------------------------------------------
   Squarespace built its form with JS from a JSON config, so there was no
   markup to carry over. This is a plain accessible form.
   -------------------------------------------------------------------------- */
.contact-form-wrap{max-width:640px;margin:0 auto}
.contact-form .field{margin:0 0 1.4rem}
.contact-form label{
  display:block;margin-bottom:.4rem;
  font-family:var(--font-display);font-style:italic;font-weight:500;
  text-transform:uppercase;letter-spacing:.04em;font-size:.8rem;
}
.contact-form .opt{text-transform:none;font-style:normal;opacity:.6;letter-spacing:0}
.contact-form input,
.contact-form textarea{
  width:100%;box-sizing:border-box;
  font:inherit;font-family:var(--font-body);
  padding:.7rem .8rem;
  color:var(--ink);background:#fff;
  border:1px solid rgba(30,30,30,.35);border-radius:0;
}
.contact-form input:focus-visible,
.contact-form textarea:focus-visible{
  outline:2px solid var(--purple);outline-offset:1px;
  border-color:var(--purple);
}
.contact-form textarea{resize:vertical;min-height:9rem}
.contact-form input:user-invalid,
.contact-form textarea:user-invalid{border-color:#c62828}
.contact-form button{cursor:pointer}
/* honeypot: hidden from people, still fillable by naive bots */
.contact-form .field--hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.form-fallback{margin-top:1.4rem;font-size:.9rem}
.form-fallback a{color:var(--purple)}

/* submission feedback, written by site.js: empty until there is something to
   say, so it takes no vertical space on a pristine form */
.form-status:empty{display:none}
.form-status{margin-top:1rem;font-size:.9rem;font-weight:500}
.form-status--error{color:var(--purple)}
.form-status--ok{color:#0a7d55}

/* --- focus visibility ----------------------------------------------------- */
a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible{outline:2px solid var(--purple);outline-offset:2px}
.site-footer a:focus-visible,
[data-section-theme="dark-bold"] a:focus-visible{outline-color:var(--mint)}

/* --- respect reduced motion ----------------------------------------------- */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important;scroll-behavior:auto!important;
  }
}

/* --- don't let long words break the layout on narrow screens -------------- */
.sqs-html-content{overflow-wrap:break-word}

/* --- webfont fallback ------------------------------------------------------
   The carried-over Squarespace CSS declares its font custom properties as a
   bare Stratos name with NO fallback list after it, so any failure to load
   dropped the whole site to the browser's default serif.

   That used to be a live risk: the Adobe kit was domain-locked to pingnews.uk
   and owned by the Squarespace account. It is moot now that the face is
   self-hosted, but the fallback chain is real either way, so a missing file
   degrades to DM Sans rather than Times. tools/build.py rewrites those
   properties on the way out; there is nothing to detect and no marker class
   to wait for.
   -------------------------------------------------------------------------- */

/* --- heading face ------------------------------------------------------------
   Headings are set in Roboto Condensed Bold; the body copy stays IBM Plex
   Sans Condensed. Roboto Condensed measured closest of the free families to
   the Stratos both replace (+0.7% on this site's own headline against Plex's
   +7.4%), so the headings sit at close to their original width.

   Squarespace set both of these on :root to suit Stratos, a denser design, so
   the weight goes up with the family. Roboto Condensed's Bold still read
   lighter than wanted, so headings sit at Black (900); 800 is a step down if
   that proves too heavy, and costs nothing extra since the file is variable. Overridden here rather than
   rewritten in the vendor file: site.css loads after it, the properties live
   on :root, and keeping it in our own stylesheet makes it obvious this is
   Ping's decision and not Squarespace's.
   -------------------------------------------------------------------------- */
:root{
  --heading-font-font-family:"Roboto Condensed","IBM Plex Sans Condensed","DM Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
  --heading-font-font-weight:900;
}

/* --- forms carried over from Squarespace form blocks ----------------------
   Squarespace built these inputs with JS from a JSON config, so the static
   markup was an empty shell that rendered as a blank block. tools/extract.py
   now reads that config and emits real fields; this styles them.
   -------------------------------------------------------------------------- */
.ping-form .field{margin:0 0 1.2rem}
.ping-form fieldset{border:0;padding:0;margin:0 0 1.2rem}
.ping-form legend{
  font-family:var(--font-display);font-weight:500;font-size:.95rem;
  padding:0;margin-bottom:.5rem;
}
.ping-form label{display:block;margin-bottom:.35rem;font-size:.9rem}
.ping-form .req{opacity:.65;font-size:.85em}
.ping-form .field-row{display:grid;grid-template-columns:1fr 1fr;gap:1rem}
.ping-form .field-row p{margin:0}
@media (max-width:600px){.ping-form .field-row{grid-template-columns:1fr}}
.ping-form input,
.ping-form textarea{
  width:100%;box-sizing:border-box;font:inherit;font-family:var(--font-body);
  padding:.6rem .7rem;color:var(--ink);background:#fbfbfb;
  border:1px solid var(--purple);border-radius:0;
}
.ping-form input:focus-visible,
.ping-form textarea:focus-visible{outline:2px solid var(--purple);outline-offset:1px}
.ping-form textarea{resize:vertical;min-height:7rem}
.ping-form button{cursor:pointer}
.ping-form .field--hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.ping-form .form-fallback{margin-top:1rem;font-size:.85rem}

/* --- self-hosted video ---------------------------------------------------- */
.ping-video{width:100%}
.ping-video video{width:100%;height:auto;display:block;background:#000}

/* --- news listing thumbnails ----------------------------------------------
   Squarespace ships the thumbnail link collapsed:

     .blog-side-by-side .image-wrapper { height:0; overflow:hidden; position:relative }

   Its JS then set a padding-bottom percentage per image (the old aspect-ratio
   hack) and positioned the <img> to fill. Without that the link has no height,
   so every listing image was clipped to nothing and the news page showed text
   with blank gaps where the pictures should be.

   The theme's own setting is a 1:1 square thumbnail (see the
   tweak-...-image-aspect-ratio-11-square class on <body>), so express that
   directly with aspect-ratio instead.
   -------------------------------------------------------------------------- */
.blog-side-by-side .blog-image-wrapper .image-wrapper,
.blog-alternating-side-by-side .blog-image-wrapper .image-wrapper{
  height:auto;
  aspect-ratio:1/1;
  display:block;
  padding-bottom:0;
}
.blog-side-by-side .blog-image-wrapper .image-wrapper img,
.blog-alternating-side-by-side .blog-image-wrapper .image-wrapper img{
  width:100%;
  height:100%;
  object-fit:cover;
  display:block;
}

/* --- shape blocks ---------------------------------------------------------
   Shape blocks are an <svg> inside a block sized by the Fluid Engine grid.
   The markup carries data-stretched-to-fill, and Squarespace's JS resized the
   SVG to fill its container. Without that JS the SVG falls back to the
   browser default of 300x150, so the full-height colour columns on
   /syndication rendered as small rectangles floating over the text.
   -------------------------------------------------------------------------- */
.sqs-block[data-definition-name="website.components.shape"] .sqs-shape-block-container{
  width:100%;
  height:100%;
}
.sqs-block[data-definition-name="website.components.shape"] .sqs-shape-block-container > svg,
.sqs-block[data-definition-name="website.components.shape"] svg.sqs-shape{
  width:100%;
  height:100%;
  display:block;
}

/* --- testimonials ---------------------------------------------------------
   DESIGN CHANGE, not a fidelity fix. The original renders these as ordinary
   paragraphs wrapped in <strong><em>, which reads as a wall of bold purple
   italic. Same content, presented as a quote.
   -------------------------------------------------------------------------- */
.ping-testimonial{
  position:relative;
  margin:0;
  padding:1.6rem 0 0 2.6rem;
  border-left:3px solid var(--mint);
  padding-left:2rem;
}
.ping-testimonial::before{
  content:"\201C";
  position:absolute;
  left:1rem; top:-.3rem;
  font-family:var(--font-display);
  font-size:4.5rem; line-height:1;
  color:var(--mint);
  pointer-events:none;
}
.ping-testimonial p{
  color:var(--ink);
  margin:0 0 1rem;
}
/* let the design carry the emphasis, not bold italic on every line */
.ping-testimonial p strong,
.ping-testimonial p em{
  font-weight:400;
  font-style:normal;
  color:inherit;
}
.ping-testimonial .ping-cite{
  margin-top:1.2rem;
  font-family:var(--font-display);
  font-style:italic; font-weight:500;
  font-size:.95rem; letter-spacing:.01em;
  color:var(--ink);
}
.ping-testimonial .ping-cite strong{font-weight:500}
.ping-testimonial .ping-cite a{color:var(--purple)}
@media (max-width:600px){
  .ping-testimonial{padding-left:1.4rem}
  .ping-testimonial::before{font-size:3.2rem;left:.4rem}
}

/* --- history timeline -----------------------------------------------------
   Drives src/js/timeline.js. Replaces the Code&Tonic jQuery plugin, whose
   spine rendered at 0px high so the page had no line at all.
   -------------------------------------------------------------------------- */
.ping-timeline-spine{position:relative}

/* The entry reveal slides items in from their own side with
   translateX(±26px). getBoundingClientRect includes transforms, so a
   full-width row shifted 26px right made the PAGE 26px wider until the entry
   revealed — horizontal overflow at every width where the slide applies.
   Clip it at the section: the movement still reads, but it cannot widen the
   document. */
.ping-timeline{overflow-x:clip}

.ping-spine-track{
  position:absolute; top:0; bottom:0; left:50%;
  width:2px; margin-left:-1px;
  background:rgba(30,30,30,.16);
}
.ping-spine-fill{
  position:absolute; top:0; left:0; width:100%; height:0;
  background:var(--ink);
  transition:height .18s linear;
}
.ping-spine-dot{
  position:absolute; left:50%;
  width:11px; height:11px; margin-left:-5.5px; margin-top:-5.5px;
  border-radius:50%;
  background:#fff;
  box-shadow:0 0 0 2px rgba(30,30,30,.25);
  transition:background .3s ease, box-shadow .3s ease, transform .3s ease;
}
.ping-spine-dot.is-on{
  background:var(--mint);
  box-shadow:0 0 0 2px var(--ink);
  transform:scale(1.35);
}

/* entries arrive from their own side of the line */
.ping-tl-entry{
  opacity:0;
  transition:opacity .55s ease, transform .55s cubic-bezier(.22,1,.36,1);
  will-change:opacity, transform;
}
.ping-tl-left{transform:translateX(-26px)}
.ping-tl-right{transform:translateX(26px)}
.ping-tl-entry.is-in{opacity:1; transform:translateX(0)}

@media (prefers-reduced-motion:reduce){
  .ping-tl-entry{opacity:1!important; transform:none!important; transition:none}
  .ping-spine-fill{transition:none}
}
@media (max-width:767px){
  .ping-spine-dot{width:9px;height:9px;margin-left:-4.5px;margin-top:-4.5px}
  .ping-tl-left,.ping-tl-right{transform:translateY(18px)}
}

/* --- extra history entries ------------------------------------------------
   Appended from src/history-additions.json, matching the captured entries:
   left of the spine is right-aligned, right of the spine is left-aligned.
   -------------------------------------------------------------------------- */
.ping-tl-extra{
  list-style:none; margin:0; padding:0;
  /* never exceed the section's content box: the <ol> sits inside a gutter, so
     a fixed max-width let it run 26px past the viewport at exactly 768px */
  width:100%; max-width:100%; box-sizing:border-box;
}
.ping-tl-extra li{
  display:grid;
  grid-template-columns:1fr 1fr;
  column-gap:9%;
  margin:0 0 3.5rem;
  width:100%; max-width:100%; box-sizing:border-box;
  min-width:0;
}
.ping-tl-extra li > div{min-width:0}
.ping-tl-extra .ping-tl-left > div{grid-column:1; text-align:right}
.ping-tl-extra .ping-tl-right > div{grid-column:2; text-align:left}
/* the captured entries put 32px between the date and its body copy
   (their h2 carries margin-bottom:32px, which absorbs the paragraph's own
   16px top margin). .5em only gave 16.64px here, so the appended entries
   read tighter than the rest of the timeline. */
.ping-tl-extra h2{margin:0 0 2rem}
.ping-tl-extra p{margin:0}

@media (max-width:767px){
  .ping-tl-extra li{grid-template-columns:1fr; column-gap:0}
  .ping-tl-extra .ping-tl-left > div,
  .ping-tl-extra .ping-tl-right > div{grid-column:1; text-align:left}
}

/* --- text colour for generated content ------------------------------------
   The captured Squarespace content sets its body colour per element, wrapping
   text in <span class="sqsrte-text-color--darkAccent">. The <p> underneath is
   left at the section theme's paragraph colour, which in this palette is
   PURPLE (--black-hsl is #7300FA, not black).

   So anything we generate — timeline entries, forms, the 404 — falls through
   to purple and looks wrong beside the captured text, which renders ink.
   Rather than sprinkle colour spans through generated markup, set it once
   here. Dates and headings keep the theme colour, which is correct.
   -------------------------------------------------------------------------- */
.ping-tl-extra p,
.ping-tl-extra li,
.ping-form label,
.ping-form .form-fallback,
.contact-form label,
.contact-form .form-fallback,
.ping-testimonial p{
  color:var(--ink);
}
/* on dark sections the same content must invert */
[data-section-theme="dark"] .ping-tl-extra p,
[data-section-theme="dark-bold"] .ping-tl-extra p,
[data-section-theme="black"] .ping-tl-extra p,
[data-section-theme="dark"] .ping-form label,
[data-section-theme="dark-bold"] .ping-form label,
[data-section-theme="black"] .ping-form label{
  color:#fff;
}

/* --- news card hover ------------------------------------------------------
   A deliberate design addition, not in the original. The brand is flat, bold
   and geometric, so the card uses a hard offset block rather than a soft
   shadow, and colour switches rather than fades.

   The whole card is clickable via a stretched link on the title, which keeps
   one meaningful link per card for screen readers and keyboard users. The
   same treatment applies on :focus-within so keyboard users see it too.
   -------------------------------------------------------------------------- */
.blog-side-by-side .blog-item{
  position:relative;
  background:transparent;
  padding:1.25rem;
  margin-left:-1.25rem; margin-right:-1.25rem;
  transition:transform .18s cubic-bezier(.2,.8,.2,1),
             background-color .18s ease,
             box-shadow .18s cubic-bezier(.2,.8,.2,1);
}
/* stretched link: the title anchor covers the card */
.blog-side-by-side .blog-title a::after{
  content:""; position:absolute; inset:0; z-index:3;
}
.blog-side-by-side .blog-item:hover,
.blog-side-by-side .blog-item:focus-within{
  background:#fff;
  transform:translate(-6px,-6px);
  box-shadow:14px 14px 0 0 var(--mint);
}

/* image sits in a square frame and pushes in on hover */
.blog-side-by-side .blog-image-wrapper .image-wrapper{overflow:hidden}
.blog-side-by-side .blog-image-wrapper .image-wrapper img{
  transition:transform .35s cubic-bezier(.2,.8,.2,1);
}
.blog-side-by-side .blog-item:hover .image-wrapper img,
.blog-side-by-side .blog-item:focus-within .image-wrapper img{
  transform:scale(1.06);
}

/* title switches to purple, no fade */
.blog-side-by-side .blog-title a{
  transition:color .12s linear;
  text-decoration:none;
}
.blog-side-by-side .blog-item:hover .blog-title a,
.blog-side-by-side .blog-item:focus-within .blog-title a{
  color:var(--purple);
}

/* Read More becomes a solid block with a nudging arrow */
.blog-side-by-side .blog-more-link{
  position:relative; z-index:4;
  display:inline-flex; align-items:center; gap:.5em;
  font-family:var(--font-display);
  font-style:italic; font-weight:500; text-transform:uppercase;
  font-size:.78rem; letter-spacing:.06em; text-decoration:none;
  color:var(--ink); background:transparent;
  padding:.62em 1em;
  box-shadow:inset 0 0 0 2px var(--ink);
  transition:background-color .14s linear, color .14s linear,
             box-shadow .14s linear, transform .18s cubic-bezier(.2,.8,.2,1);
}
.blog-side-by-side .blog-more-link::after{
  content:"\2192";
  transition:transform .22s cubic-bezier(.2,.8,.2,1);
}
.blog-side-by-side .blog-item:hover .blog-more-link,
.blog-side-by-side .blog-item:focus-within .blog-more-link{
  background:var(--ink); color:#fff; box-shadow:inset 0 0 0 2px var(--ink);
}
.blog-side-by-side .blog-item:hover .blog-more-link::after,
.blog-side-by-side .blog-item:focus-within .blog-more-link::after{
  transform:translateX(5px);
}
.blog-side-by-side .blog-more-link:focus-visible{
  outline:2px solid var(--purple); outline-offset:3px;
}

@media (prefers-reduced-motion:reduce){
  .blog-side-by-side .blog-item,
  .blog-side-by-side .blog-item:hover,
  .blog-side-by-side .blog-item:focus-within{transform:none; transition:none}
  .blog-side-by-side .blog-image-wrapper .image-wrapper img,
  .blog-side-by-side .blog-more-link::after{transition:none; transform:none!important}
}
@media (max-width:767px){
  .blog-side-by-side .blog-item:hover,
  .blog-side-by-side .blog-item:focus-within{
    transform:none; box-shadow:8px 8px 0 0 var(--mint);
  }
}

/* --- history timeline: mobile ---------------------------------------------
   Measured against the original at 375px: the spine sits at the LEFT edge
   (x=23), entries start at x=64 and their text is left-aligned. Ours kept the
   desktop centre spine (x=187), so the line ran straight through the text,
   and the appended entries had no gutter at all (x=0, full bleed).
   -------------------------------------------------------------------------- */
@media (max-width:767px){
  .ping-spine-track{left:23px; margin-left:0}
  .ping-spine-dot{left:23px}

  /* the captured entries carry inline text-align:right for the desktop
     two-column layout; the original switches them to left on mobile */
  .ping-timeline .fe-block.ping-tl-entry :is(h1,h2,h3,h4,p,ul,ol){
    text-align:left !important;
  }

  /* On mobile the captured entries stop stacking with any trailing space, so
     the first appended date collided with the last captured paragraph (a 0px
     seam). Carry the same 3.5rem the appended entries use between themselves.
     Desktop needs nothing: the captured blocks alternate sides there and
     already leave 28px. */
  .ping-tl-extra{margin-top:3.5rem}

  /* appended entries: clear the spine and pick up the page gutter */
  .ping-tl-extra li{
    padding-left:64px;
    padding-right:var(--site-gutter);
  }
  .ping-tl-extra .ping-tl-left > div,
  .ping-tl-extra .ping-tl-right > div{text-align:left}
}

/* --- cookie consent --------------------------------------------------------
   Google Analytics loads with Consent Mode denied by default, so this banner
   is what actually turns it on. It sits at the bottom, does not trap the page
   behind an overlay (nothing here is worth blocking the news for), and the
   choice is remembered in localStorage.
   -------------------------------------------------------------------------- */
.cookie-banner{
  position:fixed;left:0;right:0;bottom:0;z-index:9000;
  background:var(--ink,#1E1E1E);color:#fff;
  padding:1rem var(--site-gutter,1.25rem);
  box-shadow:0 -2px 14px rgba(0,0,0,.18);
}
.cookie-banner[hidden]{display:none}
.cookie-banner__inner{
  max-width:1100px;margin:0 auto;
  display:flex;gap:1.25rem;align-items:center;justify-content:space-between;
  flex-wrap:wrap;
}
.cookie-banner__text{
  margin:0;font-size:.9rem;line-height:1.5;max-width:64ch;
}
.cookie-banner__text strong{display:block;font-family:var(--font-display);
  font-size:1rem;letter-spacing:.02em;margin-bottom:.2rem}
.cookie-banner__text a{color:var(--mint,#00FFBE);text-decoration:underline}
.cookie-banner__actions{display:flex;gap:.6rem;flex-shrink:0}
.cookie-btn{
  font-family:var(--font-display);font-weight:600;font-size:.85rem;
  letter-spacing:.04em;text-transform:uppercase;
  padding:.65rem 1.4rem;border:2px solid transparent;cursor:pointer;
}
.cookie-btn--accept{background:var(--mint,#00FFBE);color:var(--ink,#1E1E1E)}
.cookie-btn--reject{background:transparent;color:#fff;border-color:rgba(255,255,255,.5)}
.cookie-btn:focus-visible{outline:2px solid var(--mint,#00FFBE);outline-offset:3px}

/* the way back in, for anyone who changes their mind */
.cookie-settings-line{margin:0;padding:0 var(--site-gutter,1.25rem) 1.25rem;text-align:center}
.cookie-settings-link{
  background:none;border:0;padding:.25rem;cursor:pointer;
  font-size:.78rem;color:inherit;opacity:.6;text-decoration:underline;
}
.cookie-settings-link:hover{opacity:1}

@media (max-width:600px){
  .cookie-banner__inner{flex-direction:column;align-items:flex-start}
  .cookie-banner__actions{width:100%}
  .cookie-btn{flex:1}
}
