/* ==========================================================================
   Blog index supplement — loads after the Webflow stylesheet.

   Almost every rule here is a correction rather than an invention. The blog is the only
   listing on pi.security whose cards have no chrome (/customers already ships the house
   card), and it carried three live contrast failures where 11px and 12px text was set in
   --base-colors--cold-mid-grey. That colour is deleted from this page entirely: it
   measures 3.12:1 on snow-white and 3.00:1 on the tag pill's light-cyan, and every use of
   it here was below 24px.

   ONE RULE GOVERNS THE WHOLE FILE. This sheet loads AFTER the Webflow stylesheet, and
   media queries add no specificity. So any override of a property that Webflow declares
   inside a max-width query must itself sit inside @media (min-width: 992px), or it wins
   at every width and silently breaks mobile. That bites three declarations, marked below.
   ========================================================================== */

/* --- masthead -------------------------------------------------------------------

   Promoted from h2 to h1: the page had no h1 at all. The size is unchanged because
   .heading-style-h2 is the site's own utility (YTF Giong 400 at 3.125rem, stepping to
   2rem at 767px), so the promotion is semantic and the responsive step comes free.

   The wordmark is the one place orange is allowed to be type. #ff7329 on #fefefe is
   2.69:1, which fails even the 3:1 large-text bar; it is legal only because WCAG 1.4.3
   exempts logotypes and "Off-Piste" is this blog's logotype. Everywhere else on this page
   orange appears as the tag square (a bullet, not text) or a chevron beside navy text. */

.blog_heading {
  gap: 0;                       /* the wordmark is the only child now */
  margin-bottom: 3.5rem;
  padding-bottom: 0;
  /* No rule under the wordmark. With the tagline and archive line gone it was closing a
     block that had nothing in it, and it doubled up with the hairline under the lead a
     little way below. The one under the lead is the one doing structural work. */
  border-bottom: 0;
}

/* .blog_archive and the tagline are gone. Nothing replaces Wiz's filter row: the masthead
   is the wordmark and a rule, and the lead post does the rest of the orienting. */

/* --- the card ------------------------------------------------------------------

   Chrome lifted from .cidx_feature on /customers, so the blog card and the customers card
   are the same object. Two deliberate divergences, stated rather than glossed:

   Ground is --backgrounds--primary (snow-white), not .cidx_feature's --backgrounds--
   secondary (pure white), because snow-white is this page's ground.

   The focus ring is navy, NOT .cidx_feature's gem-teal. #00ccd3 on #fefefe is 1.97:1,
   which fails WCAG 2.2 SC 1.4.11's 3:1 for a focus indicator; navy is 16.69:1. Teal stays
   as the hover border, where it is supplementary to the lift. Copying the teal ring would
   have been consistent, not correct, and /customers has the same defect.            */

/* No box. Each post is an item on the page ground, not a card in a frame.

   A boxed card asks the eye to read the container before the content, and with five
   items it made the page look like a grid waiting to be filled. The structure now comes
   from the image edge, the type hierarchy and the space between items, which is what a
   contents page does. The one piece of chrome that stays is a hairline on the IMAGE:
   several of the watercolours are near-white on a near-white ground and would otherwise
   dissolve into the page. */

.blog_card {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  overflow: visible;            /* the base rule clips; nothing needs clipping now */
}

.blog_image.is-list {
  /* 16/9, matching the lead. At 3/2 the feed images rendered 357px tall against the lead's
     293px, so the secondary cards were physically LARGER than the one they sit under and the
     picture took 63% of the card. This also stops the Bugcrowd cover being side-cropped: it
     is natively 16/9, and a logo lockup is the one cover that must not lose its edges. The
     three watercolours are 3/2 and give up 16% of their height, which their centred subjects
     and wide margins absorb without touching the subject. */
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--_sizes---border-radium--medium, .5rem);
  border: 1px solid var(--borders--primary);
  transition: border-color .25s ease;
}

/* Hover has to be signalled without a container to recolour. The title takes a rule (the
   editorial gesture, and it reads as "this is a link"), and the image edge warms. Both
   are colour and neither moves the layout, so nothing reflows under the pointer. */
.blog_card:hover .blog_title { text-decoration: underline; text-underline-offset: .15em; }
.blog_card:hover .blog_image.is-list { border-color: var(--base-colors--gem-teal); }

.blog_card:focus-visible {
  outline: 2px solid var(--text--secondary);
  outline-offset: 6px;          /* clears the image edge now the padding is gone */
  border-radius: var(--_sizes---border-radium--medium, .5rem);
}

@media (prefers-reduced-motion: reduce) {
  .blog_image.is-list { transition: none; }
}

