/* Kristi Marie Creative — shared chrome
   Reset, type, nav, buttons, links, footer, and the editorial motion system.
   Everything here is inherited by every page. Home, Rentals and Canvas add
   their own sections on top; none of them redefine what is in this file.

   Depends on tokens.css. Load order is tokens → base → page. */

/* ---------- reset ---------- */

*, *::before, *::after { box-sizing: border-box; margin: 0; }

html { scroll-behavior: smooth; }

body {
  background: var(--ground);
  color: var(--ink);
  font-family: var(--text);
  font-size: 1.125rem;
  line-height: 1.58;
  letter-spacing: -0.012em;
  -webkit-font-smoothing: antialiased;
}

/* height:auto is load-bearing, not tidiness. Every img here carries width and
   height attributes so the browser can reserve space, but without height:auto
   the attribute is treated as the used height — a 748px-tall tile in a 284px
   cell, at the correct width, overflowing. With it, the attributes become an
   aspect ratio instead, which is what they were added for. */
img { max-width: 100%; height: auto; display: block; }
a { color: inherit; }

/* <picture> is display:inline by default, so inside a grid or flex container
   IT becomes the item and the <img> stops being sized by the parent's rules.
   That shipped tiles at 75px and 28px wide inside a 216px cell. display:
   contents removes the wrapper's box and lets the img be the child the layout
   was written for. */
picture { display: contents; }

h1, h2, h3 {
  font-family: var(--serif);
  font-weight: 400;
  letter-spacing: -0.012em;
  text-wrap: balance;
}

/* Keyboard focus is visible everywhere. Components that want a different
   colour override the ring, never remove it. */
:focus-visible {
  outline: 2px solid var(--amber-deep);
  outline-offset: 3px;
}

.wrap {
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gut);
}

/* Reachable by keyboard, out of the way otherwise. */
/* A label that only a screen reader reads. The close's two fields are drawn
   with placeholders in the design; a placeholder is not a label — it vanishes
   the moment someone types, and it is not announced as one. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  background: var(--ink);
  color: var(--ground);
  font-family: var(--sans);
  font-size: var(--label-md);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 0.875rem 1.25rem;
}
.skip:focus { left: 0; }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------- shared marks ---------- */

.eyebrow {
  font-family: var(--sans);
  font-size: var(--label-md);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 400;
}
.eyebrow::before {
  content: "";
  display: inline-block;
  width: 16px; height: 1px;
  background: var(--amber);
  vertical-align: middle;
  margin-right: 10px;
}

.hr-amber {
  height: 2px;
  width: 44px;
  background: var(--amber);
  border: 0;
  margin: 0 0 22px;
}

/* ---------- topbar ---------- */

.topbar {
  font-family: var(--sans);
  font-size: var(--label-md);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  background: var(--ink);
  color: var(--ground);
  text-align: center;
  padding: 0.6875rem 1rem;
}
.topbar b { color: var(--honey); font-weight: 500; }

/* ---------- nav ---------- */

.nav {
  border-bottom: 1px solid var(--ink);
  position: sticky;
  top: 0;
  background: color-mix(in srgb, var(--ground) 94%, transparent);
  backdrop-filter: blur(10px);
  z-index: 70;
}
.nav .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 4.625rem;
  gap: 1.5rem;
}

.logo {
  /* Yields the width the wish list button will not: see the flex:none note
     there. A grid track's / flex item's automatic minimum is min-content, and
     "KRISTI MARIE CREATIVE" min-content is one long word — this lets the
     wordmark wrap to two lines instead of pushing the button off the row. */
  min-width: 0;
  font-family: var(--sans);
  font-size: var(--label-lg);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  text-decoration: none;
  font-weight: 400;
}

