/* ==========================================================
   KJRE PRODUCTION SYSTEM  |  WordPress production layer
   The ONLY file in this theme containing rules that are not
   in a locked prototype. Everything here exists because
   WordPress renders things the prototypes never had to.
   No token is redefined. No locked rule is overridden.
   ========================================================== */

/* ---------- skip link (spec 20.1) ----------
   Required landmark navigation. Visually hidden until focused. */
.kjre-skip{position:absolute;left:-9999px;top:0;z-index:9999;
  background:var(--canopy);color:var(--paper);font-family:var(--body);
  font-size:.7rem;font-weight:500;letter-spacing:.16em;text-transform:uppercase;
  padding:.9em 1.4em;text-decoration:none}
.kjre-skip:focus{left:0}

/* ---------- admin bar offset ----------
   WordPress injects a 32px (46px mobile) bar above <html>. The
   sticky nav has to account for it or it sits underneath. */
body.admin-bar .nav{top:32px}
@media screen and (max-width:782px){body.admin-bar .nav{top:46px}}

/* ---------- WordPress core content classes ----------
   Editor output that the prototypes never produced. Mapped to
   existing tokens so nothing new is introduced. */
.alignwide{max-width:var(--max);margin-inline:auto}
.alignfull{max-width:none;margin-inline:calc(50% - 50vw);width:100vw}
.wp-caption-text,figcaption{font-family:var(--body);font-size:.68rem;font-weight:300;
  letter-spacing:.04em;color:var(--ink-3);line-height:1.6;margin-top:.9em}
.screen-reader-text{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
blockquote.wp-block-quote{font-family:var(--quote);font-style:italic;font-weight:300;
  border-left:2px solid var(--brass);padding-left:1.2em;color:var(--ink);max-width:40ch}

/* ---------- image system (spec: crop discipline) ----------
   Ratios are fixed so layout never shifts while an image loads.
   CLS budget in spec 21.1 is 0.1; unsized images are the usual cause. */
.kjre-img{display:block;width:100%;height:auto;object-fit:cover;background:var(--paper-2)}
.kjre-img[data-ratio="hero"]{aspect-ratio:16/9}
.kjre-img[data-ratio="plate"]{aspect-ratio:4/5}
.kjre-img[data-ratio="card"]{aspect-ratio:3/2}
.kjre-img[data-ratio="square"]{aspect-ratio:1/1}
.kjre-img[data-ratio="wide"]{aspect-ratio:21/9}
.kjre-img[data-ratio="frame"]{aspect-ratio:5/4}
img[loading="lazy"]{background:var(--paper-2)}

/* ---------- form states (spec 08.1) ----------
   The prototypes styled fields but never an error or success
   state, because they never submitted. Production needs both. */
.field.is-error input,.field.is-error select,.field.is-error textarea{
  border-color:var(--brass);border-left-width:2px}
.field__err{display:block;font-family:var(--body);font-size:.68rem;font-weight:400;
  color:var(--brass);margin-top:.55em;line-height:1.5}
.field__hint{display:block;font-family:var(--body);font-size:.66rem;font-weight:300;
  color:var(--ink-3);margin-top:.5em;line-height:1.5}
.kjre-form__consent{font-family:var(--body);font-size:.68rem;font-weight:300;
  color:var(--ink-3);line-height:1.62;max-width:56ch;margin-bottom:1.2em}
.kjre-form__status{font-family:var(--body);font-size:.78rem;font-weight:400;
  color:var(--canopy);border-left:2px solid var(--canopy);background:var(--paper-2);
  padding:clamp(14px,2vw,22px);line-height:1.66}
.kjre-form.is-busy .btn{opacity:.55;pointer-events:none}
/* honeypot: never shown, never announced (spec 08.2 spam handling) */
.kjre-hp{position:absolute!important;left:-9999px!important;width:1px;height:1px;
  overflow:hidden}

/* ---------- placeholder flag (spec 23.1) ----------
   Visible in staging, blocked at the gate, and loud on purpose. */
.kjre-flag{display:block;font-family:var(--body);font-size:.68rem;font-weight:500;
  letter-spacing:.06em;color:var(--brass);background:var(--paper-2);
  border:1px dashed var(--brass);border-left-width:2px;padding:.75em 1em;line-height:1.6}

/* ---------- print ---------- */
@media print{
  .nav,.sheet,.burger,.kjre-skip,.foot__base,.idx,.savebar{display:none!important}
  body{background:none;color:var(--ink)}
  .wrap{max-width:none;padding-inline:0}
  a[href^="http"]::after{content:" (" attr(href) ")";font-size:.7em;color:var(--ink-3)}
}

/* ---------- reduced motion, production reinforcement ----------
   The locked system already honours this. Repeated here because
   the reveal classes are now applied by the theme's own JS. */
@media (prefers-reduced-motion:reduce){
  .js-anim [data-rv]{opacity:1!important;transform:none!important;transition:none!important}
  html{scroll-behavior:auto!important}
}

/* ---------- CTA hooks (spec 09) ----------
   These carry NO visual rules. They exist so inc/components.php can
   emit a stable hook and assets/js/kjre-analytics.js can bind named
   conversion events to it. Declared here so the build check can see
   them and so nobody adds styling to them by accident. */
.kjre-cta{}
.kjre-cta--section{}
.kjre-cta--inline{}
.kjre-cta--text{}

/* ---------- Elementor Pro form, mapped onto the locked field system ----------
   The four locked prototypes styled their own form markup. Elementor
   Pro emits different markup for the same thing, so these rules map
   its classes onto the values already in 02-system.css and
   03-seller.css. No new design decision is made here: every value
   below is copied from a locked rule, and every colour is a token.
   Scoped to .kjre-form so the existing prototype forms are untouched.
   Scoped under #kjre-main as well as .kjre-form for a reason. Elementor
   writes its own per widget defaults into the page stylesheet with five
   class selectors, and that file loads after the theme, so a class only
   rule here would lose to a default nobody chose. Every built page
   carries id kjre-main, so one id in the selector settles it without a
   specificity arms race and without copying design values into widget
   settings where they would drift.
   */
#kjre-main .elementor-form.kjre-form .elementor-form-fields-wrapper{display:grid;grid-template-columns:1fr 1fr;
  gap:clamp(16px,2vw,24px);margin:0}