.blog_details { padding-top: 1.25rem; }

/* .blog_content must FILL .blog_details, not just sit in it.

   Webflow gives it margin-bottom: auto, which was right for a box whose height came from
   its content. In an equal-height grid row it is the bug: the auto margin resolves to
   whatever slack the row has (measured 33.6px on the shorter card, 0 on the taller), so
   the shorter card's block floats up and its byline lands 33px above its neighbour's.
   The byline's own margin-top: auto could not correct it, because a flex: 0 1 auto parent
   has no free space for an auto margin to consume. */
.blog_content {
  gap: .625rem;
  flex: 1;
  margin-bottom: 0;
}

/* Unboxed items need the gap to do the separating that the borders used to do. */
.blog_collection-list:not(.is-featured) { column-gap: 3rem; row-gap: 3.5rem; }

/* Webflow puts 5.5rem under the lead's list wrapper, which used to sit below a boxed card
   that had no margin of its own. The unboxed lead now carries its own padding and margin
   around its closing hairline, so the two stacked to 144px of dead air. Declared in
   Webflow's base rule, so overriding it here needs no media guard. */
.blog_collection-list.is-featured { margin-bottom: 0; }

/* --- badges ---------------------------------------------------------------------

   A slim row under the title. Rebuilt rather than reusing .blog_tag-wrapper: that pill is
   1.25rem of horizontal padding with a 1rem gap, sized to stand alone above a title, and
   two of them side by side under one read as buttons. These are half the padding, so they
   read as metadata.

   Colour is --text--primary, not --base-colors--cold-mid-grey: on the pill's light-cyan
   ground cold-mid-grey measures 3.00:1 at this size, against a 4.5:1 requirement. */

.blog_badges {
  display: flex;
  flex-wrap: wrap;
  gap: .375rem;
  margin-top: -.125rem;
}

.blog_badge {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  padding: .1875rem .5rem;
  border-radius: var(--_sizes---border-radium--small, .25rem);
  background-color: var(--base-colors--light-cyan);
  color: var(--text--primary);
  font-family: "YTF millie mono", monospace;
  font-size: var(--_fonts---body--xxsmall);
  letter-spacing: var(--_fonts---letter-spacing--large);
  line-height: 1.45;
  text-transform: uppercase;
  white-space: nowrap;
}

/* The square denotes a category on this site, so a duration does not get one. */
.blog_badge.is-plain { background-color: var(--base-colors--cold-grey); }

.blog_badge-dot {
  flex: none;
  width: .5rem;
  height: .5rem;
  border-radius: 1px;
  background-color: var(--base-colors--orange);
}

/* The byline stays.

   Adapted from .rsr_byline rather than lifted: a card merges the research page's separate
   role and meta elements onto one line. .blog_author's own colour moves off
   --base-colors--cold-mid-grey (3.12:1 at 12px, a live failure) onto --text--primary. */

.blog_author {
  gap: .75rem;
  color: var(--text--primary);
  align-items: center;
  margin-top: auto;             /* pins the byline down so cards in a row align */
  padding-top: 1rem;
}

/* Avatars overlap when a post has more than one author: the convention for a shared
   byline, and it keeps the row the same height however many names there are. The ring is
   the page ground rather than the sky border, so the discs read as stacked cards. */
.blog_avatars { display: flex; flex: none; }

.blog_author .author_image {
  width: 2.5rem;
  box-shadow: 0 0 0 2px var(--backgrounds--primary);
}

.blog_avatars .author_image + .author_image { margin-left: -.75rem; }

.blog_card.is-featured .author_image { width: 2.75rem; }
.blog_card.is-featured .blog_avatars .author_image + .author_image { margin-left: -.875rem; }

.author_details { gap: .0625rem; }

/* Name over date, both in the body face. Mono uppercase is Pi's idiom for LABELS, and a
   person's name is not a label: at 11px with .11em tracking it read as a category. */

.blog_author-name {
  font-family: "YTF millie", sans-serif;
  font-weight: 500;
  font-size: var(--_fonts---body--regular);
  line-height: 1.35;
  color: var(--text--secondary);
}

.blog_author-meta {
  font-family: "YTF millie", sans-serif;
  font-size: var(--_fonts---body--small);
  line-height: 1.35;
  color: var(--text--primary);
}

/* Decorative, aria-hidden, so SC 1.4.3 does not apply. It takes cold-mid-grey on purpose:
   quieter than the text it separates, but present. --borders--secondary was tried on the
   research page and measured 1.32:1, which is not quiet, it is invisible. The rule was
   lost when the byline block was rewritten, which left the middot with no spacing. */
.blog_dot { color: var(--base-colors--cold-mid-grey); margin: 0 .4em; }

