/* ABOUTME: Styles for the blog components, content blocks and post-body prose; tokens come from style.css and no Global Class is assumed. */

/* Specificity notes. Prose element rules sit in :where() so a component class always out-ranks them, whatever the load order.
   Form controls restate hover and focus at three-class strength because Hello's reset recolours [type=submit] on both. */

/* ---------- Breadcrumbs ---------- */
.ffdh-breadcrumbs {
  color: var(--c-text-muted);
  font-size: var(--fs-small);
  line-height: 1.5;
}
.ffdh-breadcrumbs a {
  color: var(--c-text-subtle);
  text-underline-offset: 3px;
}
.ffdh-breadcrumbs a:hover {
  color: var(--c-fuchsia-700);
}
.ffdh-breadcrumbs .breadcrumb_last {
  color: var(--c-text-body);
}

/* ---------- Post category and byline ---------- */
.ffdh-post-category {
  margin: 0;
}
.ffdh-post-category__link {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  padding-inline: var(--sp-3);
  border-radius: var(--r-pill);
  background: var(--c-fuchsia-50);
  color: var(--c-fuchsia-700);
  font-size: var(--fs-small);
  font-weight: 600;
  text-decoration: none;
  transition: background-color var(--dur-fast) var(--ease-out);
}
.ffdh-post-category__link:hover {
  background: var(--c-fuchsia-100);
}

.ffdh-byline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-1) var(--sp-3);
  margin: 0;
  color: var(--c-text-muted);
  font-size: var(--fs-small);
  line-height: 1.5;
}
.ffdh-byline img {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
}
.ffdh-byline a {
  color: var(--c-text-body);
  font-weight: 600;
  text-decoration: none;
}
.ffdh-byline a:hover {
  color: var(--c-fuchsia-700);
  text-decoration: underline;
}
/* Separators are drawn here, never typed. They trail every item but the last, so a wrapped line can end with a dot but never begin with one. */
.ffdh-byline__item:has(~ .ffdh-byline__item)::after {
  content: "";
  display: inline-block;
  width: 4px;
  height: 4px;
  margin-inline-start: var(--sp-3);
  border-radius: 50%;
  background: var(--c-border-strong);
  vertical-align: middle;
}
/* On phones the avatar sits beside a stacked list instead of leaving a dot at the start of a wrapped line. */
@media (max-width: 519px) {
  .ffdh-byline {
    display: grid;
    grid-template-columns: auto 1fr;
    column-gap: var(--sp-3);
    align-items: center;
  }
  .ffdh-byline img {
    grid-row: 1 / span 4;
  }
  .ffdh-byline__item::after {
    display: none;
  }
}

/* ---------- Author box ---------- */
.ffdh-author-box {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-5);
  margin-block: var(--sp-9);
  padding: var(--sp-6);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  background: var(--c-surface-ghost);
}
.ffdh-author-box > img {
  flex: none;
  width: 96px;
  height: 96px;
  border-radius: 50%;
  object-fit: cover;
}
.ffdh-author-box__body {
  display: grid;
  gap: var(--sp-1);
  min-width: 0;
}
.ffdh-author-box .ffdh-author-box__eyebrow,
.ffdh-author-box .ffdh-author-box__title,
.ffdh-author-box .ffdh-author-box__bio {
  margin: 0;
}
.ffdh-author-box .ffdh-author-box__eyebrow {
  color: var(--c-text-muted);
  font-size: var(--fs-small);
  font-weight: 500;
}
.ffdh-author-box .ffdh-author-box__name {
  margin: 0;
  font-size: var(--fs-h4);
  font-weight: 600;
  line-height: 1.3;
}
.ffdh-author-box__name a {
  color: var(--c-ink-900);
  text-decoration: none;
}
.ffdh-author-box__name a:hover {
  color: var(--c-fuchsia-700);
  text-decoration: underline;
}
.ffdh-author-box .ffdh-author-box__title {
  color: var(--c-fuchsia-700);
  font-size: var(--fs-small);
  font-weight: 600;
}
.ffdh-author-box .ffdh-author-box__bio {
  margin-top: var(--sp-2);
  max-width: var(--measure);
  color: var(--c-text-subtle);
}
@media (max-width: 519px) {
  .ffdh-author-box {
    flex-direction: column;
    padding: var(--sp-5);
  }
}