#kjre-main .elementor-form.kjre-form .elementor-field-group{display:flex;flex-direction:column;
  /* Elementor sets flex-wrap:wrap on the group for its own row layout.
     Left on, a column container wraps the label and the field into two
     side by side columns instead of stacking them. */
  flex-wrap:nowrap;
  /* Elementor also centres the group's items for its row layout, which
     centres the label over the field. The locked system left aligns. */
  align-items:stretch;text-align:left;gap:.6em;
  min-width:0;margin:0;padding:0;width:auto;flex:none}
#kjre-main .elementor-form.kjre-form .elementor-field-group.elementor-col-100,
#kjre-main .elementor-form.kjre-form .elementor-field-type-textarea,
#kjre-main .elementor-form.kjre-form .elementor-field-type-submit{grid-column:1/-1}
/* A hidden field still renders its group wrapper, and in a grid that is
   a full row plus a gap. Eleven attribution fields were opening a gap
   the height of the form. They carry no visible content, so they carry
   no row. */
#kjre-main .elementor-form.kjre-form .elementor-field-type-hidden{display:none}
#kjre-main .elementor-form.kjre-form .elementor-field-label{font-family:var(--body);font-size:.58rem;font-weight:600;text-align:left;width:100%;
  letter-spacing:.17em;text-transform:uppercase;color:var(--ink-3);padding:0;line-height:1.4}
#kjre-main .elementor-form.kjre-form .elementor-field-textual{
  font-family:var(--body);font-size:.92rem;font-weight:400;color:var(--ink);
  background:transparent;border:0;border-bottom:1px solid var(--rule);
  padding:.65em 0;width:100%;border-radius:0;appearance:none;box-shadow:none;
  transition:border-color .35s var(--ease);
}
#kjre-main .elementor-form.kjre-form select.elementor-field-textual{
  background-image:linear-gradient(45deg,transparent 50%,var(--ink-3) 50%),
    linear-gradient(135deg,var(--ink-3) 50%,transparent 50%);
  background-position:calc(100% - 9px) calc(50% + 1px),calc(100% - 4px) calc(50% + 1px);
  background-size:5px 5px,5px 5px;background-repeat:no-repeat;padding-right:20px}
#kjre-main .elementor-form.kjre-form textarea.elementor-field-textual{resize:vertical;min-height:72px;line-height:1.6}
#kjre-main .elementor-form.kjre-form .elementor-field-textual:focus{border-bottom-color:var(--canopy);
  outline:none;box-shadow:none}