/* .blog_title carries display:flex with column flow, centring and 2rem gaps, a Webflow
   artifact doing nothing useful to a text node. */
.blog_title {
  display: block;
  color: var(--text--secondary);
  margin: 0;
}

/* POST TITLES ARE MILLIE, NOT GIONG.

   Giong ships weight 400 and nothing else: there is no bold file, so asking for one gets
   browser-synthesised faux bold, which smears the stems. Giong 400 at card size is
   genuinely hard to scan, which is the complaint. Millie has real 300/400/500/700, so 500
   is an actual cut rather than a simulation, and it is the same call already made for the
   /research/babel headline. Giong keeps the one job it is good at on this page: the
   Off-Piste wordmark.

   Size is set for the CARD, not the page. The feed is two-up, so cards are 536px at 1440,
   much wider than the three-up grid the original h5 was picked for. At 28px the longest
   title ran the full width edge to edge and read as crowding the picture, even though it
   was flush with it and never actually overflowed (measured: title right edge 1280, image
   right edge 1280). */

.blog_collection-list:not(.is-featured) .blog_title {
  font-family: "YTF millie", sans-serif;
  font-weight: 500;
  font-size: 1.375rem;
  line-height: 1.3;
  letter-spacing: -.01em;
}

/* Left at the body size rather than stepped down: this is the line that has to be read,
   and shrinking it to gain room would have traded the wrong thing. */
.blog_collection-list:not(.is-featured) .blog_excerpt {
  font-size: var(--_fonts---body--regular);
  line-height: 1.55;
}

/* The lead headline takes the same treatment, so the two do not read as different
   publications stacked on one page. */
.blog_card.is-featured .blog_title {
  font-family: "YTF millie", sans-serif;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -.015em;
}

.blog_excerpt {
  font-size: var(--_fonts---body--regular);
  line-height: var(--_fonts---line-height--large);
  color: var(--text--primary);
  margin: 0;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}

/* --- the lead card --------------------------------------------------------------

   .is-shadow is gone from the markup: a seven-layer blue-tinted shadow that appears
   nowhere else on the site. The site's idiom is a hairline.                          */

/* The lead loses its tinted panel too. It was the largest box on the page, and it made
   the most important post look like an advertisement inserted into the page rather than
   the top of the page. It now outranks the feed by size and position alone: a bigger
   image, a bigger headline, and a hairline underneath separating it from the feed. */

/* Webflow keeps the .is-shadow class on the featured card; the generated page drops it
   from the markup instead. Same intent, expressed where each surface needs it: locally
   this rule matches nothing, on Webflow it removes the seven-layer blue shadow that the
   unboxed lead is meant to lose along with its tinted panel. */
.blog_card.is-shadow { box-shadow: none; }

.blog_card.is-featured {
  background-image: none;
  border: 0;
  border-bottom: 1px solid var(--borders--primary);
  border-radius: 0;
  padding: 0 0 3.5rem;
  margin-bottom: 3.5rem;
  overflow: visible;
}

.blog_card.is-featured .blog_image {
  border: 1px solid var(--borders--primary);
  transition: border-color .25s ease;
}

.blog_card.is-featured:hover .blog_image { border-color: var(--base-colors--gem-teal); }

.blog_card.is-featured .blog_image { border-radius: var(--_sizes---border-radium--medium, .5rem); }

/* Headline size ladder, set so the 92-character title holds THREE lines at every target
   width and never needs a clamp. h4 is the default rather than h3: Webflow's own grid on
   this card applies from 768px up, so the text column is already narrow at 991px, where
   h3 measured four lines. h3 is then opted into only where the column is wide enough. */
.blog_card.is-featured .blog_title { font-size: var(--_fonts---headings--h4); }

.blog_card.is-featured .blog_excerpt {
  /* body--regular, not body--large: at 18px this ran four lines and was the second
     biggest contributor to the text column overhanging the picture. */
  font-size: var(--_fonts---body--regular);
  /* Not clamped. Measured, the excerpt runs four lines here and five below 992px, so a
     three-line clamp would cut "AWS rated CVE-2026-22611 low severity", which is the
     sting of the post. One hand-authored string on one card: nothing to guard against. */
  -webkit-line-clamp: none;
  display: block;
  overflow: visible;
}


/* The lead's text link. Its inherited colour is cold-mid-grey, which measures 2.94:1 on
   this card's gradient: the third live failure, and the one an audit run against the page
   ground would miss entirely. Only the chevron takes the accent. */
/* Qualified with .blog_card on purpose. The element carries .button.is-text, which is
   (0,2,0); a bare .blog_cta is (0,1,0) and loses, so the colour override silently did
   nothing and the link stayed at 2.86:1. Measured, not assumed. */