.navlinks {
  display: flex;
  gap: 1.75rem;
  font-family: var(--sans);
  font-size: var(--label-md);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  list-style: none;
  padding: 0;
  margin: 0;
}
/* ⚠️ THE SCALE COMES FROM THE KM BRANDS MOTION SUITE, v2 STANDALONE
   (Design Systems/00-KM Brands (shared)/motions, 2026-08-27), which is NEWER
   than the motions/motion.css beside it — that file is dated 08-13 and still
   carries the pre-v2 `.km-nav` with no transform at all. Read the standalone
   when the two disagree.
   ⚠️ transform-origin IS THE WHOLE TRICK. At 50% 100% the link grows from its
   own baseline, so the underline at `bottom: 0` stays exactly where it was
   drawn and only the word gets bigger. Scaling from the centre lifts the rule
   off the baseline with it and the draw stops reading as a draw.
   ⚠️ --ease-out IS --km-ease. Both are cubic-bezier(.22, 1, .28, 1); the token
   is used rather than the literal so the house owns the curve. */
.navlinks a {
  text-decoration: none;
  color: var(--muted);
  transition: color 0.2s, transform 0.28s var(--ease-out);
  transform-origin: 50% 100%;
  position: relative;
  padding: 4px 0;
  display: inline-block;
}
/* ⚠️ :focus-visible RIDES WITH :hover, WHICH THE SUITE'S OWN RULE DOES NOT DO.
   v2 writes `.km-nav:hover{transform:scale(1.15)}` and stops there, so a
   keyboard user would get the underline this file already gives them and none
   of the scale. Matching the suite literally would have been a regression
   against a parity this nav has had since it was written. */
.navlinks a:hover, .navlinks a:focus-visible { color: var(--ink); transform: scale(1.15); }
.navlinks a::after {
  content: "";
  position: absolute;
  left: 0; right: 100%; bottom: 0;
  height: 1px;
  background: var(--amber);
  transition: right 0.28s ease;
}
.navlinks a:hover::after, .navlinks a:focus-visible::after { right: 0; }
.navlinks a[aria-current="page"] { color: var(--ink); }
.navlinks a[aria-current="page"]::after { right: 0; }

/* The nav button takes the forward bloom, so the two primary actions on any
   page are the same control. It used to cross-fade a background and invert the
   label — which is the one thing the bloom was designed to avoid, because a
   colour flip has to land exactly as the fill arrives or it fails mid-way.
   ⚠️ The label stays INK throughout, on the ground and on the amber (5.19:1).
   Do not add a colour change back. */
/* ⚠️ THIS IS A `.btn btn--sm` NOW AND THIS RULE ONLY ADDS WHAT THE NAV NEEDS.
   Darrell, 2026-09-01: the growing seed in mist is retired site-wide, and this
   was the last control wearing it. Everything the button used to declare for
   itself — the border, the corner, the face, the tracking, the fill and its
   travel — comes from .btn and body.em .btn--sm, which is the point: one
   button, one place. What stays here is the nav's own business.

   ⚠️ THE SEED CLEARANCE WENT WITH THE SEED. That 1.65rem of left padding was
   there so an 11px dot at left:14px could not sit on the label's first letter
   — one of the three scars the bloom was designed against. With no dot there
   is nothing to clear, and body.em .btn--sm already carries the 8px start and
   the 36px of reserved travel on the right. Keeping the old padding would have
   left the label visibly off-centre against every other button on the page. */
.wishlist-btn {
  white-space: nowrap;
  /* ⚠️ flex:none IS THE FIX FOR THE MOBILE CLIP, and the cause is two lines
     up. `overflow: hidden` — which the bloom needs, so the seed cannot escape
     the button — changes this flex item's automatic minimum size from its
     content width to ZERO. The nav is a flex row, so at 390px it was free to
     squash a nowrap label to 140px and clip it: "RENTAL WISH LI". The label
     never wrapped and never shrank, so nothing looked broken in the CSS —
     only in the render. The wordmark beside it wraps and gives up the room. */
  flex: none;
}
/* ⚠️ `.wishlist-btn::before` WAS THE SEED AND IT IS GONE, 2026-09-01. It was a
   circle at left:14px scaling up on hover to flood the button — the bloom.
   emerald.css retired the same treatment on .btn--sm earlier ("The bloom is
   gone"); this was the one that was missed, which is why the nav button and
   the hero's buttons hovered differently on the same screen.
   Its --btn-dot / --btn-scale tokens are left where they are: nothing else
   reads them today, but they are the bloom's dimensions and deleting them
   would make the note above unverifiable. */