#kjre-main .elementor-form.kjre-form .elementor-field-textual::placeholder{color:var(--ink-3)}
/* Production accessibility. The locked system signals focus with the
   underline colour alone, which is thin for keyboard navigation, so
   forms that actually submit get a real focus ring as well. */
#kjre-main .elementor-form.kjre-form .elementor-field-textual:focus-visible,
#kjre-main .elementor-form.kjre-form .elementor-button:focus-visible{outline:2px solid var(--canopy);outline-offset:3px}
#kjre-main .elementor-form.kjre-form .elementor-button{
  display:inline-flex;align-items:center;gap:.7em;justify-content:center;
  font-family:var(--body);font-size:.74rem;font-weight:600;letter-spacing:.16em;
  text-transform:uppercase;padding:1.15em 1.9em;border:1px solid var(--ink);
  background:var(--ink);color:var(--paper);border-radius:0;width:auto;
  transition:background .45s var(--ease),color .45s var(--ease),border-color .45s var(--ease);
}
#kjre-main .elementor-form.kjre-form .elementor-button:hover{background:var(--canopy);border-color:var(--canopy);
  color:var(--paper)}
#kjre-main .elementor-form.kjre-form .elementor-field-type-submit{align-items:flex-start}
/* Elementor's own message strings, mapped onto the locked status style. */
#kjre-main .elementor-form.kjre-form ~ .elementor-message,#kjre-main .elementor-form.kjre-form .elementor-message{
  font-family:var(--body);font-size:.78rem;font-weight:400;line-height:1.66;
  padding:clamp(14px,2vw,22px);margin-top:1.1em;background:var(--paper-2);
  border-left:2px solid var(--canopy);color:var(--canopy)}
/* Danger keeps the brass hairline and nothing more. Brass is an accent
   token, "hairline accent only" in 01-tokens.css, and brass body text on
   Paper 2 measures about 3.1:1, which fails AA at this size. The state is
   carried by the rule and the wording; the words stay readable in ink. */
#kjre-main .elementor-form.kjre-form ~ .elementor-message.elementor-message-danger,
#kjre-main .elementor-form.kjre-form .elementor-message.elementor-message-danger{border-left-color:var(--brass);
  color:var(--ink)}
#kjre-main .elementor-form.kjre-form .elementor-field-group>.elementor-field-subgroup .elementor-field-option label{
  font-family:var(--body);font-size:.88rem;text-transform:none;letter-spacing:0;
  color:var(--ink)}
@media (max-width:620px){
#kjre-main .elementor-form.kjre-form .elementor-form-fields-wrapper{grid-template-columns:1fr}
}

/* ---------- comparison table, scroll region focus ----------
   The locked .cmpwrap scrolls horizontally on small screens.
   A scroll container has to be reachable by keyboard or its
   content is unreachable without a mouse, so inc/components.php
   gives it tabindex="0". A focusable element needs a visible
   focus state; this is the same ring the forms use, and it
   introduces no new token. */
#kjre-main .cmpwrap:focus-visible,#main .cmpwrap:focus-visible{outline:2px solid var(--canopy);outline-offset:2px}

/* ---------- article template (spec 16) ----------
   Spec 16 has no locked prototype. It is written as requirements,
   and 187 live URLs land in it, so it is built here rather than
   invented per page. Every value below resolves to a token or is
   copied from the locked scale in 02-system.css. No new colour,
   no new face, no new spacing idea.

   The measure is the load bearing part. Spec 04 caps body copy at
   62ch to 74ch and says it is the first thing a page builder
   discards. Here the template owns it rather than the author. */

.artpage__cat{margin-bottom:clamp(14px,1.8vw,22px)}
.artpage__lede{margin-top:clamp(18px,2.2vw,28px)}

/* Publication date, and the last updated date only when it differs.
   Spec 16.1 asks for both, visibly, because a stale guide that looks
   current is the failure mode. */
.artmeta{display:flex;flex-wrap:wrap;gap:.5em 1.4em;margin-top:clamp(16px,2vw,24px);
  font-family:var(--body);font-size:.62rem;font-weight:500;letter-spacing:.18em;
  text-transform:uppercase;color:var(--ink-3)}
.artmeta__upd{color:var(--canopy)}

