﻿/* ==========================================================================
   MOBILSISTEMI — components.css
   Buttons, cards, frames, forms, tables, logo, indicators.
   Every value comes from tokens.css.
   ========================================================================== */

/* ==========================================================================
   THE LOGO
   Styled by class only — the traced SVG carries no fill attributes, so the
   brand colours never leave tokens.css.
   ========================================================================== */

/* The three traced layers are used as CSS masks and tinted from tokens, so a
   19 KB logo is fetched once and cached rather than inlined into every page,
   and the wordmark can still flip colour per surface. */
.logo{
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 400 / 60;
  --logo-word: var(--ink);
}
.logo > span{
  position: absolute;
  inset: 0;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center;  mask-position: center;
  -webkit-mask-size: contain;     mask-size: contain;
}
.logo__word{
  -webkit-mask-image: url('/assets/images/logo-word.svg');
  mask-image: url('/assets/images/logo-word.svg');
  background: var(--logo-word);
}
.logo__rule{
  -webkit-mask-image: url('/assets/images/logo-rule.svg');
  mask-image: url('/assets/images/logo-rule.svg');
  background: var(--red);
}
.logo__figure{
  -webkit-mask-image: url('/assets/images/logo-figure.svg');
  mask-image: url('/assets/images/logo-figure.svg');
  background: var(--orange);
}
/* On a dark pill or the footer the wordmark takes the surrounding colour. */
.logo--adaptive{ --logo-word: currentColor; }