/* ---------- the primary nav on a phone ----------
   This used to be `@media (max-width: 900px) { .navlinks { display: none } }`
   and nothing else. The links were hidden and nothing replaced them, so from
   a phone the only navigation on the site was the wordmark and the wish list
   button — /about, /faq, /contact and /rentals/design were unreachable.
   Darrell found it on an iPhone, 2026-09-05.

   ⚠️ THE SAME <ul> IS THE ROW AND THE SHEET. It is hand-copied into seven
   pages; a separate mobile list would be seven more places for a link to go
   stale, and the stale one is always the one nobody sees on a laptop.

   The sheet is positioned against .nav, not .nav .wrap — .nav is sticky,
   which makes it the containing block already, and that is what lets the
   sheet run full-bleed under a bar whose contents are inset by the wrap. */

.navtoggle {
  display: none;
  flex: none;
  width: 2.75rem;
  height: 2.75rem;          /* 44px: the smallest thing a thumb should be asked to hit */
  margin-right: calc(var(--gut) * -0.5);
  align-items: center;
  justify-content: center;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--ink);
}
.navtoggle__bars,
.navtoggle__bars::before,
.navtoggle__bars::after {
  display: block;
  width: 1.25rem;
  height: 1px;
  background: currentColor;
  transition: transform 0.28s var(--ease-out), opacity 0.16s linear;
}
.navtoggle__bars::before, .navtoggle__bars::after { content: ''; position: absolute; }
.navtoggle__bars { position: relative; }
.navtoggle__bars::before { transform: translateY(-6px); }
.navtoggle__bars::after  { transform: translateY(6px); }

/* Open: the middle rule goes, the outer two cross. One control, two states,
   and the state is read from aria-expanded rather than a second class — the
   button already has to carry it for a screen reader, so nothing else should
   be able to disagree with it. */
.navtoggle[aria-expanded="true"] .navtoggle__bars { background: transparent; }
.navtoggle[aria-expanded="true"] .navtoggle__bars::before { transform: rotate(45deg); }
.navtoggle[aria-expanded="true"] .navtoggle__bars::after  { transform: rotate(-45deg); }

@media (max-width: 900px) {
  .navtoggle { display: inline-flex; }

  /* ⚠️ THE TOGGLE COSTS THE WORDMARK 44px AND IT SHOWS. "KRISTI MARIE
     CREATIVE" at 0.3em tracking wrapped to three lines once the button
     joined the row, and .nav .wrap is a fixed 4.625rem — so the bar grew to
     about 150px of a 844px phone before anything was read. Tightening the
     tracking and dropping a step of size puts it back on two lines without
     touching the desktop wordmark, which is where the 0.3em is doing its
     work. */
  .logo { font-size: 0.75rem; letter-spacing: 0.2em; }
  .nav .wrap { gap: 0.75rem; }

  .navlinks {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    gap: 0;
    padding: 0 0 0.5rem;
    background: var(--ground);
    border-bottom: 1px solid var(--ink);
    /* A short phone held sideways can be shorter than six rows. */
    max-height: calc(100dvh - 4.625rem);
    overflow-y: auto;
  }
  .navlinks[data-open] { display: flex; }

  .navlinks li { border-top: 1px solid var(--rule-line); }

  /* ⚠️ THE DESKTOP LINK IS A HOVER OBJECT AND A PHONE HAS NO HOVER. The
     scale-from-baseline and the drawn underline are both pointer
     affordances; left on, they fire on tap and read as a glitch. The row
     itself is the target here, full width, 44px tall. */
  .navlinks a {
    display: block;
    padding: 0.9rem var(--gut);
    transform: none;
    color: var(--ink);
  }
  .navlinks a::after { content: none; }
}