/* ---------- Post cards ---------- */
.ffdh-post-card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  background: var(--c-surface);
  box-shadow: var(--sh-sm);
  transition: transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.ffdh-post-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--sh-brand);
}
/* The title link stretches over the whole card; the media link is aria-hidden and out of the tab order. */
.ffdh-post-card:has(.ffdh-post-card__title a:focus-visible) {
  box-shadow: var(--ring);
}
.ffdh-post-card .ffdh-post-card__title a:focus-visible {
  box-shadow: none;
}
.ffdh-post-card__media {
  display: block;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--c-surface-mist);
}
.ffdh-post-card__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.ffdh-post-card__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-5);
}
.ffdh-post-card .ffdh-post-card__category,
.ffdh-post-card .ffdh-post-card__excerpt,
.ffdh-post-card .ffdh-post-card__meta {
  margin: 0;
}
.ffdh-post-card .ffdh-post-card__category {
  font-size: var(--fs-small);
  font-weight: 600;
  line-height: 1.4;
}
.ffdh-post-card__category a {
  position: relative;
  z-index: 1;
  color: var(--c-fuchsia-700);
  text-decoration: none;
}
.ffdh-post-card__category a:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}
.ffdh-post-card .ffdh-post-card__title {
  margin: 0;
  font-size: var(--fs-h4);
  font-weight: 600;
  line-height: 1.3;
}
.ffdh-post-card__title a {
  color: var(--c-ink-900);
  text-decoration: none;
}
.ffdh-post-card__title a::after {
  content: "";
  position: absolute;
  inset: 0;
}
.ffdh-post-card:hover .ffdh-post-card__title a {
  color: var(--c-fuchsia-700);
}
.ffdh-post-card .ffdh-post-card__excerpt {
  display: -webkit-box;
  overflow: hidden;
  color: var(--c-text-subtle);
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
}
.ffdh-post-card .ffdh-post-card__meta {
  margin-top: auto;
  padding-top: var(--sp-2);
  color: var(--c-text-muted);
  font-size: var(--fs-small);
}
.ffdh-post-card__dot {
  margin-inline: 0.15em;
  color: var(--c-border-strong);
}

/* The featured card leads the index: media beside text from tablet up. */
.ffdh-post-card--featured {
  border-color: var(--c-periwinkle-200);
  background: var(--c-surface-ghost);
}
.ffdh-post-card--featured .ffdh-post-card__body {
  justify-content: center;
  gap: var(--sp-3);
  padding: var(--sp-6);
}
.ffdh-post-card--featured .ffdh-post-card__title {
  font-size: var(--fs-h3);
  line-height: 1.2;
}
.ffdh-post-card--featured .ffdh-post-card__excerpt {
  font-size: var(--fs-lead);
  -webkit-line-clamp: 4;
  line-clamp: 4;
}
@media (min-width: 768px) {
  .ffdh-post-card--featured {
    flex-direction: row;
  }
  .ffdh-post-card--featured .ffdh-post-card__media {
    flex: 1.15;
    aspect-ratio: auto;
    min-height: 280px;
  }
  .ffdh-post-card--featured .ffdh-post-card__body {
    flex: 1;
    padding: var(--sp-8);
  }
}

/* ---------- Post grid, related posts, chips, pagination, empty state ---------- */
.ffdh-post-grid {
  display: grid;
  gap: var(--sp-7);
}
.ffdh-post-grid .ffdh-post-grid__heading {
  margin: 0;
  font-size: var(--fs-h3);
  font-weight: 700;
  line-height: 1.2;
}
.ffdh-post-grid__cards,
.ffdh-related__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-5);
}
@media (min-width: 768px) {
  .ffdh-post-grid__cards,
  .ffdh-related__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (min-width: 1081px) {
  .ffdh-post-grid__cards,
  .ffdh-related__grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
.ffdh-related {
  margin-block: var(--sp-9);
}
.ffdh-related .ffdh-related__heading {
  margin: 0 0 var(--sp-5);
  font-size: var(--fs-h3);
  font-weight: 700;
  line-height: 1.2;
}

.ffdh-chips__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.ffdh-chips__list li {
  margin: 0;
}
.ffdh-chip {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--sp-4);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-pill);
  background: var(--c-surface);
  color: var(--c-ink-900);
  font-size: 0.9375rem;
  font-weight: 500;
  text-decoration: none;
  transition: background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.ffdh-chip:hover {
  border-color: var(--c-periwinkle-400);
  background: var(--c-surface-mist);
  color: var(--c-fuchsia-700);
}

/* The wrapper list also carries .page-numbers, so only the items are pills. */
.ffdh-pagination ul.page-numbers {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.ffdh-pagination li {
  margin: 0;
}
.ffdh-pagination li > .page-numbers {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding-inline: var(--sp-3);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-pill);
  background: var(--c-surface);
  color: var(--c-ink-900);
  font-weight: 600;
  text-decoration: none;
  transition: background-color var(--dur-fast) var(--ease-out);
}
.ffdh-pagination li > a.page-numbers:hover {
  background: var(--c-surface-mist);
}
.ffdh-pagination li > .page-numbers.current {
  border-color: var(--c-ink-900);
  background: var(--c-ink-900);
  color: var(--c-text-on-dark);
}
.ffdh-pagination li > .page-numbers.dots {
  border-color: transparent;
  background: transparent;
  color: var(--c-text-muted);
}

.ffdh-post-grid--empty {
  padding: var(--sp-9) var(--sp-5);
  border: 1px dashed var(--c-border-strong);
  border-radius: var(--r-lg);
  background: var(--c-surface-ghost);
  text-align: center;
}
.ffdh-post-grid .ffdh-empty {
  margin: 0;
  color: var(--c-text-subtle);
  font-size: var(--fs-lead);
}

/* ---------- Archive header ---------- */
/* The eyebrow repeats the ffdh-eyebrow look (ffdh-eyebrow.css): this markup comes from a shortcode, so the Global Class is not on the page to apply. */
.ffdh-archive-header {
  display: grid;
  justify-items: start;
  gap: var(--sp-5);
  max-width: var(--container-narrow);
}
.ffdh-archive-header--author {
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: var(--sp-6);
  align-items: center;
}
.ffdh-archive-header--author > img {
  grid-row: 1 / span 4;
  width: 160px;
  height: 160px;
  border-radius: 50%;
  object-fit: cover;
}
.ffdh-archive-header .ffdh-archive-header__eyebrow {
  margin: 0;
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--r-pill);
  background: var(--c-fuchsia-50);
  color: var(--c-fuchsia-700);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: .02em;
}
.ffdh-archive-header .ffdh-archive-header__title {
  margin: 0;
  /* The header box above holds the measure, so the hero's 24ch H1 default does not narrow a long title further. */
  max-width: none;
  font-size: var(--fs-h1);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.015em;
  overflow-wrap: anywhere;
}
.ffdh-archive-header .ffdh-archive-header__job {
  margin: 0;
  color: var(--c-fuchsia-700);
  font-size: 1.0625rem;
  font-weight: 600;
}
/* The title's letter-spacing and the intro's colour, line-height and measure repeat what the blog hero resolves to (the kit's h1 default style and ffdh-lead.css);
   this markup comes from a shortcode, so neither reaches it. The layout test compares them with the blog hero's computed values. */