/* Without mask support the untinted PNG stands in. */
.logo__fallback{ position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
@supports ((-webkit-mask-image: url('/assets/images/logo-rule.svg')) or (mask-image: url('/assets/images/logo-rule.svg'))){
  .logo__fallback{ display: none; }
}
@supports not ((-webkit-mask-image: url('/assets/images/logo-rule.svg')) or (mask-image: url('/assets/images/logo-rule.svg'))){
  .logo > span{ display: none; }
}

/* The full traced SVG — only the preloader uses it, fetched on demand. */
.logo-svg{ width: 100%; height: auto; display: block; overflow: visible; }
.logo-svg__glyph { fill: var(--ink); }
.logo-svg__rule  { fill: var(--red); }
.logo-svg__figure{ fill: var(--orange); }

.logo-img{ width: auto; height: 28px; }

/* ==========================================================================
   BUTTONS
   Always shipped as a pair: the two notches face each other so the group
   reads as a single cut form.
   ========================================================================== */

.btn-group{
  display: inline-flex;
  align-items: stretch;
  gap: 4px;
  text-decoration: none;
  vertical-align: middle;
}

.btn{
  --btn-bg: var(--ink);
  --btn-fg: var(--paper);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
  min-height: 48px;
  padding: 0 var(--s-6);
  background: var(--btn-bg);
  color: var(--btn-fg);
  border-radius: var(--r-md);   /* fallback shape before notch.js measures */
  font: 500 0.78rem/1 var(--font-mono);
  letter-spacing: var(--track-mono-ui);
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  /* Four things want to move a button: the entrance, the magnetic pull, the
     hover lift and the press. They are composed into one declaration instead
     of each writing its own `transform`, because the last selector to win
     would otherwise silently cancel the other three — which is exactly what
     the hero's entrance did to the magnet.

     Four separate functions, not one calc() summing four custom properties:
     that version parsed as invalid and the whole declaration was dropped, so
     the buttons had no transform at all and every state below did nothing. */
  transform:
    translate(var(--mx, 0px), var(--my, 0px))   /* magnetic pull   */
    translateY(var(--enter, 0px))               /* entrance        */
    translateY(var(--lift, 0px))                /* hover lift (negative) */
    scale(var(--press, 1));                     /* press           */
  transition: background-color var(--dur-sm) var(--ease-soft),
    color var(--dur-sm) var(--ease-soft),
    transform var(--dur-sm) var(--ease-soft);
}

.btn--primary{ --btn-bg: var(--ink); --btn-fg: var(--paper); }

/* White on --orange measures 2.62:1 — below even the 3:1 non-text floor.
   Ink on --orange is 6.23:1 and matches the logo's own black-on-orange pairing,
   so every label sitting on an orange fill is --ink. See TODO.md. */
.btn--icon{
  --btn-bg: var(--orange);
  --btn-fg: var(--ink);
  min-width: 48px;
  padding: 0;
  font-size: 1.05rem;
  font-weight: 400;
  line-height: 0;
}

/* A stroked button cannot work here: clip-path cuts the border off along the
   chamfer, leaving the signature edge unpainted. Both secondary variants are
   therefore filled — a raised paper chip and a tinted chip — so the cut edge
   always reads. */
.btn--ghost{
  --btn-bg: rgb(var(--ink-rgb) / .07);
  --btn-fg: var(--fg);
}

.btn--outline{
  --btn-bg: var(--surface);
  --btn-fg: var(--fg);
  box-shadow: var(--shadow-1);
}

body.on-dark .btn--ghost,
.on-dark-block .btn--ghost{ --btn-bg: rgb(255 255 255 / .1); }
body.on-dark .btn--outline,
.on-dark-block .btn--outline{ --btn-bg: var(--dark-2); box-shadow: none; }

/* Hover — the whole pair moves together, whichever half is pointed at. */
@media (hover: hover){
  .btn-group:hover .btn--primary,
  .btn--primary:hover{
    --btn-bg: var(--orange);
    --btn-fg: var(--ink);
    --lift: -2px;
  }
  .btn-group:hover .btn--icon,
  .btn--icon:hover{
    --btn-bg: var(--ink);
    --btn-fg: var(--paper);
    --lift: -2px;
  }
  .btn-group:hover .btn--ghost,
  .btn--ghost:hover{
    --btn-bg: var(--ink);
    --btn-fg: var(--paper);
    --lift: -2px;
  }
  .btn-group:hover .btn--outline,
  .btn--outline:hover{
    --btn-bg: var(--orange);
    --btn-fg: var(--ink);
    --lift: -2px;
  }
}
.btn-group:active .btn,
.btn:active{ --lift: 0px; --press: .985; }

/* While the magnet is driving --mx/--my the transform is already being eased
   per frame in JS, so the CSS transition on top of it is a second smoothing
   of the same value — the button ends up trailing the cursor by both, which
   reads as lag rather than as weight. magnet.js adds this class while it is
   actively moving an element and removes it on release, so the hover lift
   keeps its transition and the magnetic follow does not. */
.btn.is-magnetic{
  transition: background-color var(--dur-sm) var(--ease-soft),
              color var(--dur-sm) var(--ease-soft);
}

/* On dark surfaces the primary inverts so it never disappears. */
body.on-dark .btn--primary,
.on-dark-block .btn--primary{ --btn-bg: var(--paper); --btn-fg: var(--ink); }

.btn[aria-disabled="true"], .btn:disabled{
  opacity: .45; pointer-events: none;
}

/* ---------- INLINE ARROW LINK ---------- */
.link{
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font: 500 0.78rem/1 var(--font-mono);
  letter-spacing: var(--track-mono-ui);
  text-transform: uppercase;
  padding-bottom: 4px;
  border-bottom: 1.5px solid var(--hairline);
  transition: color var(--dur-sm) var(--ease-soft),
              border-color var(--dur-sm) var(--ease-soft);
}
.link__arrow{ transition: transform var(--dur-sm) var(--ease-soft); }
@media (hover: hover){
  /* --orange-deep is 3.11:1 on paper: fine for large display text, not for a
     12.5px mono label. The hover state is carried by the rule and the arrow. */
  .link:hover{ border-color: var(--orange); }
  .link:hover .link__arrow{ transform: translate(3px, -3px); }
}
body.on-dark .link:hover{ color: var(--orange); }

/* Text link inside prose */
.link-text{
  border-bottom: 1.5px solid var(--orange);
  transition: color var(--dur-sm) var(--ease-soft),
              border-color var(--dur-sm) var(--ease-soft);
}
.link-text:hover{ border-color: var(--red); }

/* ==========================================================================
   FRAME — a notched media container
   ========================================================================== */

.frame{
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: var(--r-md);   /* fallback before notch.js */
  background: var(--paper-warm);
}
.frame > img,
.frame > picture > img{
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  transition: transform var(--dur-lg) var(--ease-out);
}
.frame--4x3{ aspect-ratio: 4 / 3; }
.frame--3x2{ aspect-ratio: 3 / 2; }
.frame--3x4{ aspect-ratio: 3 / 4; }
.frame--1x1{ aspect-ratio: 1 / 1; }
.frame--4x5{ aspect-ratio: 4 / 5; }
.frame--16x9{ aspect-ratio: 16 / 9; }

/* Text over a photograph.
   A scrim, not opacity: dropping the image's own opacity greys the whole
   frame toward the page background and the photograph goes flat and milky.
   An overlay leaves the image its contrast and only darkens what is behind
   the type. */
.frame--veiled::after{
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(rgb(20 20 20 / .55), rgb(20 20 20 / .7));
  pointer-events: none;
}
.frame--veiled > *{ position: relative; }

@media (hover: hover){
  .frame--zoom:hover > img,
  .frame--zoom:hover > picture > img,
  .card:hover .frame > img,
  .card:hover .frame > picture > img{ transform: scale(1.06); }
}

/* ==========================================================================
   CARD — the three business lines
   ========================================================================== */

.card{
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  color: inherit;
  isolation: isolate;
}
.card .frame{ margin-bottom: var(--s-2); }

.card__index{
  font: 500 0.6875rem/1.5 var(--font-mono);
  letter-spacing: var(--track-mono-label);
  color: var(--fg-3);
}
.card__title{
  font: 800 clamp(1.5rem, 2.4vw, 2.1rem)/1.0 var(--font-display);
  letter-spacing: var(--track-display-m);
  text-transform: uppercase;
  transition: color var(--dur-sm) var(--ease-soft);
}
.card__text{
  font: 400 .9375rem/1.55 var(--font-body);
  color: var(--fg-2);
  max-width: 42ch;
}
.card__rule{
  height: 3px; background: var(--red); width: 40px;
  transform-origin: left center;
  transition: width var(--dur-md) var(--ease-out);
}
.card__foot{
  margin-top: auto;
  padding-top: var(--s-4);
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4);
}