/* ---------- forward bloom button ----------
   Outlined at rest; the amber seed blooms to fill on hover.
   Three inherited scars are designed out rather than tuned:
     · the dot animates on transform only, never width/height
     · the scale is a token, so it is not re-guessed per button
     · the seed sits clear of the label's left edge
   And the label is ink at rest AND on the amber fill (5.19:1), so there is no
   delayed colour change that could fail mid-bloom. */

/* ---------- back reveal button (replaces the forward bloom, 2026-08-31) ----------
   Darrell's call out of the CTA Motion Lab: 06 back reveal, a fill opening from
   the left edge, with the label starting left-justified and travelling to
   right-justified as it lands.

   ⚠️ SCOPED AWAY FROM .btn--gilt, AND THAT IS LOAD-BEARING.
   The ember is class="btn btn--gilt", so an unscoped .btn rule here lands on it
   and rewrites its display mode, padding and width — silently wrecking the one
   control the system spends its gold on. Every rule below carries :not(.btn--gilt).

   ⚠️ THE LABEL IS RENDERED THREE TIMES. Each copy earns its place:
     · the direct child .lb is a SIZER, visibility:hidden. It is the only thing
       giving the button intrinsic width; every visible copy is absolute.
     · .track .grp is the resting label, on paper.
     · .reveal .track .grp is the same label on the fill, clipped to it.
   The third copy is not decoration. A single label that changes colour under a
   dark fill measured 1.61:1 mid-transition in the lab — both endpoints passing,
   the middle failing, which is exactly the false pass button-motion.md records
   and render.mjs:505 samples eight frames to catch. Two copies means every
   pixel is either --amber-deep on paper or the fill label on the fill. Nothing
   blends, so no timing can be wrong.

   ⚠️ THE TRAVEL IS `left` AND `transform` TOGETHER. left:0→100% with
   translateX(0)→-100% walks the group from left-aligned to right-aligned
   whatever the label says; a fixed pixel translate breaks the moment copy
   changes length. And the RIGHT PADDING reserves the travel room, so every
   button moves the same 28px — do not lean on min-width for it. */

/* The shared base every .btn still needs — the ember included. Splitting this
   out is not tidiness: scoping the WHOLE rule to :not(.btn--gilt) left the
   ember with no font-family at all and it fell back to Arial, which
   house.mjs caught as an undeclared family. Typography and the resting
   appearance are shared; only the back reveal's layout and motion are not. */
.btn {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  display: inline-block;
  font-family: var(--sans);
  font-size: var(--label-lg);
  letter-spacing: 0.10em;
  text-transform: uppercase;
  padding: 1rem 2rem;
  background: transparent;
  color: var(--ink);
  text-decoration: none;
  border: 1.5px solid var(--amber);
  border-radius: var(--radius);
  cursor: pointer;
  line-height: inherit;
  vertical-align: top;
}

.btn:not(.btn--gilt) {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  gap: 9px;
  font-family: var(--sans);
  font-size: var(--label-lg);
  font-weight: 400;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  padding: 16px 36px 16px 8px;   /* 8 right + 28 of reserved travel */
  background: transparent;
  color: var(--amber-deep);
  text-decoration: none;
  border: 1.5px solid var(--amber);
  border-radius: var(--radius);
  cursor: pointer;
  line-height: 1.15;
  white-space: nowrap;
  vertical-align: top;
  transition: border-color var(--btn-dur) var(--btn-ease),
              transform .18s cubic-bezier(.22, 1, .28, 1);
}
.btn:not(.btn--gilt) > .lb,
.btn:not(.btn--gilt) > .arrow { visibility: hidden; }   /* the sizer */