/* Hero image. Sized by the locked ratio system so nothing shifts. */
.artfig{padding-top:clamp(28px,4vw,52px)}
.artfig figure{margin:0}
/* 1.3.6: the hero honours its own locked ratio instead of being forced to
   16/9. A hero with no data-ratio keeps the old behaviour, so nothing that
   predates the change moves. The max-width caps stop a square or portrait
   hero filling the content box and pushing the article body off the fold;
   they are mirrored by the sizes values in kjre_ratios() so the browser
   never fetches wider than the slot. Every rule is scoped to .artfig, so no
   other hero on the site is affected. */
.artfig .kjre-img{aspect-ratio:16/9;margin-inline:auto}
.artfig .kjre-img[data-ratio="wide"]{aspect-ratio:21/9;max-width:100%}
.artfig .kjre-img[data-ratio="hero"]{aspect-ratio:16/9;max-width:100%}
.artfig .kjre-img[data-ratio="card"]{aspect-ratio:3/2;max-width:100%}
.artfig .kjre-img[data-ratio="frame"]{aspect-ratio:5/4;max-width:1000px}
.artfig .kjre-img[data-ratio="square"]{aspect-ratio:1/1;max-width:720px}
.artfig .kjre-img[data-ratio="plate"]{aspect-ratio:4/5;max-width:576px}

/* ---------- article body ----------
   Element selectors, because the words come from the editor and
   carry no classes. Heading sizes are the locked scale: H2 is .d2,
   H3 is .d3. Nothing new is defined. */
.artbody{padding-block:clamp(44px,6vw,86px)}
.art{max-width:70ch}
.art > * + *{margin-top:1.3em}
.art p,.art li{font-family:var(--body);font-size:.86rem;font-weight:300;
  color:var(--ink-2);line-height:1.74}
.art h2{font-family:var(--kjre-display);font-weight:400;
  font-size:clamp(2rem,4vw,3.4rem);line-height:1.06;letter-spacing:-.012em;
  text-wrap:balance;color:var(--ink);margin-top:1.6em}
.art h3{font-family:var(--kjre-display);font-weight:400;
  font-size:clamp(1.45rem,2.2vw,2.05rem);line-height:1.15;letter-spacing:-.005em;
  color:var(--ink);margin-top:1.5em}
.art h4{font-family:var(--body);font-size:.62rem;font-weight:600;letter-spacing:.2em;
  text-transform:uppercase;color:var(--ink-3);margin-top:1.8em}
.art ul,.art ol{padding-left:1.3em}
.art li + li{margin-top:.5em}
.art li::marker{color:var(--brass)}
/* Inline links in prose. The locked .ul is a UI link and is wrong
   inside a sentence, so prose links get an underline that survives
   descenders and a canopy hover, both from existing tokens. */
.art a{color:var(--ink);text-decoration:underline;text-decoration-color:var(--brass);
  text-underline-offset:.22em;text-decoration-thickness:1px;
  transition:color .3s var(--ease),text-decoration-color .3s var(--ease)}
.art a:hover{color:var(--canopy);text-decoration-color:var(--canopy)}
.art img{max-width:100%;height:auto}
.art figure{margin:0}
.art > figure + *{margin-top:1.6em}
/* A table dropped into the editor gets the locked comparison look
   rather than browser defaults. Values copied from .cmp in
   03-seller.css order, via 06-reloc.css. */
.art table{width:100%;border-collapse:collapse;display:block;overflow-x:auto}
.art th,.art td{text-align:left;vertical-align:top;
  padding:clamp(13px,1.5vw,19px) clamp(14px,1.6vw,22px);
  border-bottom:1px solid var(--rule-soft);font-size:.88rem;font-weight:300;
  color:var(--ink-2);line-height:1.6}
.art thead th{font-family:var(--kjre-display);font-size:1.05rem;font-weight:400;
  color:var(--ink);background:var(--paper-2);border-bottom:1px solid var(--rule)}

/* ---------- related links (spec 16.1, required) ---------- */
.rel{padding-block:clamp(40px,5vw,72px);border-top:1px solid var(--rule)}
.rel__list{list-style:none;margin:clamp(18px,2.4vw,30px) 0 0;padding:0;
  display:grid;gap:clamp(14px,1.8vw,22px)}
.rel__item{display:grid;grid-template-columns:auto minmax(0,1fr);
  gap:clamp(12px,1.8vw,24px);align-items:baseline;
  padding-bottom:clamp(14px,1.8vw,22px);border-bottom:1px solid var(--rule-soft)}