.ffdh-archive-header__intro {
  max-width: 60ch;
  color: var(--c-text-muted);
  font-size: var(--fs-lead);
  line-height: 1.6;
}
.ffdh-archive-header__intro > :where(p) {
  margin: 0;
}
@media (max-width: 599px) {
  .ffdh-archive-header--author {
    grid-template-columns: minmax(0, 1fr);
  }
  .ffdh-archive-header--author > img {
    grid-row: auto;
    width: 120px;
    height: 120px;
  }
}

/* ---------- Search form ---------- */
.ffdh-search {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-3);
  max-width: 560px;
}
/* The same style as .ffdh-menu__label (ffdh-chrome.css): the 404 sets the two side by side, and the label's gap to the field matches that label's gap to its list. */
.ffdh-search__label {
  color: var(--c-ink-900);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.4;
}
.ffdh-search__row {
  display: flex;
  gap: var(--sp-2);
}
.ffdh-search .ffdh-search__input {
  flex: 1;
  min-width: 0;
  min-height: 48px;
  margin: 0;
  padding-block: 0;
  padding-inline: var(--sp-5);
  border: 1.5px solid var(--c-border-input);
  border-radius: var(--r-pill);
  background: var(--c-surface);
  color: var(--c-text-body);
  font: inherit;
  font-size: 1rem;
  line-height: 1.25;
}
.ffdh-search .ffdh-search__input:focus {
  border-color: var(--c-ink-900);
  background: var(--c-surface);
  color: var(--c-text-body);
}
.ffdh-search .ffdh-search__button {
  flex: none;
  min-height: 48px;
  margin: 0;
  padding-block: 0;
  padding-inline: var(--sp-6);
  border: 2px solid transparent;
  border-radius: var(--r-pill);
  background: var(--c-fuchsia-600);
  color: var(--c-text-on-dark);
  font: inherit;
  font-weight: 600;
  line-height: 1.25;
  box-shadow: var(--sh-xs);
  cursor: pointer;
  transition: transform var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.ffdh-search .ffdh-search__button:focus {
  background: var(--c-fuchsia-600);
  color: var(--c-text-on-dark);
}
.ffdh-search .ffdh-search__button:hover {
  background: var(--c-fuchsia-700);
  color: var(--c-text-on-dark);
  transform: translateY(-2px);
  box-shadow: var(--sh-cta);
}
.ffdh-search .ffdh-search__button:active {
  transform: none;
}

/* ---------- Lists ---------- */
.ffdh-list {
  display: grid;
  gap: var(--sp-3);
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: ffdh-step;
}
/* Items are a line or two, where "pretty" can still leave one word on the second line; balance evens them (it has no filled box to leave hollow). */
.ffdh-list .ffdh-list__item {
  position: relative;
  margin: 0;
  padding-inline-start: 2.25rem;
  list-style: none;
  text-wrap: balance;
}
.ffdh-list__item::before,
.ffdh-list__item::after {
  position: absolute;
  content: "";
}
/* A fact's display can be lowercase ("skills and work-sample review"), and a fact often opens an item. */
.ffdh-list__item::first-letter {
  text-transform: uppercase;
}
/* Check and cross markers hang in the item's start padding, centred on the first line's x-height. They are out of flow, like the plain and steps markers, because
   ::first-letter does not match after an inline-block box at the start of the line (Chromium and WebKit both skip it), and the first letter of an item that opens with
   a lowercase fact is capitalised below. The top is the first line's centre (0.5lh) moved down to the x-height's centre: from the line's centre the baseline sits
   0.35em lower in Poppins (half the difference of its ascent and descent, 1.05em and 0.35em), and the x-height's centre sits half an x-height above the baseline;
   the marker's own half-height then centres the box. Another font moves the result by a fraction of a pixel. The shapes are masks over token colours, so the colours
   stay tokens and forced colours can restate them. A check is two layers: the ::before disc is the success tint mixed into the --c-surface token as an OPAQUE colour (at 14% alpha it
   turned grey-teal on mist), and the ::after tick is a mask over success. A cross is solid strokes whose ink fills the box, so its edge is the item's edge. */
.ffdh-list--check .ffdh-list__item::before,
.ffdh-list--check .ffdh-list__item::after,
.ffdh-list--cross .ffdh-list__item::before {
  top: calc(0.5lh + 0.35em - 0.5ex - var(--marker-size) / 2);
  left: 0;
  width: var(--marker-size);
  height: var(--marker-size);
  mask-repeat: no-repeat;
  mask-position: center;
  mask-size: 100% 100%;
}
.ffdh-list--cross .ffdh-list__item::after {
  content: none;
}
.ffdh-list--check .ffdh-list__item {
  --marker-size: 1.5rem;
}
.ffdh-list--check .ffdh-list__item::before {
  border-radius: 50%;
  background-color: color-mix(in srgb, var(--c-success) 14%, var(--c-surface));
  mask-image: none;
}
.ffdh-list--check .ffdh-list__item::after {
  background-color: var(--c-success);
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M7.4 12.3l3 3 6.4-6.4' fill='none' stroke='%23000' stroke-width='2.5'/%3E%3C/svg%3E");
}
/* Cross: honest and muted, never alarming red. */
.ffdh-list--cross .ffdh-list__item::before {
  --marker-size: 0.65rem;
  background-color: var(--c-text-muted);
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='M1.25 1.25L8.75 8.75M8.75 1.25L1.25 8.75' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E");
}
.ffdh-list--plain .ffdh-list__item::before {
  top: 0.7em;
  left: 0.5rem;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--c-fuchsia-600);
}
/* The badge centres on the item's first line, as the check and cross markers do. Its own font-size is smaller than the item's, so "1lh" read on the badge would be the badge's
   line, not the item's; a registered length resolves the item's line height on the item and hands the badge the result. */