/* the run both label copies travel along — the content box, identically */
.btn:not(.btn--gilt) .track {
  position: absolute; top: 0; bottom: 0;
  left: 8px; right: 8px;
  pointer-events: none;
}
.btn:not(.btn--gilt) .track .grp {
  position: absolute; top: 50%; left: 0;
  display: flex; align-items: center; gap: 9px; white-space: nowrap;
  transform: translateY(-50%);
  transition: left var(--btn-dur) var(--btn-ease),
              transform var(--btn-dur) var(--btn-ease);
}
.btn:not(.btn--gilt):hover .track .grp,
.btn:not(.btn--gilt):focus-visible .track .grp {
  left: 100%; transform: translate(-100%, -50%);
}

/* ⚠️ THE ARROW IS OUT OF FLOW, SO THE LABEL IS WHAT RIGHT-JUSTIFIES.
   Darrell, 2026-09-01: on a button WITH an arrow he wants "the text to land
   right justified, just like the buttons without the arrow, pushing the arrow
   off the button so it is no longer visible. Then when a hover is disengaged,
   the arrow returns from outside of the right side of the button to land in
   its starting position."

   The rule above right-justifies the GROUP. With the arrow in flow the group
   is [label][gap][arrow], so what lands against the track's right edge is the
   arrow, and the label stops short by the arrow's width plus the gap. Nine of
   the twelve buttons on this site have no arrow and land their label flush;
   the three that do land it 23px shy of the same edge, on the same screen.
   That is the misalignment, and it is a layout fact rather than a taste call.

   Taking the arrow out of flow makes the group's measured width the label's
   width, so ONE rule now right-justifies the label on every button, arrow or
   not. The arrow rides at left:100% of that group, which at rest is exactly
   where the 9px flex gap used to put it — nothing moves at rest — and on
   hover is 9px past the track's right edge, which is 1px outside the button's
   own border. .btn is overflow:hidden, so it is gone rather than merely
   nudged, and it travels back in from outside on the way out of hover.

   ⚠️ NO MAGIC NUMBER, AND THAT IS THE POINT. The obvious alternative is to
   translate the group an extra "arrow width plus gap" on hover, which means
   hard-coding the rendered width of → in Poppins at --label-lg. Change the
   face, the size or the glyph and the label stops landing flush again, silently.
   Position does the measuring instead. */
.btn:not(.btn--gilt) .track .grp .arrow {
  position: absolute;
  left: 100%;
  margin-left: 9px;
  top: 50%;
  transform: translateY(-50%);
}

/* the fill, carrying its own copy of the label, clipped as one */
.btn:not(.btn--gilt) .reveal {
  position: absolute; inset: 0; z-index: 1;
  background: var(--btn-fill);
  color: var(--btn-on-fill);
  clip-path: circle(0% at 0% 50%);
  transition: clip-path var(--btn-dur) var(--btn-ease);
}
.btn:not(.btn--gilt):hover .reveal,
.btn:not(.btn--gilt):focus-visible .reveal { clip-path: circle(142% at 0% 50%); }

/* ⚠️ An atomic inline box. text-decoration set on an ancestor propagates down
   and paints through descendants, and a descendant cannot cancel it at any
   specificity. This is the firebreak, not styling. */
.btn:not(.btn--gilt) .lb i {
  display: inline-block; font-style: normal; text-decoration: none;
}
/* Poppins ships 400 and 500 on the emerald pages and nothing else
   (emerald.css:387), so this is the whole sanctioned range. Static family, so
   the face swaps at the midpoint rather than easing — a quiet step, not a morph. */
.btn:not(.btn--gilt) .lb {
  font-weight: 400;
  transition: font-weight var(--btn-dur) var(--btn-ease);
}
.btn:not(.btn--gilt):hover .lb,
.btn:not(.btn--gilt):focus-visible .lb { font-weight: 500; }

.btn:active { transform: translateY(1px); }

/* No sheen sweep here on purpose. The forward bloom is the KM Brands button
   and it is already rolled out on the KME Shop; a second motion on the same
   control would compete with it rather than add to it. The sheen was built,
   measured and parked — recipe and its measured contrast ceiling are in
   PATTERNS.md. */