.rel__k{font-family:var(--body);font-size:.55rem;letter-spacing:.2em;
  text-transform:uppercase;color:var(--ink-3);white-space:nowrap}
.rel__a{font-family:var(--kjre-display);font-size:clamp(1.05rem,1.6vw,1.35rem);
  font-weight:400;line-height:1.3;color:var(--ink);text-decoration:none;
  border-bottom:1px solid var(--rule);transition:border-color .35s var(--ease),color .35s var(--ease)}
.rel__a:hover{color:var(--canopy);border-color:var(--canopy)}
@media (max-width:620px){.rel__item{grid-template-columns:1fr;gap:6px}}

/* ---------- author block (spec 16.1) ---------- */
.byl{padding-block:clamp(40px,5vw,72px);background:var(--paper-2)}
.byl__in{display:flex;flex-direction:column;align-items:flex-start;
  gap:clamp(10px,1.4vw,16px);max-width:62ch}
.byl__name{font-family:var(--kjre-display);font-size:clamp(1.2rem,2vw,1.6rem);
  font-weight:400;color:var(--ink);margin:0}
.byl__body{font-family:var(--body);font-size:.84rem;font-weight:300;
  color:var(--ink-2);line-height:1.72;margin:0}

/* ---------- article CTA (spec 16.1, required) ---------- */
.artcta{padding-block:clamp(48px,6vw,96px)}

/* ---------- article index grid (the Journal, as an index) ----------
   The .post card is locked and reused verbatim. This grid is not the
   locked .journal grid, deliberately: that one is a three card
   homepage teaser and hides its third child at the two column
   breakpoint, which would silently drop every third article from an
   index. Same card, honest container. */
.jlist{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(18px,2.4vw,34px) clamp(18px,2.4vw,34px)}
.jlist .post__plate{display:block;overflow:hidden}
.jlist .post__plate .kjre-img{aspect-ratio:16/10}
.jlist__a{color:inherit;text-decoration:none;border-bottom:1px solid transparent;
  transition:border-color .35s var(--ease),color .35s var(--ease)}
.jlist__a:hover{color:var(--canopy);border-color:var(--canopy)}
.jlist__meta{font-family:var(--body);font-size:.6rem;font-weight:500;letter-spacing:.18em;
  text-transform:uppercase;color:var(--ink-3);margin:0}
@media (max-width:1080px){.jlist{grid-template-columns:1fr 1fr}}
@media (max-width:680px){.jlist{grid-template-columns:1fr}}

.jpag{display:flex;flex-wrap:wrap;gap:.6em;margin-top:clamp(32px,4vw,60px);
  font-family:var(--body);font-size:.68rem;font-weight:600;letter-spacing:.16em;
  text-transform:uppercase}
.jpag a,.jpag span{padding:.7em 1em;border:1px solid var(--rule);color:var(--ink-2);text-decoration:none}
.jpag a:hover{border-color:var(--canopy);color:var(--canopy)}
.jpag .current{background:var(--ink);border-color:var(--ink);color:var(--paper)}

/* ---------- long text inside the locked accordion and article headings
   (1.3.1, from the Batch 1 migration measurements) ----------
   Measured on three of six migrated drafts at a 320px viewport:
   document scrollWidth 352 on draft 251, 330 on 255, 326 on 253.

   Cause, traced rather than guessed. The locked .q__btn is a flex row
   holding the question heading and the .q__ico marker. The heading is a
   flex item with the default min-width of auto, so it cannot shrink
   below its own content width, and a long question pushes the 13px
   marker past the container edge. On draft 251 the marker landed at
   x 352 inside a 320px viewport. A long article H2 on the same draft
   also overflowed its own box.

   Why it never appeared before: the four builder pages carry shorter
   questions in a wider container, so nothing in five releases of QA
   reached the width where the flex item stops fitting.

   The locked layers are untouched. These two rules let the existing
   text reflow inside the box the locked design already gives it. No
   token, no size, no spacing and no component markup changes. */
/* WIDENED IN 1.4.5, from a measurement on the migrated FAQ page.
   1.3.1 scoped this to .art because, in its words, "the four builder
   pages carry shorter questions in a wider container, so nothing in
   five releases of QA reached the width where the flex item stops
   fitting". The FAQ page migrated in 1.4.2 broke that assumption: at a
   390px viewport the document scrolled 402px, and the cause was the
   question "How well do you know Atlanta's neighborhoods?" laying out
   at its 348px max-content width inside a 253px box. Same component,
   same cause, same remedy, so the scope comes off rather than the fix
   being copied onto one more page. Verified in place at 390px: the
   heading wraps and document scrollWidth falls to 390. */