@media (hover: hover){
  .card:hover .card__title{ color: var(--orange-deep); }
  .card:hover .card__rule{ width: 72px; }
}
body.on-dark .card:hover .card__title{ color: var(--orange); }

/* ==========================================================================
   TAGS & PILLS
   ========================================================================== */

.tag{
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: 6px 12px;
  border-radius: var(--r-pill);
  border: 1px solid var(--hairline);
  font: 500 0.6875rem/1 var(--font-mono);
  letter-spacing: var(--track-mono-ui);
  text-transform: uppercase;
  color: var(--fg-2);
  white-space: nowrap;
}
.tag--solid{ background: var(--ink); border-color: var(--ink); color: var(--paper); }
.tag--orange{ background: var(--orange); border-color: var(--orange); color: var(--ink); }

/* ==========================================================================
   META ROW — the Kononenko-style specification table
   ========================================================================== */

.meta-row{
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: var(--s-5);
  padding-block: var(--s-5);
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
}
.meta-row__cell{ display: flex; flex-direction: column; gap: 6px; }
.meta-row__label{
  font: 500 0.6875rem/1.4 var(--font-mono);
  letter-spacing: var(--track-mono-label); text-transform: uppercase; color: var(--fg-3);
}
.meta-row__value{
  /* 1.2rem/700 clears 18.66px bold, so --orange-deep on the accent variant
     qualifies as large text (3.11:1 against the 3:1 AA threshold). */
  font: 700 1.2rem/1.25 var(--font-display);
  letter-spacing: var(--track-heading);
}
.meta-row__value--accent{ color: var(--orange-deep); }
body.on-dark .meta-row__value--accent{ color: var(--orange); }

/* Definition list variant used on model pages */
.spec-list{ border-top: 1px solid var(--hairline); }
.spec-list__row{
  display: grid;
  grid-template-columns: minmax(140px, 22%) 1fr;
  gap: var(--s-5);
  padding-block: var(--s-4);
  border-bottom: 1px solid var(--hairline);
  align-items: baseline;
}
.spec-list__row dt{
  font: 500 0.6875rem/1.5 var(--font-mono);
  letter-spacing: var(--track-mono-label); text-transform: uppercase; color: var(--fg-3);
}
.spec-list__row dd{ margin: 0; font-size: .9375rem; line-height: 1.55; color: var(--fg); }
@media (max-width: 600px){
  .spec-list__row{ grid-template-columns: 1fr; gap: var(--s-2); }
}

/* ==========================================================================
   INDEX TABLE — references / catalogue listing
   ========================================================================== */

.index-table{ border-top: 1px solid var(--hairline); }