@property --step-line {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}
.ffdh-list--steps .ffdh-list__item {
  --step-line: 1lh;
  counter-increment: ffdh-step;
  padding-inline-start: 2.75rem;
}
.ffdh-list--steps .ffdh-list__item::before {
  content: counter(ffdh-step);
  top: calc(var(--step-line) / 2 - 0.9375rem);
  left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.875rem;
  height: 1.875rem;
  border-radius: 50%;
  background: var(--c-fuchsia-50);
  color: var(--c-fuchsia-700);
  font-size: 0.9375rem;
  font-weight: 700;
}

/* ---------- Tables ---------- */
/* The scroller is a container at every width, so the caption and the cell caps below can size themselves to its visible width. */
.ffdh-table-wrap {
  max-width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  container-type: inline-size;
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  background: var(--c-surface);
}
.ffdh-table-wrap:focus-visible {
  box-shadow: var(--ring);
}
.ffdh-table {
  border-collapse: separate;
  border-spacing: 0;
  background: var(--c-surface);
  font-size: 0.9375rem;
  line-height: 1.5;
  text-align: start;
}
.ffdh-table caption {
  caption-side: top;
  padding: var(--sp-4) var(--sp-5);
  color: var(--c-ink-900);
  font-size: 1rem;
  font-weight: 600;
  text-align: start;
}
.ffdh-table :is(th, td) {
  padding: var(--sp-3) var(--sp-5);
  border-top: 1px solid var(--c-border);
  text-align: start;
  vertical-align: top;
}
.ffdh-table thead th {
  border-top: 0;
  border-bottom: 2px solid var(--c-border-strong);
  background: var(--c-surface-mist);
  color: var(--c-ink-900);
  font-weight: 600;
}
/* The heading's 2px rule is the line under it: the first body row's hairline would stack on it (border-collapse: separate keeps both) into a 3px two-tone band with a pale lower fringe,
   so that row draws no top border. The calculator's examples table (components.css) does the same. */
.ffdh-table thead + tbody > tr:first-child > :is(th, td) {
  border-top: 0;
}
.ffdh-table tbody th {
  color: var(--c-ink-900);
  font-weight: 600;
}
/* The component is rows divided by a hairline. Hello's reset draws a grey 1px box round every cell (0,0,2), so the sides and bottom are cleared (the top hairline is the rule
   above), and it tints the odd body rows grey (0,1,4) and darkens the row under the pointer, so each row is restated on the surface token and the pointer tint is the ghost
   token. */