#kjre-main .q__btn h3,#main .q__btn h3{min-width:0;overflow-wrap:anywhere}
#kjre-main .art h2,#main .art h2{overflow-wrap:break-word}

/* ---------- the accordion never opened, 1.4.6 ----------
   SITE WIDE, PRE EXISTING, AND A LAUNCH BLOCKER. Found while testing
   the 1.4.5 FAQ wrap fix. Not caused by 1.4.5: the defect lives in
   locked 02-system.css, which the harness proves byte identical, and
   /buy/ reproduces it with no page scoped CSS of its own.

   WHAT WAS MEASURED. On /faq/ and on /buy/, clicking a question set
   .is-open and aria-expanded=true correctly, and the panel stayed at
   height 0. Every answer on the site was unreachable.

     /faq/  after clicking question 3: height 0, is-open true, aria true
     /buy/  initial heights 0,0,0,0,0,0,0,0, then height 0 when open

   The locked rule animates the panel open by transitioning the grid
   track:

     .q__panel{grid-template-rows:0fr;transition:grid-template-rows .5s var(--ease)}
     .q.is-open .q__panel{grid-template-rows:1fr}

   CAUSE, PROVEN BY ELIMINATION, NOT GUESSED. Five track sizing
   candidates were tried first and all five still measured 0:
   minmax(0,max-content), an inner min-height:auto, align-content:start,
   1fr with height:auto, and plain auto. That ruled out the track value
   as the cause, because a running transition outranks every author
   declaration for the property it owns. The decisive test removed only
   the transition:

     open item, height 0, rows 0px
     with transition:none   -> height 221, rows 220.562px
     transition restored    -> height 221, rows 220.562px
     closed, no transition  -> height 0
     reopened, no transition -> height 221, rows 220.562px

   So the 0fr to 1fr interpolation never completes on this engine and
   leaves the track pinned at its start value. The animation is the
   defect, not the layout.

   THE FIX. Take the transition off the track, which is the one
   declaration that fails, and give the reveal back to opacity on the
   inner wrapper, which the locked layer already gives overflow:hidden.
   The panel now snaps to its true height and the answer fades in.
   Nothing else about the component moves: no token, no size, no
   spacing, no markup, and the locked layers stay untouched.

   VERIFIED IN PLACE on the real top level page, scrolled into view,
   with the reveal animation already run:

     all closed: heights 0,0,0,0,0,0,0,0
     opened 0: height 99  opacity 1  readable true
     opened 3: height 123 opacity 1  readable true
     opened 7: height 148 opacity 1  readable true
     now open: 10010001
     closed 3 again: height 0, aria false
     document overflow with panels open: 0px */
#kjre-main .q__panel,#main .q__panel{transition:none}
#kjre-main .q__panel>div,#main .q__panel>div{opacity:0;transition:opacity .28s var(--ease)}
#kjre-main .q.is-open .q__panel>div,#main .q.is-open .q__panel>div{opacity:1}
@media (prefers-reduced-motion:reduce){
  #kjre-main .q__panel>div,#main .q__panel>div{transition:none}
}

/* Long words in article headings, 1.3.3.
   1.3.1 gave .art h2 a break, from the Batch 1 evidence. Batch 4 found the
   same overflow one level down and one level up: an H3 reading
   "4. HOA Fees (For Condos/Townhomes)" and an article H1 containing
   "Multigenerational" each pushed a 320px page to 360 and 362. Measured on
   drafts 510 and 470 before this rule existed, and 320 with it. Same
   treatment, no size or spacing change, no locked layer touched. */
#kjre-main .art h3,#main .art h3,#kjre-main .art h4,#main .art h4{overflow-wrap:break-word}
#kjre-main .artpage .hero h1,#main .artpage .hero h1{overflow-wrap:break-word}