.index-row{
  display: grid;
  grid-template-columns: 4.5rem minmax(0, 2.2fr) minmax(0, 1.3fr) minmax(0, 1fr) 3rem;
  gap: var(--s-5);
  align-items: center;
  padding-block: clamp(1.1rem, 2vw, 1.75rem);
  padding-inline: var(--s-3);
  border-bottom: 1px solid var(--hairline);
  color: inherit;
  position: relative;
  transition: background-color var(--dur-sm) var(--ease-soft),
              transform var(--dur-sm) var(--ease-soft),
              padding-inline var(--dur-sm) var(--ease-soft);
}
.index-row__num{
  font: 500 0.6875rem/1 var(--font-mono);
  letter-spacing: var(--track-mono-label); color: var(--fg-3);
}
.index-row__title{
  font: 800 clamp(1.2rem, 2vw, 1.75rem)/1.05 var(--font-display);
  letter-spacing: var(--track-display-m);
  transition: color var(--dur-sm) var(--ease-soft);
}
.index-row__meta{
  font: 400 .875rem/1.4 var(--font-body);
  color: var(--fg-2);
}
.index-row__arrow{
  justify-self: end;
  font-size: 1.1rem;
  opacity: 0;
  transform: translate(-6px, 6px);
  transition: opacity var(--dur-sm) var(--ease-soft),
              transform var(--dur-sm) var(--ease-soft);
}
@media (hover: hover){
  .index-row:hover{
    background: rgb(var(--ink-rgb) / .035);
    transform: translateY(-2px);
    padding-inline: var(--s-5);
  }
  .index-row:hover .index-row__title{ color: var(--orange-deep); }
  .index-row:hover .index-row__arrow{ opacity: 1; transform: none; }
}
.index-row:focus-visible .index-row__arrow{ opacity: 1; transform: none; }
body.on-dark .index-row:hover{ background: rgb(255 255 255 / .05); }
body.on-dark .index-row:hover .index-row__title{ color: var(--orange); }

@media (max-width: 860px){
  .index-row{
    grid-template-columns: 3rem 1fr 2rem;
    row-gap: var(--s-2);
  }
  .index-row__meta{ grid-column: 2 / 4; }
}

/* ==========================================================================
   STAT — the count-up trust bar
   ========================================================================== */

.stat{ display: flex; flex-direction: column; gap: var(--s-2); }
/* Sized so the widest value in the trust bar ("31.500 m²") still fits one
   quarter of the grid without overrunning its neighbour. */