.ffdh-table :is(th, td) {
  border-inline: 0;
  border-bottom: 0;
}
/* Hello's "table caption + thead tr:first-child th" (0,1,5) draws a 50% grey line on the heading row of a table with a caption, a second and darker edge inside the frame's own.
   The heading row has no top edge (above), so the reset's selector is restated one class heavier; the calculator's examples table is the same component and has the same caption. A table with
   no caption draws the same line through the reset's "thead:first-child" form, which is restated alongside. */
table:is(.ffdh-table, .ffdh-calc__table) :is(caption + thead, thead:first-child) tr:first-child th {
  border-block-start: 0;
}
.ffdh-table tbody tr:is(:nth-child(odd), :nth-child(even)) > * {
  background: var(--c-surface);
}
.ffdh-table tbody tr:hover > * {
  background: var(--c-surface-ghost);
}

/* The component is as wide as its content and scrolls in its frame when that is wider, instead of squeezing every column to its narrowest wrap: a short value or a two-word
   heading is never broken to make the table fit. Wrapping is the exception, and it is capped from the frame: a text cell wraps at 60% of the frame's visible width (26rem on a
   wide one; 60% is what the 40% pinned column below leaves), so a text column is never wider than the strip a reader can see, and a heading wraps at 11rem, so a long heading does not hold its column wide for a short figure.
   A cell never goes narrower than its longest unbreakable run, so a cap below that widens the column instead of spilling the text over its neighbour. Hello's reset leaves a 15px margin under every table, which inside a frame with no padding is an empty band under the last row. */
.ffdh-table {
  width: max-content;
  min-width: 100%;
  margin: 0;
}
.ffdh-table :is(th, td) {
  min-width: min-content;
  max-width: min(26rem, 60cqi);
}
.ffdh-table thead th {
  max-width: 11rem;
}
/* Table text is balanced, which the site's wrap rules (style.css) do not reach: they cover paragraphs and headings, so a cell, a heading cell and a caption wrapped greedily and ended on
   one word ("The same amount at any / salary", "Recruiting pricing models / compared"). A cell is not a paragraph: "pretty" left "Benefits, private industry full-time / workers" and
   "Recruiting / as a service / (RaaS)" as they were, where "balance" evens the lines. Cells hold at most a few short lines, inside the six that balance covers, and a single-line value is
   untouched. The calculator's examples table is the same component. */
:is(.ffdh-table, .ffdh-calc__table) :is(caption, th, td) {
  text-wrap: balance;
}
/* The caption belongs to the table, which can be wider than the scroller, so it would lay its text out at the table's width and the scroller would clip it. It takes the
   scroller's own width and stays at the start edge as the table scrolls under it (the calculator's examples table does the same). */
.ffdh-table caption {
  position: sticky;
  inset-inline-start: 0;
  box-sizing: border-box;
  width: 100cqi;
}
/* An edge shade says the table scrolls, where a column boundary happens to fall at the frame's edge and no next column peeks. It is the frame's ::after, a sticky grid item laid
   over the table in the table's own grid cell: it stretches to the table's height, so it is never taller than the table and the frame, which clips vertically, has no vertical
   scroll range for find-in-page, an anchor jump or a script to shift the table up in (a shade sized past the table, as a tall one hanging from the caption was, gave it one). The
   frame is a one-column grid only so the shade can share the table's cell, and the track is as wide as the table, never narrower than the frame: at least the table's own width
   (its max-content, as the component's table is as wide as its content; the calculator's table is as wide as the frame, so its min-content), and the frame's width when the table is
   narrower. A track of minmax(100%, max-content) stays at the frame's width, so the shade rode left with the content once the table scrolled, and an auto track does the same in WebKit.
   The shade sticks to the inline-end edge of what is visible and a scroll-driven animation shows it from the start until the end of the scroll: it never draws on a table that fits,
   and it is gone once the last column is in view. The calculator's examples table (components.css) keeps its own markup and takes the same rules. Above the pinned column (z-index 2)
   so the edge reads over it. A browser without scroll-driven animations draws no shade and leaves the cue to the
   next column's peek. */