.blog_card .blog_cta {
  color: var(--text--secondary);
  margin-top: 1.25rem;
  align-self: flex-start;
}

.blog_card .blog_cta .icon-small { color: var(--text--accent); }

/* --- layout: desktop only -------------------------------------------------------

   EVERYTHING IN THIS BLOCK IS GUARDED. Webflow declares grid-template-columns on
   .blog_collection-list and side padding on .blog_details.is-featured inside its own
   max-width queries. An unguarded override here would beat them at every width, because
   this sheet loads later and media queries add no specificity, and the feed would stay
   two-up on a phone.                                                                  */

/* The lead is two-column from 768px up: that is where Webflow stops setting display:block
   on it. So the column split belongs at that breakpoint, NOT at 992. Guarding it to 992
   left 768 to 991 on Webflow's equal 1fr/1fr, which gave a 375px text column where the
   headline ran four lines. Webflow declares these columns in its BASE rule, so overriding
   them outside a max-width query is safe. */

@media screen and (min-width: 768px) {
  /* Text keeps the larger share. Tried equal columns to buy the image 22px of height and
     it cost more than it gained: the text column narrowed from 599px to 560px, the
     headline went back to four lines, and the overhang got WORSE (91px to 107px). */
  .blog_card.is-featured {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
    gap: 0;
    /* Top-aligned, not centred: the headline starts on the same line as the top of the
       picture, so the two columns share a baseline the eye can find. Centring floated the
       text against nothing and left the title starting somewhere arbitrary. */
    align-items: start;
  }

  .blog_card.is-featured .blog_image {
    aspect-ratio: 16 / 9;       /* native 1672x941, so no crop of the labelled cells */
    object-fit: cover;
    height: auto;
  }
}

/* The feed is inset from the lead rather than matching it. At container width the
   secondary cards were 536px wide with a 302px image, which read as four more leads rather
   than a feed under one. 440 + 48 gap + 440 = 928 keeps the two-up grid and the 16/9 ratio,
   so nothing is cropped further; the picture simply stops being the loudest thing on a card
   that is meant to be scanned. The lead keeps the full 1120 and now clearly outranks it. */
@media screen and (min-width: 992px) {
  .blog_collection-list:not(.is-featured) {
    max-width: 928px;
    margin-inline: auto;
  }
}

@media screen and (min-width: 992px) {
  /* GUARDED, and it has to be: Webflow declares .blog_card.is-featured padding and
     .blog_details.is-featured side padding inside its own max-width:991 and 767 queries.
     Media queries add no specificity, and this sheet loads later, so an unguarded rule
     here would win at every width and the phone layout would inherit desktop padding. */
  /* The lead is unboxed now, so only the text column needs insetting from the image. */
  /* Webflow sets justify-content: center on this column, which would keep the text block
     floating in the middle of a track that is now start-aligned. Declared in its base
     rule, so no media guard needed. */
  .blog_details.is-featured { padding: 0 0 0 3.5rem; justify-content: flex-start; }

  /* "If there is no fifth item, go two-up." Four cards in a three-column grid strand one
     on its own row; two-up gives four a balanced block and a 60-character excerpt instead
     of 34. It releases itself when a fifth feed post publishes, which then gives 3 + 2. */
  .blog_collection-list:not(.is-featured):not(:has(> :nth-child(5))) {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* No h3 step any more. That size was picked while the title was Giong; Millie sets wider
   at the same nominal size, so 40px ran to FOUR lines (192px of title against a 293px
   image) and the text column overhung the picture by 168px. h4 holds it at three. */

@media screen and (max-width: 767px) {
  /* Webflow already flips the lead to display:block and zeroes the details side padding
     here, which is right once the card supplies its own. */
  .blog_card.is-featured .blog_image { aspect-ratio: 16 / 9; object-fit: cover; }
  .blog_card.is-featured .blog_title { font-size: var(--_fonts---headings--h5); }
  .blog_card.is-featured .blog_excerpt { font-size: var(--_fonts---body--regular); }
}

@media screen and (max-width: 479px) {
  .blog_card { padding: .75rem; }
  .blog_card.is-featured .blog_title { font-size: var(--_fonts---headings--h6); }
  /* the feed title is already 1.375rem from the rule above; h6 here would step it UP */
  .blog_collection-list:not(.is-featured) .blog_title { font-size: 1.25rem; }
  /* Each card is alone on its row, so nothing needs to align and the excerpt can run on.
     "Why we built Pi" is the one that needs the fourth line at this width. */
  .blog_excerpt { -webkit-line-clamp: 4; }
}