/* .btn--ink is gone, 2026-08-31. It was kept on the argument that "Rentals
   will want a second button weight"; Rentals took the ember instead, and the
   bloom it depended on no longer exists. Its own comment said to delete it
   rather than leave dead CSS if it stayed unused. It did. */


.text-link {
  font-family: var(--sans);
  font-size: var(--label-md);
  /* 600. Poppins is requested at 300/400/500/600 — the 600 exists for exactly
     this. Do not raise this past a weight that is actually loaded: the browser
     will not refuse, it will synthesise the bold by smearing the 400 glyphs,
     and on uppercase set at 0.11em tracking that shows immediately. */
  font-weight: 600;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--amber-deep);
  position: relative;
  padding-bottom: 4px;
  /* inline-flex so the arrow sits on the text's own baseline row and the
     underline below spans label and arrow together. */
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  /* ⚠️ THE SCALE COMES FROM THE KM BRANDS MOTION SUITE, v2 STANDALONE
     (2026-08-27), which is newer than the motion.css beside it — that file is
     dated 08-13 and its `.km-link` has no transform. Read the standalone when
     the two disagree; the same split produced the nav's scale.
     ⚠️ transform-origin IS `left center` HERE AND `50% 100%` ON THE NAV, and
     the difference is deliberate in the suite. A nav item is centred over its
     own underline, so it grows from its baseline. A text link starts at a left
     edge with an arrow on the right, so it grows rightward and the first letter
     stays put — anything else slides the label out from under the eye that is
     already reading it.
     ⚠️ --ease-out IS --km-ease: both cubic-bezier(.22, 1, .28, 1). */
  transition: transform 0.28s var(--ease-out);
  transform-origin: left center;
}
.text-link:hover, .text-link:focus-visible { transform: scale(1.12); }
.text-link::after {
  content: "";
  position: absolute;
  left: 0; right: 100%; bottom: 0;
  height: 1px;
  background: var(--amber);
  transition: right 0.28s ease;
}
.text-link:hover::after, .text-link:focus-visible::after { right: 0; }

/* The arrow is markup, not generated content, and it carries aria-hidden —
   same as .col-go's. A pseudo-element would be terser but several screen
   readers do announce ::after content, and "Read our story right arrow" is
   noise. This way the accessible name stays the label alone.

   Matches .col-go .arrow's 5px nudge and timing on purpose: two arrows moving
   by different amounts on the same page reads as a mistake. */
.text-link .arrow {
  display: inline-block;
  transition: transform 0.28s var(--ease-out);
}
.text-link:hover .arrow, .text-link:focus-visible .arrow { transform: translateX(5px); }

@media (prefers-reduced-motion: reduce) {
  .text-link::after { transition: none; }
  .text-link .arrow { transition: none; }
  .text-link:hover .arrow, .text-link:focus-visible .arrow { transform: none; }
  /* The transform is the motion, so it is what comes out — stilling only the
     transition would leave the link snapping to 1.12 rather than easing to it.
     Same correction the nav's block makes against the suite's own reduced
     block, which stills transitions and leaves the transforms in place. */
  .text-link { transition: none; }
  .text-link:hover, .text-link:focus-visible { transform: none; }
}

/* ---------- section scaffolding ---------- */

.sec { padding: var(--sec-pad) 0; }
.sec--tight { padding-top: 0; }
.sec-head { margin-bottom: 3.25rem; }
.sec-head h2 { font-size: clamp(1.875rem, 3.6vw, 2.875rem); line-height: 1.06; max-width: 16em; }
.sec-head p { color: var(--muted); max-width: 36em; margin-top: 1.125rem; font-size: 1.25rem; }

/* ---------- footer ---------- */