@supports (animation-timeline: scroll(nearest inline)) {
  :is(.ffdh-table-wrap, .ffdh-calc__table-wrap) {
    display: grid;
  }
  .ffdh-table-wrap {
    grid-template-columns: minmax(max-content, 1fr);
  }
  .ffdh-calc__table-wrap {
    grid-template-columns: minmax(min-content, 1fr);
  }
  :is(.ffdh-table-wrap, .ffdh-calc__table-wrap) > :is(.ffdh-table, .ffdh-calc__table),
  :is(.ffdh-table-wrap, .ffdh-calc__table-wrap)::after {
    grid-area: 1 / 1;
  }
  :is(.ffdh-table-wrap, .ffdh-calc__table-wrap)::after {
    content: "";
    position: sticky;
    inset-inline-end: 0;
    justify-self: end;
    z-index: 3;
    width: var(--sp-5);
    background: linear-gradient(to left, color-mix(in srgb, var(--c-ink-900) 14%, transparent), transparent);
    opacity: 0;
    pointer-events: none;
    animation: ffdh-table-edge linear both;
    animation-timeline: scroll(nearest inline);
  }
  :is(.ffdh-table-wrap, .ffdh-calc__table-wrap):dir(rtl)::after {
    background: linear-gradient(to right, color-mix(in srgb, var(--c-ink-900) 14%, transparent), transparent);
  }
  /* The pinned column's end edge is the counterpart: scrolled under it, the next column's tail shows beside it and reads as a value, so each pinned cell draws a shade over the
     next column from the moment the table leaves its start, fading in across the first scroll step. The cell is sticky, so it is the shade's containing block; the heading's shade
     reaches under its 2px rule and a body cell's over its 1px hairline, so the shade is one band down the column. */
  :is(.ffdh-table--sticky, .ffdh-calc__table) :is(thead, tbody) tr > :first-child::after {
    position: absolute;
    inset-block: -1px 0;
    inset-inline-start: 100%;
    width: var(--sp-5);
    background: linear-gradient(to right, color-mix(in srgb, var(--c-ink-900) 14%, transparent), transparent);
    opacity: 0;
    pointer-events: none;
    animation: ffdh-table-pinned-edge linear both;
    animation-timeline: scroll(nearest inline);
    animation-range: 0 var(--sp-5);
  }
  /* The shade exists only where its cell is pinned: a sticky table's always, the calculator's below the width components.css pins its role column at. */
  .ffdh-table--sticky :is(thead, tbody) tr > :first-child::after {
    content: "";
  }
  @container (max-width: 32rem) {
    .ffdh-calc__table :is(thead, tbody) tr > :first-child::after {
      content: "";
    }
  }
  :is(.ffdh-table--sticky, .ffdh-calc__table) thead tr > :first-child::after {
    inset-block: 0 -2px;
  }
  /* The first body row under a heading has no hairline (above), so its shade starts at its own top: the heading's shade already covers the rule, and a second one would darken it. */
  :is(.ffdh-table--sticky, .ffdh-calc__table) thead + tbody > tr:first-child > :first-child::after {
    inset-block-start: 0;
  }
  :is(.ffdh-table--sticky, .ffdh-calc__table):dir(rtl) :is(thead, tbody) tr > :first-child::after {
    background: linear-gradient(to left, color-mix(in srgb, var(--c-ink-900) 14%, transparent), transparent);
  }
}
@keyframes ffdh-table-pinned-edge {
  to {
    opacity: 1;
  }
}
@keyframes ffdh-table-edge {
  from,
  90% {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}
/* Scrolled under the pinned column, the column after it shows a tail beside the pin, and a tail of "31.5%" reads as "1.5%". table-edge.js measures how far the text the pin cuts reaches,
   marks that cell and sets the distance from the cell's start edge as --ffdh-tail; the cell's own ::after covers that much in the cell's own ground (inherited, so a row's tint and the
   pointer's deepening are followed), under the pinned column and over the cell's text. A cell without the mark has no rule applying to it, so without the script the table renders as it
   always did and the pinned column's 24px shade is the cue. The cover is static: it moves with the scroll and animates nothing, so reduced motion has nothing to switch off. */
:is(.ffdh-table--sticky, .ffdh-calc__table) tr > [data-ffdh-tail] {
  position: relative;
}
:is(.ffdh-table--sticky, .ffdh-calc__table) tr > [data-ffdh-tail]::after {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: var(--ffdh-tail);
  background: inherit;
  pointer-events: none;
}
/* On a phone the cells give up half their side padding, so the strip beside the pinned column holds a whole money figure at 320px. Below 360px the type steps down to the small
   size and the padding to the next step too: at 15px the longest role name ("Bookkeeper") is wider than the 40% the pinned column is allowed there, and a narrower pinned column
   leaves the next column enough edge to notice. */
@media (max-width: 767px) {
  .ffdh-table :is(thead, tbody) :is(th, td) {
    padding-inline: var(--sp-3);
  }
  /* The caption's text starts where the cells' text does, so the frame has one left edge. It is a caption of the base rule's weight, which a later rule of the same weight wins. */
  table.ffdh-table caption {
    padding-inline: var(--sp-3);
  }
}
@media (max-width: 359px) {
  table.ffdh-table {
    font-size: var(--fs-small);
  }
  .ffdh-table :is(thead, tbody) :is(th, td),
  table.ffdh-table caption {
    padding-inline: var(--sp-2);
  }
}
/* A sticky table pins its first column and that column's heading whenever it scrolls, at any width: a reader keeps the row keys and the heading row's start while the rest slides.
   The pinned column is capped at 40% of the frame so the strip beside it holds a whole value and the next column peeks at the edge (the key and role names wrap to fit). The
   cells' own backgrounds are opaque (the rules above), so scrolled cells never show through, and the heading cell sits above the body's. */
.ffdh-table--sticky :is(thead, tbody) tr > :first-child {
  position: sticky;
  inset-inline-start: 0;
  z-index: 1;
  max-width: min(26rem, 40cqi);
  box-shadow: 1px 0 0 var(--c-border-strong);
}
.ffdh-table--sticky thead tr > :first-child {
  z-index: 2;
}

/* A table with two columns that is not sticky holds short phrases (a term and its value), where wrapping a phrase onto a second line reads better than sliding the value column out of sight,
   so it is as wide as its frame and never scrolls. The frame's track is the frame's width, not the table's max-content (which would keep the unwrapped width). The table's min-content is far
   under 320px, so it never needs the track the edge shade rides in. This is the component's option for a key-and-value table: sticky="0" with two columns. A sticky table, or one of
   more columns, keeps the scrolling behaviour above. */
.ffdh-table-wrap:has(> .ffdh-table:not(.ffdh-table--sticky) thead th:nth-child(2):last-child) {
  grid-template-columns: minmax(0, 1fr);
}
.ffdh-table:not(.ffdh-table--sticky):has(thead th:nth-child(2):last-child) {
  width: 100%;
}

/* ---------- Facts updated ---------- */
.ffdh-facts-updated {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

/* ---------- Post body prose ---------- */
.ffdh-post-body {
  font-size: var(--fs-prose);
  line-height: 1.7;
}
.ffdh-post-body :where(p, ul, ol, blockquote, h2, h3, h4) {
  max-width: var(--measure);
}
.ffdh-post-body :where(h2) {
  margin: var(--sp-9) 0 var(--sp-4);
  font-size: var(--fs-h3);
  font-weight: 700;
  line-height: 1.2;
  scroll-margin-top: calc(var(--header-h-scrolled) + var(--sp-5));
}
.ffdh-post-body :where(h3) {
  margin: var(--sp-7) 0 var(--sp-3);
  font-size: var(--fs-h4);
  font-weight: 600;
  line-height: 1.3;
  scroll-margin-top: calc(var(--header-h-scrolled) + var(--sp-5));
}
.ffdh-post-body :where(h2:first-child, h3:first-child) {
  margin-top: 0;
}
.ffdh-post-body :where(p) {
  margin: 0 0 var(--sp-5);
}
.ffdh-post-body :where(ul, ol):not(.ffdh-list) {
  margin: 0 0 var(--sp-5);
  padding-inline-start: 1.6em;
}
.ffdh-post-body :where(li):not(.ffdh-list__item) {
  margin-block: var(--sp-2);
}
.ffdh-post-body :where(ul:not(.ffdh-list) > li)::marker {
  color: var(--c-fuchsia-600);
}
.ffdh-post-body :where(ol:not(.ffdh-list) > li)::marker {
  color: var(--c-text-subtle);
  font-weight: 600;
}
.ffdh-post-body :where(a:not([class])) {
  color: var(--c-fuchsia-700);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.ffdh-post-body :where(a:not([class])):hover {
  color: var(--c-fuchsia-600);
  text-decoration-thickness: 2px;
}
.ffdh-post-body :where(blockquote) {
  margin: var(--sp-6) 0;
  padding-inline: var(--sp-5);
  border-inline-start: 4px solid var(--c-periwinkle-400);
  color: var(--c-text-subtle);
  font-size: 1.0625em;
}
.ffdh-post-body :where(blockquote > :last-child) {
  margin-bottom: 0;
}
.ffdh-post-body :where(img) {
  max-width: 100%;
  height: auto;
  border-radius: var(--r-md);
}
.ffdh-post-body :where(.ffdh-table-wrap, .wp-block-table) {
  margin-block: var(--sp-6);
}

/* ---------- Post-body callouts ---------- */
:is(.ffdh-quick-answer, .ffdh-key-takeaways, .ffdh-definition, .ffdh-fee-callout) {
  margin-block: var(--sp-6);
  padding: var(--sp-5) var(--sp-6);
  border-radius: var(--r-lg);
}
:is(.ffdh-quick-answer, .ffdh-key-takeaways, .ffdh-definition, .ffdh-fee-callout) > :last-child {
  margin-bottom: 0;
}
/* Callouts share the prose column's width, so their edges line up with the text above and below. */
.ffdh-post-body :is(.ffdh-quick-answer, .ffdh-key-takeaways, .ffdh-definition, .ffdh-fee-callout, .ffdh-inline-cta, .ffdh-sources) {
  max-width: var(--measure);
}
/* The accent bar sits on a squared edge; a bar on a fully rounded corner bends into a crescent. */
:is(.ffdh-quick-answer, .ffdh-key-takeaways, .ffdh-fee-callout) {
  border-start-start-radius: var(--r-xs);
  border-end-start-radius: var(--r-xs);
}
@media (max-width: 519px) {
  :is(.ffdh-quick-answer, .ffdh-key-takeaways, .ffdh-definition, .ffdh-fee-callout) {
    padding: var(--sp-4) var(--sp-5);
  }
  .ffdh-inline-cta {
    padding: var(--sp-5);
  }
}
:is(.ffdh-quick-answer, .ffdh-key-takeaways, .ffdh-definition, .ffdh-fee-callout) > .ffdh-box-title {
  margin: 0 0 var(--sp-2);
  color: var(--c-ink-900);
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.4;
}
/* Quick answer: the periwinkle panel, the one block a skimming reader should land on. */
.ffdh-quick-answer {
  border-inline-start: 4px solid var(--c-periwinkle-600);
  background: var(--c-surface-mist);
}
.ffdh-key-takeaways {
  border: 1px solid var(--c-border);
  border-inline-start: 4px solid var(--c-fuchsia-600);
  background: var(--c-surface-ghost);
}
.ffdh-definition {
  border: 1px solid var(--c-periwinkle-200);
  background: var(--c-surface);
  box-shadow: var(--sh-xs);
}
.ffdh-fee-callout {
  border: 1px solid var(--c-border-strong);
  border-inline-start: 4px solid var(--c-fuchsia-600);
  background: var(--c-surface);
  box-shadow: var(--sh-xs);
}
.ffdh-fee-callout > :where(p) {
  max-width: none;
}
.ffdh-post-body .ffdh-sources {
  margin-block: var(--sp-6);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--c-border);
  color: var(--c-text-subtle);
  font-size: 0.9375rem;
}
.ffdh-sources :where(h2, h3) {
  color: var(--c-ink-900);
}
.ffdh-verified {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-block: var(--sp-5) 0;
  color: var(--c-text-muted);
  font-size: var(--fs-small);
  font-weight: 500;
}
.ffdh-verified::before {
  content: "";
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--c-success);
}

/* ---------- Mid-article call to action (pattern root) ---------- */
/* The one dark block in a post. Its colours are stated in full because no dark-ground class is on the page. */
.ffdh-inline-cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4) var(--sp-6);
  margin-block: var(--sp-8);
  padding: var(--sp-6);
  border-radius: var(--r-xl);
  background: var(--c-ink-900);
  color: var(--c-text-on-dark);
}
.ffdh-inline-cta .ffdh-inline-cta__text {
  flex: 1 1 22rem;
  max-width: 40ch;
  margin: 0;
  color: var(--c-text-on-dark);
  font-size: 1.125rem;
  font-weight: 600;
  line-height: 1.5;
}
.ffdh-inline-cta .ffdh-inline-cta__actions {
  margin: 0;
}
.ffdh-inline-cta .wp-block-button__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding-block: var(--sp-3);
  padding-inline: var(--sp-6);
  border: 2px solid transparent;
  border-radius: var(--r-pill);
  background: var(--c-fuchsia-600);
  color: var(--c-text-on-dark);
  font-size: var(--fs-button);
  font-weight: 600;
  line-height: 1.25;
  text-decoration: none;
  box-shadow: var(--sh-cta);
  transition: transform var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out);
}
.ffdh-inline-cta .wp-block-button__link:hover {
  background: var(--c-fuchsia-700);
  color: var(--c-text-on-dark);
  transform: translateY(-2px);
}
.ffdh-inline-cta .wp-block-button__link:active {
  transform: none;
}
.ffdh-inline-cta :where(a, button):focus-visible {
  box-shadow: var(--ring-on-dark);
}

