/* ==========================================================================
   KF Elements — foundation
   --------------------------------------------------------------------------
   Registered as `kfe-base`; every widget stylesheet depends on it, so it loads
   once, and only on pages that use at least one widget from the pack.

   TOKENS: each --kfe-* custom property chains to the site's Elementor Global
   Colors/Fonts first, so a fresh install already matches the site's kit. The
   literal at the end of each chain is the fallback for sites that never set up
   their kit. Widget style controls override these per instance via Elementor's
   generated CSS (which loads after this file and wins).

   Nothing in this file is ever saved into post content — it is enqueued from
   disk, which keeps host WAFs happy and CSS out of translation jobs.
   ========================================================================== */

.kfe {
  /* palette */
  --kfe-primary: var(--e-global-color-primary, #23305a);
  --kfe-accent: var(--e-global-color-accent, #5d78c7);
  --kfe-ink: var(--e-global-color-text, #1a2238);
  --kfe-highlight: #b89a5e;          /* restrained warm accent (quotes, FAQ) */
  --kfe-surface: #f8f9fc;            /* cool off-white section bg */
  --kfe-surface-deep: #e9ecf4;       /* slightly deeper panel */
  --kfe-white: #fff;

  /* derived shades — literal fallbacks first, color-mix below when supported */
  --kfe-primary-deep: #161f3a;
  --kfe-muted: #5b6580;
  --kfe-line: rgba(35, 48, 90, 0.14);
  --kfe-on-dark: rgba(255, 255, 255, 0.8);   /* body text on dark grounds */
  --kfe-on-dark-soft: #cdd8f2;               /* accents on dark grounds */

  /* type — inherit the theme/kit unless the kit defines globals */
  --kfe-font-heading: var(--e-global-typography-primary-font-family, inherit);
  --kfe-font-body: var(--e-global-typography-text-font-family, inherit);
  --kfe-heading-weight: 300;
  --kfe-h3-weight: 400;

  /* buttons — today's literals, declared (ADR-0004 decision 2). The ink, the
     solid fill and the font are NOT declared: each read carries today's value
     as its fallback, so it resolves where the button paints. */
  --kfe-btn-radius: 50px;
  --kfe-btn-pad: 18px 44px;
  --kfe-btn-size: 13px;
  --kfe-btn-weight: 400;
  --kfe-btn-tracking: 0.16em;
  --kfe-btn-transform: uppercase;
  --kfe-btn-lift: translateY(-3px);
  --kfe-btn-shadow: 0 16px 40px rgba(0, 0, 0, 0.25);

  /* lists and cards — today's literals, declared (ADR-0004 decision 2's first
     form). `--kfe-list-indent` is the one value FOURTEEN rules in this pack
     wrote by hand after #74 — thirteen in widget stylesheets and one here, on
     `.kfe-text-block`, which is the one the first count missed. Three of the
     thirteen are in `facts-card.css` (:44, :57, :110), which is where a
     sheet-by-sheet count slips; `--kfe-shadow-card` has no declaration on purpose
     and is read at its three call sites with that site's shadow as its
     fallback, because the three were never the same shadow (see #81's section
     in ADR-0004). */
  --kfe-list-indent: 20px;

  /* layout */
  /* The kit's Content Width, published as --container-max-width on .e-con;
     the literal is the fallback for a site with no kit, and for a widget in a
     legacy boxed section, where the same setting is a plain max-width and no
     variable is published. */
  --kfe-max: var(--container-max-width, 1280px);
  --kfe-gutter: 6vw;
  --kfe-section-pad: 13vh;
}

@supports (color: color-mix(in srgb, red 50%, blue)) {
  .kfe {
    --kfe-primary-deep: color-mix(in srgb, var(--kfe-primary), #000 32%);
    --kfe-muted: color-mix(in srgb, var(--kfe-ink), #8a92a8 55%);
    --kfe-line: color-mix(in srgb, var(--kfe-primary), transparent 86%);
  }
}

/* ---------- primitives ---------- */
.kfe * { box-sizing: border-box; }
.kfe img, .kfe video { display: block; max-width: 100%; }

.kfe-wrap { max-width: var(--kfe-max); margin: 0 auto; padding: 0 var(--kfe-gutter); }

.kfe-section {
  font-family: var(--kfe-font-body);
  /* The pack's body weight is a TOKEN, and its default is the web's own 400
     (#104). `font-weight: 300` stood here from `9244bde`, the initial 0.1.0
     widget pack, so every `p`, `li`, card body, checklist line, table caption
     and footer link inside a kf-elements widget inherited Light while the
     site's own kit asked for 400 — the headings escaped it only because they
     declare a weight of their own. That is one site's house style shipped as
     everyone's default, and the pack stops contradicting the kit it sits in.
     The token is deliberately NOT declared in the `.kfe` block above (ADR-0004
     decision 2's pattern, and the reason is sharper here): an UNDECLARED read
     means a site that genuinely wants Light writes `--kfe-body-weight: 300`
     wherever it keeps its token map — `:root`, `body`, its child theme — and
     the value inherits in, instead of having to out-specify a rule in a
     stylesheet it does not own. */
  font-weight: var(--kfe-body-weight, 400);
  line-height: 1.65;
  color: var(--kfe-ink);
  padding: var(--kfe-section-pad-start, var(--kfe-section-pad)) 0 var(--kfe-section-pad-end, var(--kfe-section-pad));
  position: relative;
}
.kfe h2 { font-family: var(--kfe-font-heading); font-weight: var(--kfe-heading-weight); letter-spacing: -0.01em; color: var(--kfe-heading, var(--kfe-primary)); margin: 0; }
.kfe h3 { font-family: var(--kfe-font-heading); font-weight: var(--kfe-h3-weight); color: var(--kfe-heading, var(--kfe-primary)); margin: 0; }
.kfe p { margin: 0; }
/* The eyebrow's size, tracking, colour and weight are TOKEN READS with today's
   literal as each fallback (ADR-0004 decision 2's second form, w2.homepack.1):
   a site that sets none of them computes exactly what it computed before, and a
   page that needs a louder eyebrow — the consuming site's 18–45 half draws its
   eyebrow larger than the heading under it — sets them where it keeps its
   tokens, instead of out-specifying this rule. */
.kfe .kfe-label { font-size: var(--kfe-label-size, 12px); letter-spacing: var(--kfe-label-tracking, 0.3em); text-transform: uppercase; color: var(--kfe-label-color, var(--kfe-accent)); margin-bottom: 22px; font-weight: var(--kfe-label-weight, 400); }

/* ---------- the heading accent: heading_text()'s `em.kfe-title-accent` ----------
   The words an author wraps in <em>…</em> inside a heading (class-widget-base.php,
   heading_text()). A class no released markup carries, so no existing page can
   match it; italic and the heading's own colour by default, and four undeclared
   tokens for the site that wants the reference's serif italic in its own colour. */
.kfe .kfe-title-accent {
  font-style: var(--kfe-title-accent-style, italic);
  font-family: var(--kfe-title-accent-font, inherit);
  font-weight: var(--kfe-title-accent-weight, inherit);
  color: var(--kfe-title-accent-color, inherit);
}
.kfe .kfe-dark .kfe-title-accent { color: var(--kfe-title-accent-on-dark, inherit); }

/* ---------- background presets (see add_background_controls) ---------- */
.kfe .kfe-bg-surface { background: var(--kfe-surface); }
.kfe .kfe-bg-white { background: var(--kfe-white); }
.kfe .kfe-bg-tint { background: var(--kfe-surface-deep); }
.kfe .kfe-bg-dark { background: var(--kfe-primary); }
.kfe .kfe-bg-deep { background: var(--kfe-primary-deep); }
.kfe .kfe-bg-accent { background: var(--kfe-accent); }

/* dark-ground text flips, shared by every widget with a dark treatment */
.kfe .kfe-dark { color: var(--kfe-on-dark); }
.kfe .kfe-dark h2, .kfe .kfe-dark h3 { color: #fff; }
.kfe .kfe-dark .kfe-label { color: var(--kfe-on-dark-soft); }

/* ---------- buttons ---------- */
.kfe .kfe-btn {
  display: inline-block; background: var(--kfe-white); color: var(--kfe-btn-ink, var(--kfe-primary));
  padding: var(--kfe-btn-pad); border-radius: var(--kfe-btn-radius); text-decoration: none;
  font-size: var(--kfe-btn-size); letter-spacing: var(--kfe-btn-tracking); text-transform: var(--kfe-btn-transform); font-weight: var(--kfe-btn-weight);
  text-align: center; line-height: 1.4; transition: transform 0.4s, box-shadow 0.4s;
}
/* The button font, for ANCHORS only and at zero specificity (ADR-0004
   decision 2): today's rule declares no font, so an anchor button takes any
   author rule on `a` (a kit's Link typography) and that rule still wins here;
   a <button class="kfe-btn"> (kfe_form's submit) is not reached at all, so it
   keeps the font it has today on a theme with or without a button reset. */
:where(.kfe a.kfe-btn) { font-family: var(--kfe-btn-font, inherit); }
.kfe .kfe-btn:hover { transform: var(--kfe-btn-lift); box-shadow: var(--kfe-btn-shadow); }
.kfe .kfe-btn.solid { background: var(--kfe-btn-solid, var(--kfe-primary)); color: #fff; }
.kfe .kfe-btn.solid:hover { background: var(--kfe-primary-deep); }
.kfe .kfe-btn.outline {
  background: transparent; border: 1px solid var(--kfe-highlight); color: var(--kfe-highlight);
  transition: background 0.4s, color 0.4s, transform 0.4s;
}
.kfe .kfe-btn.outline:hover { background: var(--kfe-highlight); color: var(--kfe-primary-deep); transform: translateY(-2px); }
/* `line` — a secondary button drawn as a hairline box in the ink, filled on
   hover (w2.homepack.1). A modifier no released markup prints: the page
   header's photo layout and the split's second link use it through
   render_link( …, 'button', 'line' ), which emits no data-* — it is never one
   of the three calls to action. On a dark ground it draws in white.
   The hairline is an INSET SHADOW, not a border, so a line button beside a
   solid one is exactly as tall as it: `.kfe-btn` carries no border. */
.kfe .kfe-btn.line {
  background: transparent; color: var(--kfe-btn-line, var(--kfe-ink));
  box-shadow: inset 0 0 0 1px var(--kfe-btn-line, var(--kfe-ink));
  transition: background 0.35s, color 0.35s, transform 0.35s;
}
.kfe .kfe-btn.line:hover { background: var(--kfe-btn-line, var(--kfe-ink)); color: var(--kfe-btn-line-ink, #fff); box-shadow: inset 0 0 0 1px var(--kfe-btn-line, var(--kfe-ink)); transform: translateY(-2px); }
.kfe .kfe-dark .kfe-btn.line { color: var(--kfe-white); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5); }
.kfe .kfe-dark .kfe-btn.line:hover { background: var(--kfe-white); color: var(--kfe-primary-deep); }

/* ---------- scroll reveal ----------
   Applied only once shared.js has announced itself on <html>; without JS (or
   with reduced motion) content is simply visible. */
html.kfe-js .kfe .kfe-reveal { opacity: 0; transform: translateY(38px); transition: opacity 1s cubic-bezier(0.2, 0.7, 0.2, 1), transform 1s cubic-bezier(0.2, 0.7, 0.2, 1); }
html.kfe-js .kfe .kfe-reveal.in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  html.kfe-js .kfe .kfe-reveal { opacity: 1; transform: none; transition: none; }
}

/* ---------- shared two-column split ---------- */
.kfe .kfe-split { display: grid; grid-template-columns: 1fr 1fr; gap: 6vw; align-items: center; }
@media (max-width: 820px) { .kfe .kfe-split { grid-template-columns: 1fr; gap: 36px; } }

/* ==========================================================================
   Shared primitives for the section frame (ADR-0004 decision 6)
   --------------------------------------------------------------------------
   Rendered by Widget_Base's helpers and wired into no released widget by the
   record that added them: each widget lane opts its own widgets in. Every rule
   leads with a class no widget's markup carried before, so none of them can
   match an existing page. Colours and site-level sizes are tokens, each read
   with today's value as its fallback; small fixed geometry (the mark column,
   the underline) is written as the reference draws it.
   ========================================================================== */

/* ---------- the arrow link: render_link( …, 'arrow' ) ---------- */
.kfe .kfe-more {
  color: var(--kfe-link, var(--kfe-accent)); font-weight: 600; text-decoration: none;
  border-bottom: 1px solid currentColor; padding-bottom: 2px;
}
@supports (color: color-mix(in srgb, red 50%, blue)) {
  .kfe .kfe-more { border-bottom-color: color-mix(in srgb, var(--kfe-link, var(--kfe-accent)) 35%, transparent); }
}
.kfe .kfe-more:hover,
.kfe .kfe-more:focus-visible { color: var(--kfe-accent-deep, var(--kfe-link, var(--kfe-accent))); border-bottom-color: currentColor; }
.kfe .kfe-dark .kfe-more,
.kfe .kfe-dark .kfe-more:hover { color: #fff; border-bottom-color: rgba(255, 255, 255, 0.5); }

/* ---------- the check / dash list: render_list() ---------- */
.kfe .kfe-list { list-style: none; margin: 0; padding: 0; }
.kfe .kfe-list li {
  position: relative; padding-inline-start: 30px;
  color: var(--kfe-body, var(--kfe-muted)); font-size: var(--kfe-body-size, inherit); line-height: 1.6;
  /* A token read with `inherit` as its fallback (w2.homepack.1): a list of
     short claims set in the heading face. `inherit` is today's computed value
     wherever no theme rule of lower specificity (a bare `li` rule) sets a
     face on the item; where one does, the item now takes its parent's. */
  font-family: var(--kfe-list-font, inherit);
}
.kfe .kfe-list li + li { margin-top: 10px; }
.kfe .kfe-list li::before {
  content: "\2713"; position: absolute; inset-inline-start: 0; top: 0;
  color: var(--kfe-accent); font-weight: 700;
}
.kfe .kfe-list[data-glyph="heavy"] li::before { content: "\2714"; }
.kfe .kfe-list[data-marker="dash"] li::before { content: "\2013"; color: var(--kfe-muted); font-weight: 400; }
.kfe .kfe-list a { color: var(--kfe-link, var(--kfe-accent)); }
.kfe .kfe-list a:hover { color: var(--kfe-accent-deep, var(--kfe-link, var(--kfe-accent))); }
.kfe .kfe-dark .kfe-list li { color: var(--kfe-on-dark); }
.kfe .kfe-dark .kfe-list li::before { color: var(--kfe-on-dark-soft); }
.kfe .kfe-dark .kfe-list a { color: #fff; }

/* ---------- the text column: render_text_column() ---------- */
.kfe .kfe-text-col { color: var(--kfe-body, var(--kfe-muted)); font-size: var(--kfe-body-size, inherit); }
.kfe .kfe-text-col > * + * { margin-top: 1.5em; }
/* The lead's face, style, weight, colour and line height are TOKEN READS
   with `inherit` as each fallback — which is what an undeclared inherited
   property computes wherever no theme rule of lower specificity sets it on
   this element — and its size keeps today's 1.06em (w2.homepack.1). A site
   that sets none of them computes what it computed before; one that needs a
   lead drawn as a serif statement sets them where it keeps its tokens. */
.kfe .kfe-text-col-lead {
  font-size: var(--kfe-lead-size, 1.06em);
  font-family: var(--kfe-lead-font, inherit);
  font-style: var(--kfe-lead-style, inherit);
  font-weight: var(--kfe-lead-weight, inherit);
  color: var(--kfe-lead-color, inherit);
  line-height: var(--kfe-lead-line-height, inherit);
}
.kfe .kfe-text-col p + p,
.kfe .kfe-text-col p + ul,
.kfe .kfe-text-col p + ol,
.kfe .kfe-text-col ul + p,
.kfe .kfe-text-col ol + p { margin-top: 1em; }
.kfe .kfe-text-col-lead ul, .kfe .kfe-text-col-lead ol,
.kfe .kfe-text-col-body ul, .kfe .kfe-text-col-body ol,
.kfe .kfe-text-block ul:not(.kfe-list), .kfe .kfe-text-block ol { margin: 0; padding-inline-start: var(--kfe-list-indent); }

/* ---------- a list keeps its items with their markers ----------
   `kfe_text_section` reads `--kfe-head-align` on its COLUMN as well as its head
   (text-section.css:21-22), so a centred section centres every line of prose in
   it — which is what the reference draws — and the centring INHERITS into the
   list items' text. A marker is not text: `list-style-position: outside` places
   it against the `li`'s own box, and the `li` is as wide as the column. So on
   `/pris-graa-staer/` the two `•` of the price list sat at the far left of the
   content column with their lines centred ~300px away (wp-eedk #255), and the
   reference renders that same list left-aligned and tight under its intro.

   The property that is wrong is the INHERITED `text-align`, not the marker's
   position and not the list's box: a list is a structured block whose marker
   and line are one unit, and re-aligning the marker by hand (`inside`, or a
   shrink-to-fit box with automatic margins) moves a released look on every
   left-aligned instance to fix a centred one. `text-align: start` on the list
   alone is a NO-OP wherever the column is not centred — which is every other
   section in the corpus — and it is the rule `kfe_cta` has carried since 0.13.0
   for exactly this reason (cta.css:47: a band that centres everything, whose
   body list does not). `.kfe-list` is included: its `::before` check glyph is
   laid out from the `li`'s start edge and strands identically.

   WHAT IT REACHES, NAMED RATHER THAN ASSUMED, because this is base.css and a
   rule here reaches every widget on every site the pack is installed on. The
   selector is `.kfe-text-col`, which is the shared column `render_text_column()`
   emits (class-widget-base.php:993) and which has exactly TWO callers:
   `kfe_text_section` (class-text-section.php:194) and `kfe_split_media`
   (class-split-media.php:523). split-media.css:25 centres the whole widget from
   the same `--kfe-head-align`, so a centred split-media's body list strands its
   markers exactly as the text section did and is fixed by the same rule. Same
   defect, same fix, wider than the issue that filed it — wp-eedk #255 names only
   the text section.

   WHAT IT DOES NOT REACH, and this is deliberate rather than an oversight. The
   pack centres a list in two other places and neither is a `.kfe-text-col`:
   `price-table.css:155` centres `.style-list .foot` UNCONDITIONALLY for that
   style, and `feature-grid.css:342` centres `.align-center .card`; four more
   containers (`facts-card.css:26`, `gallery.css:23`, `faq.css:18`/`:49` and
   split-media's own `.body`) centre from `--kfe-head-align` and hold a list that
   takes `--kfe-list-indent`. Each is the same defect. None is measured here, and
   adding six rules to six released stylesheets overnight on an argument rather
   than on a marker measurement is the move #81's own section refuses two
   paragraphs above. They are filed as **#136** with this list and with the
   measurement recipe. */
.kfe .kfe-text-col ul, .kfe .kfe-text-col ol { text-align: start; }

/* ---------- ...and a centred column centres the list as ONE block ----------
   The rule above re-joined each marker to its line, but left the LIST at the
   column's start edge. In a centred section every paragraph around the list is
   centred, so on `/pris-graa-staer/` the two price lines still sat alone at the
   column's far left, under a centred heading and a centred intro (wp-eedk's
   final read of 2026-09-25 at 1440; wp-eedk #255 stayed open against it).

   The list now takes one track as wide as its widest item
   (`minmax(0, max-content)`: never wider than the column, so a line that must
   wrap wraps where it did) and that track sits where the column's text sits:
   `justify-content` reads the SAME `--kfe-head-align` the column's
   `text-align` reads, with the same `start` fallback. The items stay
   start-aligned inside the track, so each marker stays against its line and
   the lines share one left edge, as a list should.

   WHERE IT IS A NO-OP. A column that is not centred computes `start`, and a
   start-justified track of `max-content` puts every item at the x it had as a
   block, with the same line breaks — the ul's own box, margins and padding are
   untouched, and the pack sets no vertical margin on a plain list item. The
   two containers inside a column that reset `text-align: start` (the pillar
   panel and the `rule` block) reset the track too, so a list there stays at
   their start edge in a centred section. The `.kfe-list` check list is a list
   like any other here: its `::before` glyph is laid out from the item. */
.kfe .kfe-text-col ul, .kfe .kfe-text-col ol {
  display: grid;
  grid-template-columns: minmax(0, max-content);
  justify-content: start;
}
.kfe :is(.kfe-text-section, .kfe-split-media) .kfe-text-col :is(ul, ol) { justify-content: var(--kfe-head-align, start); }
.kfe :is(.kfe-text-section, .kfe-split-media) .kfe-text-col :is(.kfe-text-pillars, .kfe-text-block[data-style="rule"]) :is(ul, ol) { justify-content: start; }
.kfe .kfe-text-block-title { font-family: var(--kfe-font-heading); color: var(--kfe-heading, var(--kfe-primary)); margin: 0 0 10px; }
.kfe .kfe-text-block-label { color: var(--kfe-heading, var(--kfe-primary)); margin: 0 0 10px; }
.kfe .kfe-text-col-lead a,
.kfe .kfe-text-col-body a,
.kfe .kfe-text-block a { color: var(--kfe-link, var(--kfe-accent)); }
.kfe .kfe-text-block[data-style="note"] { background: var(--kfe-surface); border-radius: 8px; padding: 14px 16px; }
.kfe .kfe-text-block[data-style="callout"] {
  background: var(--kfe-white); border-inline-start: 4px solid var(--kfe-accent);
  border-radius: 8px; padding: 26px 28px;
}
.kfe .kfe-text-block[data-style="chips"] p { display: flex; flex-wrap: wrap; gap: 10px; }
.kfe .kfe-text-block[data-style="chips"] a {
  display: inline-block; background: var(--kfe-white); border: 1px solid var(--kfe-line); border-radius: 8px;
  padding: 10px 16px; color: var(--kfe-heading, var(--kfe-primary)); font-weight: 600; text-decoration: none;
}
.kfe .kfe-text-block[data-style="chips"] a:hover { border-color: var(--kfe-accent); color: var(--kfe-accent); }

/* ---------- three block styles added by w2.homepack.1 ----------
   Each leads with a class or an attribute value no released markup carries, so
   no existing page can match one. Colours are tokens; geometry is the shape's.

   `pillar` — a run of consecutive pillar blocks, side by side inside ONE panel
   (`div.kfe-text-pillars`, printed by render_text_column() only around such a
   run), with a hairline between neighbours; stacked at 720px and below. */
.kfe .kfe-text-pillars {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  text-align: start;
  background: var(--kfe-pillars-ground, var(--kfe-white));
  border: 1px solid var(--kfe-line);
  border-radius: var(--kfe-radius-card, 14px);
  box-shadow: var(--kfe-shadow-card, 0 30px 70px -40px rgba(10, 14, 28, 0.35));
  padding: clamp(34px, 4.6vw, 60px) clamp(20px, 3vw, 40px);
  position: relative;
}
.kfe .kfe-text-pillars::before {
  content: ""; position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: 64px; height: 3px; border-radius: 0 0 4px 4px;
  background: linear-gradient(90deg, transparent, var(--kfe-accent), transparent);
}
.kfe .kfe-text-pillars > .kfe-text-block { padding: 0 clamp(20px, 3vw, 44px); margin: 0; }
.kfe .kfe-text-pillars > .kfe-text-block + .kfe-text-block { border-inline-start: 1px solid var(--kfe-pillars-rule, var(--kfe-line)); }
.kfe .kfe-text-block[data-style="pillar"] .kfe-text-block-title {
  font-size: var(--kfe-pillar-title-size, clamp(1.35rem, 2.4vw, 1.75rem)); line-height: 1.1; margin-bottom: 12px;
}
@media (max-width: 720px) {
  .kfe .kfe-text-pillars { grid-auto-flow: row; }
  .kfe .kfe-text-pillars > .kfe-text-block { padding: 0; }
  .kfe .kfe-text-pillars > .kfe-text-block + .kfe-text-block {
    border-inline-start: 0; border-top: 1px solid var(--kfe-pillars-rule, var(--kfe-line));
    margin-top: 32px; padding-top: 32px;
  }
}

/* `rule` — short lines, each paragraph drawn with a rule at its start; a bold
   key at the head of a line takes its own colour. */
.kfe .kfe-text-block[data-style="rule"] { display: grid; gap: 14px; text-align: start; }
.kfe .kfe-text-block[data-style="rule"] p {
  padding-inline-start: 18px; border-inline-start: 2px solid var(--kfe-rule-color, var(--kfe-accent));
}
.kfe .kfe-text-block[data-style="rule"] p + p { margin-top: 0; }
.kfe .kfe-text-block[data-style="rule"] strong { color: var(--kfe-rule-key, inherit); }

/* `facts` — a figure beside what it measures, rows ruled top and bottom. */
.kfe .kfe-text-facts { margin: 0; border-top: 1px solid var(--kfe-line); }
.kfe .kfe-text-facts > div {
  display: flex; align-items: baseline; gap: 18px;
  padding: 18px 0; border-bottom: 1px solid var(--kfe-line);
}
.kfe .kfe-text-facts dt {
  min-width: 90px; margin: 0;
  font-family: var(--kfe-font-heading); font-weight: var(--kfe-facts-weight, 600);
  font-size: var(--kfe-facts-size, 24px); line-height: 1.1;
  color: var(--kfe-facts-color, var(--kfe-accent));
}
.kfe .kfe-text-facts dd { margin: 0; font-size: var(--kfe-facts-label-size, 15px); color: var(--kfe-body, var(--kfe-muted)); }
.kfe .kfe-dark .kfe-text-facts,
.kfe .kfe-dark .kfe-text-facts > div { border-color: rgba(255, 255, 255, 0.14); }
.kfe .kfe-dark .kfe-text-facts dt { color: var(--kfe-on-dark-soft); }
.kfe .kfe-dark .kfe-text-facts dd { color: var(--kfe-on-dark); }
.kfe .kfe-dark .kfe-text-col { color: var(--kfe-on-dark); }
.kfe .kfe-dark .kfe-text-block-title,
.kfe .kfe-dark .kfe-text-block-label,
.kfe .kfe-dark .kfe-text-col-lead a,
.kfe .kfe-dark .kfe-text-col-body a { color: #fff; }

/* ---------- a link in a widget's OWN body copy ---------- */
/* The four rules above cover a link in the SHARED markup — the arrow link, the
   check list, the text column and the text block. They cover nothing in the
   `div.body` a widget prints for itself (`kfe_cta`, `kfe_split_media`,
   `kfe_event_list`) or in a numbered `.kfe-steps .step`, so an inline link in
   either was left to whatever the theme draws: Hello Elementor's `reset.css`
   sets `a { color: #c36 }` and five links on the EuroEyes preview set rendered
   in that pink (#102). One rule beside the existing four, and the dark-ground
   flip the others already carry.

   THE EXCLUSION IS `:not([class])` — an anchor with no class attribute at all —
   and it is written that way after the first round got it wrong. `kfe_event_list`
   prints its sign-up link INSIDE the very `div.body` this rule reaches
   (class-event-list.php:436, printed at :503), as `a.kfe-btn.outline` when the
   CTA is a button and `a.kfe-event-list-cta` when `cta_style=link`. Naming the
   classes one by one — `:not(.kfe-btn):not(.kfe-more)` — cost the rule its place
   in the cascade: each `:not()` ADDS a class to the count, so the light rule rose
   to (0,4,1) and out-specified `event-list.css`'s own
   `.kfe .kfe-event-list .event .kfe-event-list-cta` (0,4,0). Nothing rendered
   differently, because both sides resolve to the same
   `var(--kfe-link, var(--kfe-accent))` — but base.css was winning `color` over a
   widget sheet that owns the anchor, and the next widget to add a class here
   would have had to be named in this list too.

   `:not([class])` costs one attribute, not four classes: (0,3,1) light and
   (0,4,1) dark, back UNDER both of event-list's own rules (light (0,4,0), dark
   `.kfe .kfe-event-list.kfe-dark .event .kfe-event-list-cta` (0,5,0)). It also
   excludes by construction rather than by list — `kfe-btn`, `kfe-more`,
   `kfe-event-list-cta` and any class a later widget invents. The target set is
   unchanged, because the only anchor this rule is about is an inline one in
   prose, and `paragraphs_rich()`'s allow-list permits href/title/rel/target on
   `a` and no `class` at all (class-widget-base.php:155-167).

   The dark ground is where dropping the exclusion is visible rather than merely
   wrong: with no exclusion, `.kfe .kfe-dark .body a` (0,3,1) beats
   `.kfe .kfe-btn.outline` (0,3,0) and the harness reads the sign-up BUTTON at
   `rgb(255, 255, 255)` where the pack draws it `rgb(184, 154, 94)`. Both
   variants are measured: `cta_style` button AND `cta_style=link`, light and
   deep. */
.kfe .body a:not([class]),
.kfe .kfe-steps .step a:not([class]) { color: var(--kfe-link, var(--kfe-accent)); }
/* The dark flip is written `.kfe-dark.kfe-steps` — a COMPOUND, not a descendant
   pair — and the harness is why. `section_classes()` puts `kfe-dark` on the
   section element itself, so on a dark steps band `.kfe-dark` and `.kfe-steps`
   are the SAME element and `.kfe-dark .kfe-steps` matches nothing: the first
   run of this rule left `kfe-f-steps-link-deep`'s three links in the accent on
   a navy ground while the two `.body` widgets flipped to white, because for
   those `.body` IS a descendant of the section. */
.kfe .kfe-dark .body a:not([class]),
.kfe .kfe-dark.kfe-steps .step a:not([class]) { color: #fff; }

/* ---------- the bleed split: text to the container's edge, media to the viewport's ---------- */
.kfe .kfe-split-bleed { display: grid; grid-template-columns: minmax(0, 50fr) minmax(0, 50fr); align-items: stretch; }
.kfe .kfe-split-bleed > .txt {
  display: flex; flex-direction: column; justify-content: center; min-width: 0;
  padding-block: var(--kfe-section-pad-start, var(--kfe-section-pad)) var(--kfe-section-pad-end, var(--kfe-section-pad));
  padding-inline: max(var(--kfe-gutter), calc((100vw - var(--kfe-max)) / 2 + var(--kfe-gutter))) var(--kfe-gutter);
}
.kfe .kfe-split-bleed > .media { position: relative; align-self: stretch; line-height: 0; min-height: 100%; }
.kfe .kfe-split-bleed > .media img {
  position: absolute; inset: 0; width: 100%; height: 100%; max-width: none;
  object-fit: cover; object-position: var(--kfe-focus-x, 50%) var(--kfe-focus-y, 50%);
}
.kfe .kfe-split-bleed.media-left > .media { order: -1; }
.kfe .kfe-split-bleed.media-left > .txt {
  padding-inline: var(--kfe-gutter) max(var(--kfe-gutter), calc((100vw - var(--kfe-max)) / 2 + var(--kfe-gutter)));
}

/* Nothing inside pack grids may force the page wider than the viewport, and the
   two declarations here do DIFFERENT halves of that, which the first round of
   this comment ran together. `min-width: 0` bounds the TRACK: without it a grid
   item's `min-width: auto` floor is its min-content width, so the longest word
   in a tile sets the column's minimum and the grid — and with it the page — is
   forced wider. `overflow-wrap: break-word` bounds the CONTENT: it lets a token
   that still does not fit its line box break rather than spill past it. Only
   the first is kept on the grid rule; the second is not what stops a page
   widening, and saying so was wrong.

   `overflow-wrap: break-word` rode along on the same rule and inherited into
   everything in the grid, so it broke ORDINARY words the moment a narrow column
   made one the longest thing on its line — four Danish compounds split
   mid-word in `kfe_feature_grid` tiles at 390 (#101), where the reference draws
   each on one line. A compound noun is not an unbreakable token, and breaking
   it at an arbitrary letter is worse than letting it sit proud of its tile by a
   few pixels, which is what a compound does: measured at 390 in a two-column
   grid, `Alderssynethed` overruns its h3 box by ~16px and stays well inside its
   144px track, and `document.scrollWidth` reads 390 against `innerWidth` 390.

   What is left is the declaration made only where an unbreakable token is
   genuinely expected — a machine address printed as its own link text, which is
   `mailto:`, `tel:` AND `http(s):`, the commonest of the three and the one the
   first round left out. The grid scoping stays on those, because that is where
   the tile is narrow enough to matter.

   `kfe-break-long` is DELIBERATELY NOT scoped to the grid. `overflow-wrap`
   inherits, the class is already namespaced, and the one route a site has to
   the class — Elementor's Advanced → CSS Classes — puts it on the
   `.elementor-element` WRAPPER, which is an ancestor of the `div.kfe` this pack
   emits (class-widget-base.php:74) and therefore an ancestor of every
   `.kfe-grid` inside it. Scoped to `.kfe .kfe-grid .kfe-break-long` the opt-in
   was unreachable by that route and by any other, since `paragraphs_rich()`
   allows no `class` attribute on anything: a promise in three files with no way
   to keep it. Unscoped, it works from the wrapper and inherits inward, which is
   what this comment, ADR-0004 (3) and readme.txt all say it does.

   The residual risk the blanket declaration used to cover is MEASURED rather
   than assumed, and it is real. A 60-character unbreakable token in a non-link
   grid child, two columns at 390: `document.scrollWidth` reads 390 against an
   `innerWidth` of 390 with the blanket declaration, and 966 against 390
   without it. Nothing the pack renders is that shape — a compound overruns its
   tile by ~16px and the page stays at 390, and a machine address is covered by
   the rule above — and `kfe-break-long` restores the old behaviour wherever a
   site does put one. The trade is named in ADR-0004 and carried to the
   operator on PR #105 rather than decided here. */
.kfe .kfe-grid { min-width: 0; }
.kfe .kfe-grid > * { min-width: 0; }
.kfe .kfe-grid a[href^="mailto:"],
.kfe .kfe-grid a[href^="tel:"],
.kfe .kfe-grid a[href^="http"] { overflow-wrap: break-word; }
.kfe-break-long { overflow-wrap: break-word; }

/* ==========================================================================
   The card (ADR-0005's dated section of 2026-09-21, kf-elements #148)
   --------------------------------------------------------------------------
   ONE CARD, TWO WIDGETS. These rules were `feature-grid.css`'s, where 125 of
   that sheet's 387 rules named `.kfe-feature-grid` and no second widget could
   reach one. `kfe_post_grid` draws the same card from a live query, so the
   rules moved here — the sheet every widget already depends on — rather than
   being copied into a second file that would drift from this one the first
   time either was touched.

   THE SELECTORS ARE THE SAME SELECTORS, AT THE SAME SPECIFICITY. Each moved
   rule reads `.kfe :is(.kfe-feature-grid, .kfe-post-grid) …` where it read
   `.kfe .kfe-feature-grid …`: `:is()` takes the specificity of its most
   specific argument, which is one class either way, so nothing in this block
   out-specifies or loses to anything it did not before. A third widget that
   wants cards adds its stem to each `:is()` — and its markup must print the
   same `.card`, `.ic`, `.card-img` and `.more` the shared renderer prints,
   because these rules are the other half of that renderer's contract.

   THE NAMESPACED PARTS CARRY BOTH NAMES. `kfe_feature_grid` has printed
   `.kfe-feature-grid-tag` since 0.13.0 and may never stop (KFE-001), so the
   part selectors name the stem that prints them: `:is(.kfe-feature-grid-tag,
   .kfe-post-grid-tag)`. That single class token is the ONE way an archive card
   and a page card differ, and `w2.postgrid.1`'s browser probe is what says the
   computed style does not.

   WHAT DID NOT MOVE, AND WHICH HALF OF IT IS HONEST. The split photo is
   `feature-grid.css`'s own and the empty state and the pager are
   `post-grid.css`'s own: 7 lines and 28 lines that the other sheet does not
   contain and should not. The head, the grid container's track list, its two
   responsive safety nets and the intro/outro rules are NOT each widget's own —
   measured at the fix round, they are 23 BYTE-IDENTICAL LINES IN TWO SHEETS
   once the stem is normalised, out of `feature-grid.css`'s 30. That is the
   drift this extraction exists to refuse, and a second extraction under this
   same `:is()` pattern is owed: kf-elements #152, which names the 23 and
   requires the three-capture render proof and a develop-vs-branch
   computed-style reading before it merges. The triforce's
   `width: calc(50% - 17px)` stays per-sheet whatever else moves, because the
   orphan's width is half of THAT grid's own gap — it is a copy today only
   because both gaps are 34px.

   NO EUROEYES VALUE LIVES HERE, as nowhere in this file does: colours are
   tokens, mixes of tokens or the pack's own `#fff` / `rgba(…)` idioms, and
   every size that varies per section is a control with no default.
   ========================================================================== */

.kfe :is(.kfe-feature-grid, .kfe-post-grid) .card {
  background: var(--kfe-white);
  border: 1px solid var(--kfe-line);
  border-radius: var(--kfe-radius-card, 6px);
  padding: 40px 34px;
  transition: transform 0.5s, box-shadow 0.5s;
}
/* The card shadow is a token READ, undeclared, with this sheet's own shadow as
   its fallback (#81): the three "same" card shadows in this pack are three
   different shadows, so a declared token would repaint two of them. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid) .card:hover { transform: var(--kfe-card-lift, translateY(-6px)); box-shadow: var(--kfe-shadow-card, 0 22px 50px rgba(10, 14, 28, 0.1)); }
.kfe :is(.kfe-feature-grid, .kfe-post-grid) .card .ic {
  width: 48px; height: 48px; border-radius: 50%;
  background: var(--kfe-surface-deep);
  margin-bottom: 24px;
  display: flex; align-items: center; justify-content: center;
  color: var(--kfe-accent);
  font-family: var(--kfe-font-heading); font-style: italic;
}
.kfe :is(.kfe-feature-grid, .kfe-post-grid) .card h3 { font-size: 20px; margin-bottom: 14px; line-height: 1.25; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid) .card p { color: var(--kfe-body, var(--kfe-muted)); font-size: var(--kfe-body-size, 15px); }

/* dark-ground treatment mirrors the Steps widget */
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-dark .card {
  background: rgba(255, 255, 255, 0.04);
  border-color: rgba(255, 255, 255, 0.14);
  box-shadow: none;
}
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-dark .card:hover { border-color: var(--kfe-highlight); background: rgba(255, 255, 255, 0.07); }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-dark .card .ic { background: rgba(255, 255, 255, 0.08); color: var(--kfe-on-dark-soft); }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-dark .card h3 { color: #fff; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-dark .card p { color: rgba(255, 255, 255, 0.68); }

/* An icon inside the same 48px circle the marker uses. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid) .card .ic.ic-icon { font-size: 20px; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid) .card .ic.ic-icon svg { width: 20px; height: 20px; fill: currentColor; }

/* An image instead of the circle: the card's own radius, cropped to 4:3. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid) .card .card-img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--kfe-radius-media, 6px);
  margin-bottom: 24px;
}
/* The no-photo plate (ADR-0005's dated section of 2026-09-23): the `div` a
   card prints in its picture's place when a widget opts in and the row has
   no image. It carries `.card-img`, so its BOX is the rule above's and, in
   the bleed card, `.image-bleed .card .card-img`'s — this rule declares the
   ground and nothing else. Re-declaring a box property here would TIE the
   rule above at (0,4,0) and win by source order, rewriting the shared
   card's box from a rule that reads like an addition. And the selector ends
   in ONE class on purpose: `.card-img.card-img-none` would be (0,5,0), tie
   the bleed rule and beat it by order, and a no-photo bleed card would draw
   a 4:3 gap where the 190px band belongs — which no markup diff can see. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid) .card .card-img-none { background: var(--kfe-surface-deep); }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-dark .card .card-img-none { background: rgba(255, 255, 255, 0.06); }

/* The inline card link — not a button, so it is not .kfe-btn. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid) .card .more {
  display: inline-block;
  margin-top: 16px;
  color: var(--kfe-accent);
  text-decoration: none;
  font-size: 14px;
}
.kfe :is(.kfe-feature-grid, .kfe-post-grid) .card .more:hover { text-decoration: underline; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-dark .card .more { color: var(--kfe-highlight); }

/* The rich card body (decision 3). base.css's `.kfe p { margin: 0 }` gives a
   second paragraph no spacing, the colour and size sit on `.card p` so a list
   needs them too, and base.css has no list reset. Added rules: a single-line
   `marker` card matches 0.11.0 because neither selector can match it. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid) .card p + p { margin-top: 10px; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid) .card ul, .kfe :is(.kfe-feature-grid, .kfe-post-grid) .card ol { color: var(--kfe-body, var(--kfe-muted)); font-size: var(--kfe-body-size, 15px); margin: 10px 0 0; padding-inline-start: var(--kfe-list-indent); }
.kfe :is(.kfe-feature-grid, .kfe-post-grid) .card li + li { margin-top: 6px; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-dark .card ul, .kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-dark .card ol { color: rgba(255, 255, 255, 0.68); }

/* The row titles at any other heading level. Today's `.card h3` rule above is
   untouched; these carry the same values for the tag one level up or down, so
   `header_size` changes the outline and nothing else. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid) .card h2,
.kfe :is(.kfe-feature-grid, .kfe-post-grid) .card h4,
.kfe :is(.kfe-feature-grid, .kfe-post-grid) .card h5 { font-size: 20px; margin-bottom: 14px; line-height: 1.25; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-dark .card h2,
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-dark .card h4,
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-dark .card h5 { color: #fff; }

/* The eyebrow: the reference's tag line above the title. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid) :is(.kfe-feature-grid-tag, .kfe-post-grid-tag) {
  font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--kfe-accent); font-weight: 700; margin: 0 0 10px;
}
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-dark :is(.kfe-feature-grid-tag, .kfe-post-grid-tag) { color: var(--kfe-on-dark-soft); }

/* The notes: one inset per blank-line paragraph. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid) :is(.kfe-feature-grid-note, .kfe-post-grid-note) {
  background: var(--kfe-surface); border-radius: 8px; padding: 14px 16px; margin-top: 14px;
  color: var(--kfe-body, var(--kfe-muted)); font-size: var(--kfe-body-size, inherit);
}
.kfe :is(.kfe-feature-grid, .kfe-post-grid) :is(.kfe-feature-grid-note, .kfe-post-grid-note) p + p { margin-top: 10px; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-dark :is(.kfe-feature-grid-note, .kfe-post-grid-note) { background: rgba(255, 255, 255, 0.06); color: var(--kfe-on-dark); }

/* The bare / inline icon wrapper. NO UNCONDITIONAL `fill` IN THESE RULES, and
   that is the whole point of a second wrapper: the marker circle's
   `.ic.ic-icon svg { fill: currentColor }` beats a `fill="none"` presentation
   attribute at any specificity, so a stroked outline icon renders solid inside
   it. Here a stroked icon keeps its own `fill="none"` and `color` reaches its
   `stroke="currentColor"`.

   `svg:not([fill])` is the other half, and it is measured, not assumed: an
   icon that declares no fill of its own does NOT inherit `color` — SVG's
   initial `fill` is `black`, not `currentColor`. Elementor renders a Font
   Awesome glyph as `<svg class="e-font-icon-svg e-fas-…" viewBox="…">` with no
   `fill` attribute (read on the harness at 1440), so without this rule every
   Font Awesome icon in a `bare` or `inline` grid computes
   `fill: rgb(0, 0, 0)` and prints black where the section's accent is wanted —
   the icon-grid reference (744#10) draws them in the accent. The attribute
   selector matches only an SVG with no `fill` of its own, so a `fill="none"`
   outline is still never touched. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid) :is(.kfe-feature-grid-icon, .kfe-post-grid-icon) {
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--kfe-accent); line-height: 0; margin-bottom: 18px;
}
.kfe :is(.kfe-feature-grid, .kfe-post-grid) :is(.kfe-feature-grid-icon, .kfe-post-grid-icon) svg { width: 1em; height: 1em; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid) :is(.kfe-feature-grid-icon, .kfe-post-grid-icon) svg:not([fill]) { fill: currentColor; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid) :is(.kfe-feature-grid-icon, .kfe-post-grid-icon) i { font-style: normal; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-dark :is(.kfe-feature-grid-icon, .kfe-post-grid-icon) { color: var(--kfe-on-dark-soft); }

/* `inline`: the icon beside the title, the body under the title. A grid rather
   than a flex row, because a flex row would put the title and the body side by
   side as two more flex items. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid).icon-inline .card {
  display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 12px; align-items: start;
}
.kfe :is(.kfe-feature-grid, .kfe-post-grid).icon-inline .card > * { grid-column: 2; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).icon-inline .card > :is(.kfe-feature-grid-icon, .kfe-post-grid-icon) {
  grid-column: 1; grid-row: 1; margin-bottom: 0; margin-block-start: 2px;
}

/* The avatar: the round portrait, or the initials disc. The ring is the
   SECTION'S OWN GROUND, published per preset as a widget-local property — a
   ring in a colour the section does not paint would draw a halo.

   THE PROPERTY IS RENAMED AND THE OLD NAME STILL WINS. It was
   `--kfe-feature-grid-ground`, which names one widget and so cannot serve two;
   the declarations below publish `--kfe-card-ground` instead. The ring READS
   the old name first with the new one as its fallback. Nothing in the pack
   declares the old name any more; it survives as an undeclared read kept for a
   consumer this repository cannot see (ADR-0004 decision 2's pattern).

   WHICH CONSUMER KEEPS WHAT, STATED PRECISELY, because the obvious sentence is
   wrong in one direction. A site that set `--kfe-feature-grid-ground` ON THE
   SECTION ITSELF keeps exactly the ring it chose. A site that set it in a
   `:root` or `.kfe` token map PREVIOUSLY HAD NO RING FROM IT: the rules below
   used to redeclare the old name on `.kfe .kfe-feature-grid` and on every
   `.kfe-bg-*` preset, so the widget overrode the inherited value on every
   section and the token map never reached the ring. It reaches it now. That is
   a behaviour change, in the opposite direction from "nothing moves", and its
   whole reach is one box-shadow colour: ADR-0004:674 declares this token
   widget-local rather than public. Every site that never heard of the name
   computes exactly what it computed before. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid) { --kfe-card-ground: transparent; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-bg-surface { --kfe-card-ground: var(--kfe-surface); }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-bg-white { --kfe-card-ground: var(--kfe-white); }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-bg-tint { --kfe-card-ground: var(--kfe-surface-deep); }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-bg-dark { --kfe-card-ground: var(--kfe-primary); }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-bg-deep { --kfe-card-ground: var(--kfe-primary-deep); }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).kfe-bg-accent { --kfe-card-ground: var(--kfe-accent); }
.kfe :is(.kfe-feature-grid, .kfe-post-grid) :is(.kfe-feature-grid-avatar, .kfe-post-grid-avatar),
.kfe :is(.kfe-feature-grid, .kfe-post-grid) :is(.kfe-feature-grid-initials, .kfe-post-grid-initials) {
  width: 132px; height: 132px; border-radius: 50%; overflow: hidden;
  margin: 0 auto 18px; flex: 0 0 auto;
  box-shadow: 0 0 0 5px var(--kfe-feature-grid-ground, var(--kfe-card-ground));
}
.kfe :is(.kfe-feature-grid, .kfe-post-grid) :is(.kfe-feature-grid-avatar, .kfe-post-grid-avatar) { background: var(--kfe-surface-deep); }
.kfe :is(.kfe-feature-grid, .kfe-post-grid) :is(.kfe-feature-grid-avatar, .kfe-post-grid-avatar) img {
  width: 100%; height: 100%; max-width: none;
  object-fit: cover; object-position: center 22%; border-radius: 50%;
}
.kfe :is(.kfe-feature-grid, .kfe-post-grid) :is(.kfe-feature-grid-initials, .kfe-post-grid-initials) {
  background: linear-gradient(135deg, var(--kfe-accent), var(--kfe-primary-deep));
  color: #fff; font-weight: 700; font-size: 38px;
  display: flex; align-items: center; justify-content: center;
}
.kfe :is(.kfe-feature-grid, .kfe-post-grid).media-avatar .card { display: flex; flex-direction: column; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).media-avatar .card:hover {
  transform: var(--kfe-card-lift, translateY(-2px)); box-shadow: 0 6px 22px rgba(0, 0, 0, 0.08);
}

/* `bleed`: the picture runs to the card's edges at its head, with the card's
   own radius clipping it. The card becomes a positioning context so that the
   row link can stretch over it — one real link, as the reference's own comment
   asks for, rather than a card full of nested anchors. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid).image-bleed .card { overflow: hidden; padding: 0; display: flex; flex-direction: column; position: relative; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).image-bleed .card .card-img {
  width: 100%; aspect-ratio: auto; height: 190px; object-position: center 40%;
  border-radius: 0; margin-bottom: 0;
}
/* The card's own inset is padding on ordinary copy, but MARGIN on a link, and
   that difference is measured, not stylistic. `.kfe-more` carries its underline
   as a `border-bottom` (base.css:150-154), which is drawn on the PADDING box:
   with `padding-inline: 24px` and a 26px foot padding the clinic card's
   `a.kfe-more` measured 269x77 at 1440, so its underline ran the width of the
   card and sat 26px clear of the label instead of under it. `.kfe-btn` has the
   same trouble for a different reason — the inline padding would add to the
   button's own 12/22 and stretch the pill. Margins give both the same inset
   with the border still tight to the text. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid).image-bleed .card > :not(.card-img):not(.more):not(.kfe-more):not(.kfe-btn) { padding-inline: 24px; }
/* The top inset belongs to ONE element — the first flow child under the photo.
   `:first-of-type` matched once per element TYPE, so the title, the first body
   paragraph, the first note and the pinned anchor each took the 24px: measured
   at 1440 the clinic card's title-to-address gap ran 62px against the sibling
   paragraphs' 34px, and `a.kfe-more` measured 221x53 where a 16px label with a
   2px underline padding is ~29px. `.card-img + *` is the bleed card; the
   `:first-child` arm keeps the inset on a row whose `card_image` is empty and
   prints no `img`. The links are excluded here as they are on the two
   neighbouring rules, because their inset is the margin below. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid).image-bleed .card > .card-img + :not(.more):not(.kfe-more):not(.kfe-btn),
.kfe :is(.kfe-feature-grid, .kfe-post-grid).image-bleed .card > :first-child:not(.card-img):not(.more):not(.kfe-more):not(.kfe-btn) { padding-block-start: 24px; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).image-bleed .card > :last-child:not(.more):not(.kfe-more):not(.kfe-btn) { padding-block-end: 26px; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).image-bleed .card .more,
.kfe :is(.kfe-feature-grid, .kfe-post-grid).image-bleed .card .kfe-more,
.kfe :is(.kfe-feature-grid, .kfe-post-grid).image-bleed .card .kfe-btn {
  margin-block-start: auto; align-self: flex-start;
  margin-inline: 24px; margin-block-end: 26px;
}
.kfe :is(.kfe-feature-grid, .kfe-post-grid).image-bleed .card .more::after,
.kfe :is(.kfe-feature-grid, .kfe-post-grid).image-bleed .card .kfe-more::after { content: ""; position: absolute; inset: 0; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).image-bleed .card:hover {
  transform: var(--kfe-card-lift, translateY(-3px)); box-shadow: 0 12px 30px rgba(0, 0, 0, 0.12);
}
.kfe :is(.kfe-feature-grid, .kfe-post-grid).image-bleed .card:focus-within { box-shadow: 0 12px 30px rgba(0, 0, 0, 0.12); }

/* ---------- card_style: five permanent values ---------- */

/* `flat` — the card with no lift and no shadow, the link pinned to the foot. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-flat .card {
  border-radius: var(--kfe-radius-card, 14px); padding: 30px 28px;
  display: flex; flex-direction: column; box-shadow: none;
}
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-flat .card:hover { transform: none; box-shadow: none; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-flat .card .more,
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-flat .card .kfe-more,
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-flat .card .kfe-btn { margin-block-start: auto; align-self: flex-start; }

/* `plain` — no chrome at all. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-plain .card {
  background: none; border: 0; border-radius: 0; padding: 0; box-shadow: none;
}
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-plain .card:hover { transform: none; box-shadow: none; }

/* `filled` — the accent as the ground, the copy on it. The mixed body colour
   sits inside @supports with the plain token before it, so a browser without
   color-mix() gets the readable fallback rather than nothing. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-filled .card {
  background: var(--kfe-accent); border-color: var(--kfe-accent);
  border-radius: var(--kfe-radius-card, 12px); padding: 17px; box-shadow: none;
}
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-filled .card:hover { transform: none; box-shadow: none; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-filled .card h2,
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-filled .card h3,
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-filled .card h4,
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-filled .card h5 { color: #fff; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-filled .card p,
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-filled .card li { color: var(--kfe-on-dark); }
@supports (color: color-mix(in srgb, red 50%, blue)) {
  .kfe :is(.kfe-feature-grid, .kfe-post-grid).style-filled .card p,
  .kfe :is(.kfe-feature-grid, .kfe-post-grid).style-filled .card li { color: color-mix(in srgb, var(--kfe-on-dark) 85%, var(--kfe-accent)); }
}
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-filled :is(.kfe-feature-grid-icon, .kfe-post-grid-icon) { color: #fff; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-filled :is(.kfe-feature-grid-tag, .kfe-post-grid-tag) { color: #fff; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-filled :is(.kfe-feature-grid-note, .kfe-post-grid-note) { background: rgba(255, 255, 255, 0.12); color: var(--kfe-on-dark); }

/* `tile` — the whole card is the anchor. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-tile .card {
  display: block; border-radius: var(--kfe-radius-card, 14px); padding: 22px 20px;
  text-decoration: none; color: inherit; box-shadow: none;
}
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-tile a.card:hover { border-color: var(--kfe-accent); }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-tile .card:hover { transform: none; box-shadow: none; }
/* A tile's title is a short label, often one word, and it must fit INSIDE
   the tile (wp-eedk #427 item 1). The #101 note above let a compound "sit
   proud of its h3 box". On ee-build at 390 in two columns, a 20px
   `Alderssynethed` is about 164px wide in a 112px content box, so it ran
   past the tile's border.
   The title is sized to the tile instead. The card is an inline-size
   container, and the title computes at most today's 20px. It shrinks only
   when the tile is narrow. At phone width the tile's side padding goes from
   20px to 14px, so the label keeps a size close to the reference's.
   A wide tile (four columns at 1440, two at 820) still computes 20px. So
   does a browser without container units, because it drops the
   declaration. The `item_title_size` control's selector outranks this rule,
   so an instance's own size still wins. The 13px floor keeps the label
   legible. It is reached at 360, where the widest label runs 7px into the
   padding but stays inside the border. On a narrower screen a long word can
   still overrun, as every label did before this change. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-tile .card { container-type: inline-size; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).style-tile .card :is(h2, h3, h4, h5) { font-size: clamp(13px, 11.5cqi, 20px); }
@media (max-width: 767px) {
  .kfe :is(.kfe-feature-grid, .kfe-post-grid).style-tile .card { padding-inline: 14px; }
}

/* ---------- the remaining per-instance choices ---------- */
.kfe :is(.kfe-feature-grid, .kfe-post-grid).title-accent .card h2,
.kfe :is(.kfe-feature-grid, .kfe-post-grid).title-accent .card h3,
.kfe :is(.kfe-feature-grid, .kfe-post-grid).title-accent .card h4,
.kfe :is(.kfe-feature-grid, .kfe-post-grid).title-accent .card h5 { color: var(--kfe-accent); }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).align-center .card { text-align: center; align-items: center; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).link-arrow .card,
.kfe :is(.kfe-feature-grid, .kfe-post-grid).link-button .card { display: flex; flex-direction: column; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).link-arrow .card .kfe-more,
.kfe :is(.kfe-feature-grid, .kfe-post-grid).link-button .card .kfe-btn { margin-block-start: auto; align-self: flex-start; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).link-button .card .kfe-btn { padding: 12px 22px; }

/* The pin needs a FLOOR, and it cannot be a top margin. `margin-block-start:
   auto` above resolves to `margin-top` in horizontal-tb and carries the same
   five classes, so a `margin-top: 16px` written here loses on source order and
   never applies — and the auto margin is 0 whenever the card has no free space,
   which is every card at one column and the tallest card of each row. Measured
   at 390: "Se muligheder for 18-45" sat on the last body line with no gap on
   the tagged cards, and "Se profil" flush under "Ojenkirurg" on the team grid
   at both widths. The floor goes where the auto margin cannot eat it: padding
   on the arrow, whose `border-bottom` underline is drawn on the padding box's
   BOTTOM edge and so stays tight to the label; and a bottom margin on the one
   element before the button, whose own 12/22 padding must not grow. `:has()`
   picks that single sibling — an engine without it simply keeps today's tight
   foot, which is what this rule replaces. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid).link-arrow .card .kfe-more { padding-block-start: 16px; }
.kfe :is(.kfe-feature-grid, .kfe-post-grid).link-button .card > :has(+ .kfe-btn) { margin-block-end: 16px; }

/* `card_align=center` centres the pinned link too, and this block MUST stay
   after the `link-*` block above. Both selectors carry five classes, so they
   tie on specificity and source order decides: with the centring written first
   the link kept `align-self: flex-start` and sat at the card's inline start
   while its title and body were centred — measured on the team grid at 390,
   card centre 195px against link centre 89px. The `style-flat` and
   `image-bleed` pins are written earlier still, so they were never the ones
   that won. */
.kfe :is(.kfe-feature-grid, .kfe-post-grid).align-center .card .more,
.kfe :is(.kfe-feature-grid, .kfe-post-grid).align-center .card .kfe-more,
.kfe :is(.kfe-feature-grid, .kfe-post-grid).align-center .card .kfe-btn { align-self: center; }

/* ==========================================================================
   RTL — connected scripts need room, not tracking
   --------------------------------------------------------------------------
   Latin display type is set tight; Arabic (and other RTL scripts) needs the
   opposite: negative letter-spacing damages joining, and diacritics need real
   vertical room. Specificity is deliberately high (html[dir="rtl"] body …) so
   these survive Elementor's generated per-element CSS, which loads later.
   ========================================================================== */
html[dir="rtl"] body .kfe h1,
html[dir="rtl"] body .kfe h2,
html[dir="rtl"] body .kfe h3 { letter-spacing: 0; }
html[dir="rtl"] body .kfe h2 { line-height: 1.45; }
html[dir="rtl"] body .kfe h3 { line-height: 1.5; }
html[dir="rtl"] body .kfe p,
html[dir="rtl"] body .kfe li { line-height: 1.9; }
html[dir="rtl"] body .kfe .kfe-label { letter-spacing: 0; text-transform: none; }
html[dir="rtl"] body .kfe .kfe-btn { letter-spacing: 0; text-transform: none; }

/* Add `kfe-hide-rtl` to any widget's Advanced → CSS Classes field to hide it
   on right-to-left pages only. The setting is shared across languages — the
   CSS is what is language-scoped — so setting it in the LTR editor is safe. */
html[dir="rtl"] body .kfe-hide-rtl,
html[dir="rtl"] body .kfe .kfe-hide-rtl { display: none !important; }