/* ---------- the current market pathway at phone widths, 1.3.7 ----------
   Measured on the Morningside-Lenox Park guide at a 360px viewport:
   document scrollWidth 373 against a clientWidth of 360, 13px of
   horizontal overflow, with no element box exceeding the viewport.

   Cause, traced rather than guessed. The locked .mkthub in
   11-edgewood.css is a two track grid, auto then minmax(0,1fr), and
   .mkthub__k is white-space:nowrap. No layer declares a mobile
   override for it. At 360px the label CURRENT MORNINGSIDE-LENOX PARK
   MARKET cannot wrap, so the auto track takes 344px of the 320px the
   container has, the second track collapses to zero width, and the
   heading text in it spills out of the box. Measured: the label track
   at 344px, the second track at 0px, the h3 line boxes ending at 363
   to 367.

   Why it never appeared before: Edgewood, Kirkwood, Decatur, Inman
   Park, East Atlanta Village and Old Fourth Ward all carry labels
   short enough to fit, so six releases of QA never reached the length
   where the first track stops leaving room.

   The locked layer is untouched. Below the breakpoint the component
   stacks and the label wraps, which is what every other two track
   component in the system already does at phone widths. Desktop and
   tablet presentation is unchanged. No token, no size, no spacing and
   no component markup changes. */
@media (max-width:560px){
  #kjre-main .mkthub,#main .mkthub{grid-template-columns:minmax(0,1fr)}
  #kjre-main .mkthub__k,#main .mkthub__k{white-space:normal}
}

/* ---------- the events calendar, 1.4.4 ----------
   The /events page. Every value here is a token or is derived from one.
   No new colour, no new face, no new spacing scale.

   Why this layer and not a new one: layers 03 to 12 are locked
   component layers extracted from the prototypes, and the calendar has
   no prototype. 20-production.css is the only file in the stack whose
   rules exist because WordPress renders something the prototypes never
   had to, which is exactly what this is.

   Namespace evx. Checked against every layer in the stack before
   choosing it: no rule anywhere in 01 to 12 matches .ev, .evt, .evx or
   .evd, so this cannot collide the way .mkt did on the market
   dashboard. */

.evx{margin:0}
.evx--empty{max-width:62ch}

/* the area filter. Buttons, because they act on this page rather than
   navigating, and a row that wraps rather than scrolls so nothing is
   hidden off the edge at phone widths. */
.evx__filters{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 clamp(28px,4vw,44px)}
.evx__f{
  font-family:var(--body);font-size:12px;font-weight:500;letter-spacing:.08em;
  text-transform:uppercase;line-height:1.2;
  color:var(--ink-2);background:transparent;
  border:1px solid var(--rule);border-radius:999px;
  padding:9px 15px;cursor:pointer;
  transition:color .25s var(--ease),border-color .25s var(--ease),background-color .25s var(--ease);
}
.evx__f:hover{color:var(--ink);border-color:var(--ink-3)}
.evx__f:focus-visible{outline:2px solid var(--canopy);outline-offset:2px}
.evx__f.is-on{color:var(--paper);background:var(--canopy);border-color:var(--canopy)}
.evx__n{opacity:.62;font-variant-numeric:tabular-nums}
.evx__f.is-on .evx__n{opacity:.78}

/* month groups */
.evx__group{margin:0 0 clamp(40px,6vw,72px)}
.evx__group:last-of-type{margin-bottom:0}
.evx__month{
  font-family:var(--kjre-display);font-weight:400;
  font-size:clamp(20px,2.4vw,26px);line-height:1.2;letter-spacing:.01em;
  color:var(--ink);margin:0 0 20px;padding:0 0 12px;
  border-bottom:1px solid var(--rule-soft);
}

/* the card grid */
.evx__list{list-style:none;margin:0;padding:0;display:grid;gap:clamp(18px,2.4vw,28px);
  grid-template-columns:repeat(auto-fill,minmax(min(100%,300px),1fr))}
.evx__item{margin:0;min-width:0}
.evx__item[hidden]{display:none}
.evx__group[hidden]{display:none}

.evx__card{
  height:100%;display:flex;flex-direction:column;
  background:var(--paper-2);border:1px solid var(--rule-soft);
  overflow:hidden;
}

/* the image, when the source supplied one. There is no placeholder for
   when it did not: a card with no image keeps its own proportions and
   reads as a text card, which is a design decision rather than a gap. */
.evx__media{margin:0;aspect-ratio:16/9;overflow:hidden;background:var(--paper)}
.evx__media img{display:block;width:100%;height:100%;object-fit:cover}

.evx__body{padding:clamp(18px,2vw,24px);display:flex;flex-direction:column;gap:9px;flex:1 1 auto}