/* ---------- Forced colors ---------- */
/* Backgrounds are dropped in this mode, so a state or marker drawn only as a background takes a system colour. */
@media (forced-colors: active) {
  .ffdh-pagination li > .page-numbers.current {
    forced-color-adjust: none;
    background: Highlight;
    border-color: Highlight;
    color: HighlightText;
  }
  .ffdh-list--check .ffdh-list__item::after,
  .ffdh-list--cross .ffdh-list__item::before,
  .ffdh-list--plain .ffdh-list__item::before {
    forced-color-adjust: none;
    background: CanvasText;
  }
  /* The check's disc stays a faint disc behind a solid tick, as it is outside this mode. */
  .ffdh-list--check .ffdh-list__item::before {
    forced-color-adjust: none;
    background: color-mix(in srgb, CanvasText 14%, Canvas);
  }
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  .ffdh-post-category__link,
  .ffdh-post-card,
  .ffdh-chip,
  .ffdh-pagination li > .page-numbers,
  .ffdh-search .ffdh-search__button,
  .ffdh-inline-cta .wp-block-button__link {
    transition: none !important;
  }
  .ffdh-post-card:hover,
  .ffdh-search .ffdh-search__button:hover,
  .ffdh-inline-cta .wp-block-button__link:hover {
    transform: none !important;
  }
  /* The table's edge shades are scroll-driven animations; without them a shade stays at its resting opacity of 0, and the next column's peek is the cue. */
  :is(.ffdh-table-wrap, .ffdh-calc__table-wrap)::after,
  :is(.ffdh-table--sticky, .ffdh-calc__table) tr > :first-child::after {
    animation: none !important;
  }
}