.site-footer { border-top: 2px solid var(--ink); padding: 4.75rem 0 3.5rem; }
.foot-grid { display: grid; grid-template-columns: 1.4fr 1fr 1.2fr; gap: 3.5rem; }
.foot-wordmark {
  font-family: var(--sans);
  font-size: var(--label-lg);
  letter-spacing: 0.3em;
  text-transform: uppercase;
}
.foot-tag { margin-top: 0.875rem; font-style: italic; font-size: var(--text-sm); color: var(--muted); max-width: 30ch; }
.foot-contact { margin-top: 1.5rem; display: grid; gap: 0.375rem; font-size: 1rem; }
.site-footer a { text-decoration: none; color: var(--ink); }
.site-footer a:hover, .site-footer a:focus-visible { color: var(--amber-deep); }
.foot-col-title {
  font-family: var(--sans);
  font-size: var(--label-sm);
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 1rem;
}
.foot-links { list-style: none; padding: 0; display: grid; gap: 0.5rem; font-size: 1rem; }
.foot-body { font-size: 1rem; color: var(--ink); }
.foot-bottom {
  border-top: 1px solid var(--rule-line);
  margin-top: 3.5rem;
  padding-top: 1.5rem;
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  font-family: var(--sans);
  font-size: var(--label-sm);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.foot-bottom a { color: var(--muted); }

@media (max-width: 900px) { .foot-grid { grid-template-columns: 1fr; gap: 2.5rem; } }

/* ============================================================
   EDITORIAL MOTION
   One-shot arrivals. Nothing scroll-linked, nothing pinned, nothing looping.

   Every hiding rule is gated behind html.mo, which JS adds. No JS means no
   hiding, so a script failure renders a complete page instead of a blank one.

   Reveals are reversible — the observer toggles rather than latches — so each
   one is read more than once. Timings run ~35% longer than the originals to
   suit that.
   ============================================================ */

.mo [data-type] .w {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.42em);
  transition: opacity 0.7s ease, transform 0.85s var(--ease-out);
  transition-delay: calc(var(--w) * 52ms);
}
.mo [data-type].is-in .w { opacity: 1; transform: none; }

/* ⚠️ THE REVEAL MOVES WITH `translate`, NOT `transform`, AND THAT IS A FIX
   RATHER THAN A STYLE. Darrell, 2026-09-01: "this link motion should match the
   READ OUR STORY link above and be applied throughout the site."

   He was looking at two identical `a.text-link`s, one of which grew on hover
   and one of which did not. The only difference between them was `data-rise`,
   and the reason is specificity: `.mo [data-rise].is-in { transform: none }` is
   (0,3,0) and `.text-link:hover { transform: scale(1.12) }` is (0,2,0), so the
   settled reveal state won and the hover silently did nothing. Nineteen
   elements on the homepage carry data-rise; every one of them that also has a
   hover transform lost it the same way.

   ⚠️ SO THE AUDIT HE ASKED FOR IS ANSWERED HERE INSTEAD OF ELEMENT BY ELEMENT.
   Raising specificity on each hover rule would have fixed the ones anyone
   thought to look at and left the rest, and it would have to be done again
   every time a hover transform is added anywhere. `translate` and `transform`
   are separate properties that the browser composes, so a reveal that uses one
   can never overwrite a hover that uses the other. There is no specificity
   contest left to lose.

   ⚠️ ORDER OF COMPOSITION IS translate, rotate, scale, THEN transform — the
   same rule that put the drop ring off-centre on the canvas. Here it is
   harmless: at rest the reveal is finished and its translate is none, so the
   hover's scale is the only thing acting. */
.mo [data-rise] {
  opacity: 0;
  translate: 0 24px;
  transition: opacity 0.85s ease, translate 0.98s var(--ease-out);
  transition-delay: calc(var(--i, 0) * 95ms);
}
.mo [data-rise].is-in { opacity: 1; translate: none; }

.mo [data-draw] {
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 1.05s var(--ease-out);
}
.mo [data-draw].is-in { transform: scaleX(1); }

.mo [data-plate] { overflow: hidden; }
.mo [data-plate] img {
  opacity: 0;
  transform: scale(1.045);
  filter: blur(9px);
  transition: opacity 1.2s ease, transform 1.5s var(--ease-out), filter 1.25s ease;
}
.mo [data-plate].is-in img { opacity: 1; transform: none; filter: blur(0); }