.evx__when{
  font-family:var(--body);font-size:11px;font-weight:600;letter-spacing:.12em;
  text-transform:uppercase;color:var(--canopy);margin:0;
}
.evx__title{
  font-family:var(--kjre-display);font-weight:400;
  font-size:clamp(17px,1.5vw,20px);line-height:1.25;color:var(--ink);margin:0;
}
.evx__where{font-family:var(--body);font-size:13px;line-height:1.5;color:var(--ink-3);margin:0}
.evx__desc{font-family:var(--body);font-size:14px;line-height:1.62;color:var(--ink-2);margin:0}

.evx__facts{display:flex;flex-wrap:wrap;gap:6px;margin:4px 0 0}
.evx__fact{
  font-family:var(--body);font-size:10px;font-weight:600;letter-spacing:.1em;
  text-transform:uppercase;color:var(--ink-3);
  border:1px solid var(--rule);padding:4px 8px;line-height:1;
}
.evx__fact--area{color:var(--brass);border-color:var(--brass)}

.evx__src{margin:auto 0 0;padding-top:12px;font-family:var(--body);font-size:13px;line-height:1.5;color:var(--ink-3)}
.evx__src a{color:var(--canopy)}

.evx__none{font-family:var(--body);font-size:15px;line-height:1.62;color:var(--ink-2);margin:0}
.evx__none[hidden]{display:none}

@media (max-width:560px){
  .evx__list{grid-template-columns:minmax(0,1fr)}
  .evx__f{font-size:11px;padding:8px 12px}
}


/* ---------- events typography against the Elementor kit, 1.4.5 ----------
   MEASURED ON THE PUBLISHED PAGE. The month headings rendered at 46px
   against the system's 26px, the card titles at 34px against 20px and
   the filter buttons at 16px against 12px.

   Cause, traced rather than guessed. The Elementor kit styles h2, h3
   and button through selectors of the shape .elementor-kit-6 h2, which
   at (0,1,1) out-specifies a single class selector at (0,1,0). This is
   the same cascade problem that made .btn--solid render ink on ink, and
   it is answered the same way the locked accordion and mkthub fixes
   above answer it, by qualifying with the main landmark id.

   No token, no size and no component markup changes: these restate the
   values already declared on .evx__month, .evx__title and .evx__f at a
   specificity the kit cannot beat. */
#kjre-main .evx__month,#main .evx__month{font-size:clamp(20px,2.4vw,26px);line-height:1.2;letter-spacing:.01em;font-weight:400}
#kjre-main .evx__title,#main .evx__title{font-size:clamp(17px,1.5vw,20px);line-height:1.25;letter-spacing:0;font-weight:400}
#kjre-main .evx__f,#main .evx__f{font-size:12px;line-height:1.2;font-weight:500;letter-spacing:.08em;text-transform:uppercase}
@media (max-width:560px){
  #kjre-main .evx__f,#main .evx__f{font-size:11px}
}

/* ---------- not found, and site search, 1.4.6 ----------
   Both arrived with 404.php and searchform.php. Until 1.4.6 there was
   no 404 template at all, so a missing path fell back to index.php and
   rendered a body headed "Archives" full of article content, and
   get_search_form() emitted WordPress's default markup with classes
   this system has never styled.

   Neither template needed a new component. 404.php is the locked hero
   plus section grammar, and searchform.php is the locked .field row
   plus the locked .btn--solid. What is left for this layer is the two
   things a template cannot express: how the four routes lay out, and
   how the search row lays out. Nothing here sets a colour, a font, a
   size or a border, so there is no value to drift from a token. */
.nf__routes{display:flex;flex-wrap:wrap;gap:12px;margin:0 0 56px}
.nf__find{max-width:56ch}
.nf__find .d2{margin:0 0 20px}
.nf__find p{margin:20px 0 0}

/* The search row puts the field and the button on one line, with the
   field taking the slack. min-width:0 is what stops the field from
   refusing to shrink below its own intrinsic width, which is the same
   flex behaviour that made the accordion question overflow at 320px. */
.nf__search{display:flex;align-items:flex-end;gap:16px;flex-wrap:wrap}
/* On the search results page the form sits above the result grid, so
   it needs the separation an index heading would otherwise give it.
   On the 404 page it is the last thing in its block and the margin
   collapses against the section, so one rule serves both. */
.nf__search{margin:0 0 48px}
.nf__search .field{flex:1 1 260px;min-width:0}
@media (max-width:480px){
  .nf__search{align-items:stretch}
  .nf__search .field{flex:1 1 100%}
}