.stat__value{
  font: 900 clamp(2.25rem, 4.2vw, 3.75rem)/0.9 var(--font-display);
  letter-spacing: var(--track-display-xl);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* The unit rides the number rather than becoming a second line of display
   type. Still large enough to clear the 3:1 large-text threshold. */
.stat__unit{
  color: var(--orange-deep);
  font-size: .38em;
  letter-spacing: var(--track-body);
}
.stat__label{
  font: 500 0.6875rem/1.5 var(--font-mono);
  letter-spacing: var(--track-mono-label); text-transform: uppercase; color: var(--fg-3);
  max-width: 20ch;
}
.stat__rule{ height: 3px; width: 32px; background: var(--red); }

/* ==========================================================================
   FORM
   ========================================================================== */

.form{ display: grid; gap: var(--s-5); }
.form-grid{
  display: grid; gap: var(--s-5);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (max-width: 700px){ .form-grid{ grid-template-columns: 1fr; } }

.field{ display: flex; flex-direction: column; gap: var(--s-2); }
.field--full{ grid-column: 1 / -1; }

.label{
  font: 500 0.6875rem/1.5 var(--font-mono);
  letter-spacing: var(--track-mono-label); text-transform: uppercase; color: var(--fg-2);
}
.label .req{ color: var(--red); }

.input, .textarea, .select{
  width: 100%;
  min-height: 52px;
  padding: var(--s-3) var(--s-4);
  background: var(--surface);
  color: var(--fg);
  border: 1px solid var(--hairline);
  border-radius: var(--r-xs);
  font: 400 1rem/1.5 var(--font-body);
  transition: border-color var(--dur-sm) var(--ease-soft),
              box-shadow var(--dur-sm) var(--ease-soft);
}
.textarea{ min-height: 150px; resize: vertical; padding-top: var(--s-4); }

.select{
  appearance: none;
  padding-right: var(--s-8);
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 22px) 50%, calc(100% - 15px) 50%;
  background-size: 7px 7px, 7px 7px;
  background-repeat: no-repeat;
  cursor: pointer;
}

.input:hover, .textarea:hover, .select:hover{ border-color: var(--muted); }
.input:focus-visible, .textarea:focus-visible, .select:focus-visible{
  border-color: var(--orange);
  outline: 2px solid var(--orange);
  outline-offset: 1px;
}

.input::placeholder, .textarea::placeholder{ color: var(--fg-3); opacity: 1; }

.field.is-invalid .input,
.field.is-invalid .textarea,
.field.is-invalid .select{ border-color: var(--red); border-width: 1.5px; }

.field__error{
  font: 400 .8125rem/1.4 var(--font-body);
  color: var(--red-deep);
  min-height: 0;
}
.field:not(.is-invalid) .field__error{ display: none; }

/* Checkbox — consent */
.check{
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: var(--s-3);
  align-items: start;
  font-size: .875rem;
  line-height: 1.5;
  color: var(--fg-2);
  cursor: pointer;
}
.check input{
  appearance: none;
  width: 22px; height: 22px; margin: 0;
  border: 1.5px solid var(--muted);
  border-radius: var(--r-xs);
  background: var(--surface);
  display: grid; place-content: center;
  cursor: pointer;
  transition: background-color var(--dur-sm) var(--ease-soft),
              border-color var(--dur-sm) var(--ease-soft);
}
.check input::before{
  content: "";
  width: 10px; height: 6px;
  border-left: 2px solid var(--ink);
  border-bottom: 2px solid var(--ink);
  transform: rotate(-45deg) scale(0);
  transition: transform var(--dur-xs) var(--ease-soft);
}
.check input:checked{ background: var(--orange); border-color: var(--orange); }
.check input:checked::before{ transform: rotate(-45deg) scale(1); }

/* Honeypot — off-screen, never announced, never tabbable. */
.hp{
  position: absolute !important;
  left: -9999px; top: auto;
  width: 1px; height: 1px; overflow: hidden;
}

.form-status{
  display: none;
  padding: var(--s-5);
  border-left: 4px solid var(--orange);
  background: rgb(var(--orange-rgb) / .08);
  font-size: .9375rem;
}
.form-status.is-visible{ display: block; }
.form-status--error{ border-left-color: var(--red); background: rgb(var(--red-rgb) / .07); }

/* ==========================================================================
   SCROLL CUE
   ========================================================================== */

.scroll-cue{
  display: inline-flex; align-items: center; gap: var(--s-3);
  font: 500 0.6875rem/1 var(--font-mono);
  letter-spacing: var(--track-mono-label); text-transform: uppercase;
  transition: opacity var(--dur-md) var(--ease-out);
}
.scroll-cue__line{
  display: block; width: 46px; height: 1px; background: currentColor;
  /* Deliberately static. This used to pulse on a 2.4s infinite loop; nothing
     on the site loops any more. An idle animation competes with the content
     for attention and never stops asking. */
}

/* The cue has done its job the moment the page moves. Hidden past 40px of
   scroll, and hidden outright on phones — it is a desktop affordance, and
   next to a full-bleed image that obviously continues it says nothing. */
body.is-scrolled .scroll-cue{ opacity: 0; pointer-events: none; }

@media (max-width: 768px){
  .scroll-cue{ display: none; }
}

/* ==========================================================================
   PHONE CARD — the four tappable lines
   ========================================================================== */

.phone-card{
  display: flex; flex-direction: column; gap: var(--s-2);
  padding: var(--s-5);
  border: 1px solid var(--hairline);
  border-radius: var(--r-xs);
  background: var(--surface);
  transition: border-color var(--dur-sm) var(--ease-soft),
              transform var(--dur-sm) var(--ease-soft),
              box-shadow var(--dur-sm) var(--ease-soft);
  min-height: 44px;
}
.phone-card__label{
  font: 500 0.6875rem/1.4 var(--font-mono);
  letter-spacing: var(--track-mono-label); text-transform: uppercase; color: var(--fg-3);
}
.phone-card__num{
  font: 800 clamp(1.2rem, 1.8vw, 1.45rem)/1.1 var(--font-display);
  letter-spacing: var(--track-heading);
}
@media (hover: hover){
  .phone-card:hover{
    border-color: var(--orange);
    transform: translateY(-3px);
    box-shadow: var(--shadow-2);
  }
  .phone-card:hover .phone-card__num{ color: var(--orange-deep); }
}

/* ==========================================================================
   PROCESS STEP — how ordering works
   ========================================================================== */

.step{ display: flex; flex-direction: column; gap: var(--s-3); }
.step__num{
  font: 900 clamp(2.5rem, 4.5vw, 4rem)/0.85 var(--font-display);
  letter-spacing: var(--track-display-xl);
  color: var(--fg-3);
  opacity: .35;
}
.step__title{
  font: 800 1.15rem/1.15 var(--font-display);
  letter-spacing: var(--track-heading); text-transform: uppercase;
}
.step__text{ font-size: .9375rem; line-height: 1.55; color: var(--fg-2); max-width: 34ch; }

.progress-line{
  position: relative;
  height: 3px;
  background: var(--hairline);
  margin-bottom: var(--s-7);
  overflow: hidden;
}
.progress-line__fill{
  position: absolute; inset: 0 auto 0 0;
  width: 100%;
  background: var(--red);
  transform: scaleX(var(--p, 0));
  transform-origin: left center;
}

/* ==========================================================================
   PLACEHOLDER MEDIA — until real photography exists
   ========================================================================== */

.ph{
  width: 100%; height: 100%;
  display: grid; place-content: center;
  background:
    repeating-linear-gradient(
      -45deg,
      transparent 0 11px,
      rgb(var(--ink-rgb) / .035) 11px 12px
    ),
    var(--paper-warm);
  color: var(--fg-3);
  text-align: center;
  padding: var(--s-4);
}
.ph__label{
  font: 500 0.625rem/1.5 var(--font-mono);
  letter-spacing: var(--track-mono-label); text-transform: uppercase;
}

/* ==========================================================================
   B4 — MICRO-INTERACTIONS
   Every interactive element on the site has :hover, :focus-visible and
   :active. The last one is the one builds usually forget, and it is exactly
   what separates an interface that feels responsive from one that feels
   like a picture of an interface.
   ========================================================================== */

/* ---------- BUTTONS: a real press ----------
   The press is a scale, not a translate, so it reads as the surface giving
   under the finger rather than the button sliding. --press composes into the
   shared transform declared on .btn. */
.btn:active{ transition-duration: var(--dur-xs); }

/* ---------- LINKS: a two-phase underline ----------
   Draws in from the left on enter and retracts to the right on leave, by
   swapping transform-origin at the moment the state flips. A single-origin
   underline slides back the way it came, which reads as an undo rather than
   as a release. */
.link-text{
  position: relative;
  border-bottom: 0;                 /* the ::after is the rule now */
  padding-bottom: 2px;
}
.link-text::after{
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--orange);
  transform: scaleX(1);
  transform-origin: right center;   /* resting origin = the retract direction */
  transition: transform var(--dur-sm) var(--ease-soft);
}
@media (hover: hover){
  .link-text:hover::after{
    transform: scaleX(1);
    transform-origin: left center;
  }
  /* The retract, expressed as its own state so the origin has flipped before
     the scale animates. */
  .link-text:not(:hover)::after{ transform: scaleX(1); }
}
.link-text:active::after{ background: var(--red); }