/* THE DEAL — rows deal in, a shorter throw than the rise.
   The house library's third arrival: a card or a row lands from 14px with a
   62ms stagger, where [data-rise] lands from 24px at 95ms. It exists because a
   ROW of siblings wants a quicker, tighter hand than a stack of blocks — dealt
   like cards, not raised like a curtain. Same one-shot discipline, same --i. */
.mo [data-deal] {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.6s ease, transform 0.72s var(--ease-out);
  transition-delay: calc(var(--i, 0) * 62ms);
}
.mo [data-deal].is-in { opacity: 1; transform: none; }

/* ⚠️ THE WINK WAS HERE AND WENT ON 2026-08-30. A row arrived making the
 * colour move a hover would, staggered 900ms apart, so a directory of three
 * links landed as three separate thoughts rather than one block of hairlines.
 *
 * Darrell: it was written for a text-heavy directory that needed telling it
 * was interactive at all. The short rows that replaced it had nothing for it
 * to explain, so what was left was type moving on a timer.
 *
 * ⚠️ THE RULE IT WENT OUT UNDER, and it governs anything added here next:
 * A BUMP OR A WINK BELONGS TO AN INTERACTION, NOT TO ARRIVAL. Hover, focus or
 * press — or at minimum scroll-LINKED, so the reader drives it and stopping
 * stops it. What is forbidden is the middle case: an element becomes visible,
 * a timer starts, and type moves while somebody is trying to read it.
 * This does NOT retire the arrival reveals below. A rise or a draw brings an
 * element INTO its resting state once and leaves it alone; the wink moved
 * something already placed and readable. That is the line.
 *
 * The animation itself is kept in §10 of the shared motions doc
 * (Design Systems/00-KM Brands (shared)/motions/motion.css), not lost.
 */

/* prefers-reduced-motion neutralises every reveal and every hover transition
   that moves something. Colour changes are left alone; they do not induce
   motion sickness and removing them would cost the hover affordance. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .mo [data-type] .w,
  .mo [data-rise],
  .mo [data-deal],
  .mo [data-draw],
  .mo [data-plate] img {
    opacity: 1 !important;
    /* ⚠️ BOTH PROPERTIES, because the rise moved to `translate` and the others
       still use `transform`. Neutralising only one would leave every risen
       element sitting 24px low for a reader who asked for no motion — the
       reveal never runs, so nothing would ever bring it back up. */
    transform: none !important;
    translate: none !important;
    filter: none !important;
    transition: none !important;
  }
  /* Keep the colour and the fill's arrival; drop the movement. A hover that
     does nothing visible is a regression, not a graceful degradation. */
  .btn .reveal, .btn .track .grp, .btn .lb { transition: none; }
  .navlinks a::after, .text-link::after { transition: none; }
  /* The suite's reduced block only stills the transition, which would leave a
     nav link SNAPPING to 1.15 instead of easing to it — the transform is the
     motion, so it is the thing to remove. Same shape as .text-link .arrow
     directly above: kill the transition and the destination. */
  .navlinks a { transition: none; }
  .navlinks a:hover, .navlinks a:focus-visible { transform: none; }
}


/* ---------- accented copy ----------
 *
 * One idea per paragraph, lifted out of the grey so a scanning eye lands on
 * the thing that actually decides something: a number, a promise, a
 * differentiator. Amber ink, a half-step of weight, nothing else — no
 * highlight, no underline, no size change. Colour as ink, never as area.
 *
 * ⚠️ Restraint is the whole mechanism. Accent two or three things in a
 * paragraph and none of them stand out; the reader is back to reading every
 * word, which is the state this exists to rescue them from. One per
 * paragraph, and plenty of paragraphs with none.
 *
 * Scoped to <p> deliberately: <strong> inside a heading or a nav item means
 * something different and should not be recoloured.
 */
p strong {
  color: var(--amber-deep);
  font-weight: 500;
}