/* ---------- CARDS ----------
   The image scales inside its crop; the card itself does not move. Moving the
   whole card is the cheap version of this, and it shifts everything around it. */
.card{ transition: box-shadow var(--dur-md) var(--ease-soft); }
@media (hover: hover){
  .card:hover{ box-shadow: var(--shadow-sm); }
  .card:hover .frame > img,
  .card:hover .frame > picture > img{ transform: scale(1.045); }
  .card:hover .card__title{ color: var(--orange-deep); }
  .card:hover .link__arrow,
  .card:hover .card__arrow{ transform: translateX(4px); }
}
.card:active{ box-shadow: none; }

/* Cards carry no shadow at rest. Anything with a resting shadow reads as a
   template; the shadow is a response to the pointer, not a decoration. */
.card, .model-card, .index-row{ box-shadow: none; }

/* ---------- HAIRLINES ON DARK ----------
   --line is mixed for paper and goes muddy on #141414. Every rule inside a
   dark section takes --line-dark instead. */
.is-dark .hairline,
body.on-dark .hairline{ background: var(--line-dark); }
.is-dark hr:not(.rule),
body.on-dark hr:not(.rule){ border-color: var(--line-dark); }


/* ---------- FORM: the sent state ----------
   Everything except the confirmation is removed once the enquiry is away.
   A form still sitting there under a success message invites a duplicate
   submission and gives no clear signal that anything finished. */
.form.is-sent > *:not([data-form-success]){ display: none; }
.form.is-sent{ min-height: 0; }
