/* ============================================================
   DUCKIT MOTORSPORT — foundation
   Palette derived from the mark: monochrome white on carbon.
   One hot accent (torch orange) = glowing manifold / brake disc.
   Geometry derived from the mark: nested diffuser-fin chevrons,
   expressed site-wide as a single repeated cut angle.
   ============================================================ */

:root {
  /* --- surface ladder: carbon, cool-shifted --- */
  --ink:        #0A0A0B;
  --ink-2:      #101012;
  --ink-3:      #17171A;
  --ink-4:      #202025;
  --paper:      #F2F2EF;   /* off-white, faintly warm — light sections */
  --paper-2:    #E4E4DF;

  /* --- type --- */
  --white:      #FFFFFF;
  --mute:       #8A8A93;   /* 5.78:1 on --ink   ✓AA */
  --mute-dim:   #7C7C85;   /* 4.79:1 on --ink, 4.60:1 on --ink-2  ✓AA at 12px */
  --on-paper:   #131315;
  --on-paper-mute: #55555C; /* 6.9:1 on --paper ✓AA */

  /* --- accent: torch. 5.89:1 on --ink, safe for text --- */
  --torch:      #FF4A1C;
  --torch-hot:  #FF6A3D;
  --torch-dim:  rgba(255, 74, 28, 0.14);

  /* --- burnt-titanium heat wash. DECORATIVE ONLY, never behind text --- */
  --ti: linear-gradient(100deg, #2B7FFF 0%, #7A4BE0 38%, #FF4A1C 68%, #FFB020 100%);

  /* --- lines --- */
  --line:        rgba(255, 255, 255, 0.10);
  --line-strong: rgba(255, 255, 255, 0.22);
  --line-paper:  rgba(19, 19, 21, 0.14);

  /* --- type families --- */
  --display: 'Saira', 'Helvetica Neue', Arial, sans-serif; /* sporty technical grotesque */
  --body:    'IBM Plex Sans', 'Helvetica Neue', Arial, sans-serif;
  --mono:    'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace; /* part codes, specs */
  /* The part card and the part page only: the torch line above a part name is
     set in this rather than in --mono, bigger and bolder than the eyebrows
     elsewhere. Deliberately not used site-wide - everything else keeps the
     original pairing. */
  --plate:   'Rajdhani', 'HelveticaNeue', 'Helvetica Neue', sans-serif;

  /* --- fluid type scale (dialled down one notch from the first cut) --- */
  --t-hero:  clamp(2.4rem, 1.55rem + 4.1vw, 5.25rem);
  --t-xl:    clamp(1.65rem, 1.2rem + 2.3vw, 3rem);
  --t-lg:    clamp(1.35rem, 1.08rem + 1.3vw, 2rem);
  --t-md:    clamp(1.1rem, 0.99rem + 0.55vw, 1.4rem);
  --t-body:  clamp(0.975rem, 0.94rem + 0.18vw, 1.0625rem);
  --t-sm:    0.875rem;
  --t-xs:    0.75rem;

  /* --- rhythm --- */
  --gap:      20px;
  --pad-x:    clamp(20px, 5vw, 72px);
  --sec-y:    clamp(72px, 9vw, 148px);
  /* content column. One knob — .wrap, the header and the home hero all use it */
  --maxw:     1440px;
  --header-h: 76px;
  /* Inner-page banner height. Only the home hero fills the screen; every other
     page opens on a band and gets its content into view straight away. */
  --band-h:   clamp(280px, 40svh, 440px);

  /* --- signature cut angle (diffuser fin) --- */
  --cut:      22px;
  --skew:     -7deg;

  --ease:     cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in:  cubic-bezier(0.65, 0, 0.35, 1);
}

@media (max-width: 767px) {
  :root {
    --header-h: 62px; --cut: 14px;

    /* One step down across the whole scale on a phone. Every token keeps its
       original vw coefficient, so the type still grows with the viewport and
       the hierarchy between the steps is unchanged — only the baseline moves,
       by about 10% at 375px. Done here on the tokens rather than per component
       so nothing can drift out of step with the rest. */
    --t-hero: clamp(2.05rem, 1.3rem + 4.1vw, 3.25rem);
    --t-xl:   clamp(1.4rem, 1.03rem + 2.3vw, 2.4rem);
    --t-lg:   clamp(1.12rem, 0.95rem + 1.3vw, 1.75rem);
    --t-md:   clamp(0.97rem, 0.88rem + 0.55vw, 1.25rem);
    --t-body: clamp(0.875rem, 0.84rem + 0.18vw, 0.95rem);
    --t-sm:   0.8125rem;
    --t-xs:   0.6875rem;
  }
}

/* ============================================================ reset */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* anchor targets clear the sticky header */
  scroll-padding-top: calc(var(--header-h) + 16px);
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--white);
  font-family: var(--body);
  font-size: var(--t-body);
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden; /* belt-and-braces; layouts must not need it */
}

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--display);
  font-weight: 700;
  line-height: 0.98;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

p { margin: 0; text-wrap: pretty; }
ul, ol { margin: 0; padding: 0; list-style: none; }

img, svg, video {
  display: block;
  max-width: 100%;
  height: auto;
}

a { color: inherit; text-decoration: none; }

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

/* ============================================================ a11y */

:focus-visible {
  outline: 2px solid var(--torch);
  outline-offset: 3px;
  border-radius: 2px;
}
:focus:not(:focus-visible) { outline: none; }

.skip {
  position: absolute;
  left: 12px;
  top: -100px;
  z-index: 999;
  padding: 12px 20px;
  background: var(--torch);
  color: var(--ink);
  font-weight: 700;
  transition: top 0.18s var(--ease);
}
.skip:focus { top: 12px; }

.sr {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ============================================================ layout */

.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--pad-x);
}

/* full-bleed blocks keep edge contact but still pad their contents */
.bleed { width: 100%; padding-inline: var(--pad-x); }

.section { padding-block: var(--sec-y); }

/* ============================================================ shared type atoms */

/* small letterspaced label — used as the one recurring "tag" voice */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--torch);
}
.eyebrow::before {
  content: '';
  width: 26px;
  height: 2px;
  background: var(--torch);
  flex: none;
}
.eyebrow--paper { color: var(--on-paper); }
.eyebrow--paper::before { background: var(--torch); }

.lede {
  color: var(--mute);
  font-size: var(--t-md);
  line-height: 1.5;
  max-width: 46ch;
}

.spec {
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.08em;
  color: var(--mute);
}

/* small local-area line (SEO long-tail: surrounding towns / Klang Valley) */
.local-note { margin-top: 14px; max-width: 62ch; color: var(--mute); font-size: var(--t-sm); line-height: 1.6; }

/* ============================================================ buttons */

/* An author `display` beats the UA stylesheet's [hidden] { display: none }, so a
   .btn shipping the attribute would paint regardless. The copy-link button on the
   part page relies on it: it ships hidden and site.js reveals it only where the
   clipboard is actually reachable. Without this it shows dead for everyone else. */
.btn[hidden] { display: none; }

.btn {
  --btn-bg: var(--torch);
  /* near-black on torch = 5.89:1 (white would be 3.36:1, below AA),
     and black-on-orange is the truer motorsport signage pairing anyway */
  --btn-fg: var(--ink);
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 15px 26px;
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--t-sm);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  /* the signature cut — bottom-left fin notch */
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%, 0 100%);
  transition: background 0.2s var(--ease), transform 0.2s var(--ease);
}
.btn:hover { --btn-bg: var(--torch-hot); transform: translateY(-2px); }

/* Sixty-six hover transitions on this site and not one press state: nothing
   read as physically actuated. 90ms on --ease-in is the contact tier — down
   hard, released on the site's own curve. The hover lift is -2px, so +1px reads
   as 3px of travel from the hovered position.
   The base transition list above is left alone deliberately: retiming it to
   90ms would also retime that hover lift. Scoping the duration to :active
   gives 90ms down and the original 200ms back. Must come after :hover — both
   are (0,2,0), so source order is what decides. */
.btn:active {
  transform: translateY(1px) scale(0.99);
  transition-duration: 90ms;
  transition-timing-function: var(--ease-in);
}
.btn svg { width: 17px; height: 17px; flex: none; }

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--white);
  box-shadow: inset 0 0 0 1px var(--line-strong);
}
.btn--ghost:hover { --btn-bg: rgba(255,255,255,0.07); --btn-fg: var(--white); }

/* clip-path cuts the inset shadow along with the box, so the ghost outline used
   to stop dead at the notch and the corner read as a hole bitten out of the
   button rather than a chamfer — the filled variant gets away with it because
   its fill draws the shape. Redraw the missing hypotenuse: a 1px bar pinned to
   the bottom edge --cut in from the right, pivoted on that point and spun -45deg
   so its far end lands exactly where the cut meets the right edge. Any overshoot
   is taken by the same clip-path. */
.btn--ghost::after {
  content: '';
  position: absolute;
  left: calc(100% - var(--cut));
  bottom: 0;
  width: calc(var(--cut) * 1.4143);
  height: 1px;
  background: var(--line-strong);
  transform-origin: 0 100%;
  transform: rotate(-45deg);
}

.btn--paper { --btn-bg: var(--on-paper); --btn-fg: var(--paper); }
.btn--paper:hover { --btn-bg: var(--torch); --btn-fg: var(--ink); }

/* copy-link. Every label and both icons sit in one grid cell each, so the
   button is sized by its widest state on first paint and does not resize when
   the label flips — a button row that reflows under the cursor is how you get
   a mis-click on "More for this car". */
/* Full ghost weight: on the part page it is now the second of two buttons, not
   a third one squeezed in, so it should read like the peer of "Enquire". */
.btn--share.is-copied { --btn-fg: var(--torch); box-shadow: inset 0 0 0 1px var(--torch); }
.btn--share.is-copied::after { background: var(--torch); }

.btn__states { display: grid; }
.btn__state {
  grid-area: 1 / 1;
  visibility: hidden;      /* not `display:none` — the cell must still size */
  white-space: nowrap;
}
.btn__state.is-on { visibility: visible; }

.btn--share .btn__i-ok { display: none; }
.btn--share.is-copied .btn__i-link { display: none; }
.btn--share.is-copied .btn__i-ok { display: block; }

/* ============================================================ motion */

[data-reveal] {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease), clip-path 0.7s var(--ease);
  transition-delay: var(--d, 0ms);
}

/* A small vocabulary rather than one effect everywhere: a page that fades every
   block up by the same 26px reads as one animation played on a loop. site.js
   only ever adds `.in`, so a section chooses its entrance purely by the
   attribute's value and the sweep needs to know nothing about it — and because
   every variant resolves to the same `.in` state, none can stick half-played.

   Kept deliberately short: entrances on a workshop's site should feel like
   something being placed, not something floating in. */
[data-reveal='left'] { transform: translate3d(-30px, 0, 0); }
[data-reveal='right'] { transform: translate3d(30px, 0, 0); }
[data-reveal='scale'] { transform: scale(0.96); }
/* wipes across instead of moving — for plates and rules, where a shift would
   fight the edge it is aligned to */
[data-reveal='wipe'] { opacity: 1; transform: none; clip-path: inset(0 100% 0 0); }

[data-reveal].in { opacity: 1; transform: none; clip-path: inset(0); }

/* no-JS / no-IntersectionObserver fallback: always visible */
.no-js [data-reveal] { opacity: 1; transform: none; clip-path: none; }

/* ---------------------------------------------------------- hero entrance
   Every page opened with its first screen simply present. The type now arrives
   in sequence on load — CSS only, so there is no script between the visitor and
   the first frame, and `backwards` holds the start state during the delay
   instead of flashing the finished position first.

   The photograph is never touched: it is the LCP element on every one of these
   pages, and hiding it to fade it in would trade a real metric for an effect. */
@keyframes hero-rise {
  from { opacity: 0; transform: translate3d(0, 20px, 0); }
  to { opacity: 1; transform: none; }
}

.hero__in > *,
.chero__in > *,
.dhero__in > *,
.shero__in > *,
.khero__in > *,
.ahero__text > * {
  animation: hero-rise 0.85s var(--ease) backwards;
}
.hero__in > :nth-child(1), .chero__in > :nth-child(1), .dhero__in > :nth-child(1),
.shero__in > :nth-child(1), .khero__in > :nth-child(1), .ahero__text > :nth-child(1) { animation-delay: 0.06s; }
.hero__in > :nth-child(2), .chero__in > :nth-child(2), .dhero__in > :nth-child(2),
.shero__in > :nth-child(2), .khero__in > :nth-child(2), .ahero__text > :nth-child(2) { animation-delay: 0.16s; }
.hero__in > :nth-child(3), .chero__in > :nth-child(3), .dhero__in > :nth-child(3),
.shero__in > :nth-child(3), .khero__in > :nth-child(3), .ahero__text > :nth-child(3) { animation-delay: 0.26s; }
.hero__in > :nth-child(n+4), .chero__in > :nth-child(n+4), .dhero__in > :nth-child(n+4),
.shero__in > :nth-child(n+4), .khero__in > :nth-child(n+4), .ahero__text > :nth-child(n+4) { animation-delay: 0.36s; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    /* delays too, not just durations: the hero entrance holds its start state
       through `backwards`, so a surviving 0.36s delay would blank that line for
       0.36s rather than shortening the animation to nothing */
    animation-delay: 0s !important;
    transition-duration: 0.001ms !important;
    /* The transition side needs the same treatment and did not have it. A
       staggered set whose durations collapse to nothing but whose delays
       survive does not become still — it becomes a burst of instant pop-ins
       fired one after another, which is more jarring than the full-fidelity
       version and is precisely what this query exists to prevent. */
    transition-delay: 0s !important;
    scroll-behavior: auto !important;
  }
  /* clip-path as well as opacity/transform — the wipe variant is invisible by
     clip, so resetting only the other two would leave it permanently hidden */
  [data-reveal] { opacity: 1; transform: none; clip-path: none; }

  /* Zeroing durations does not remove a transform, it only makes it instant.
     Left alone these become discrete jumps tied to input and to scroll
     position: every control would still displace 1px on press and the logo
     would still snap between two sizes as the page leaves the top.

     !important is load-bearing rather than defensive: base.css is the first
     sheet loaded, and shell.css / pages.css / catalog.css all follow it with
     equal-specificity selectors, so without it every one of them wins. */
  .btn:active, .chip:active, .tw:active, .chips__pager:active,
  .cat__reset:not([disabled]):active, .caps__all:active, .sig__go:active {
    transform: none !important;
  }
  .hdr.is-stuck .hdr__logo img { transform: none !important; }
}

/* ============================================================ touch
   Phones only. The desktop layout puts a lot of weight on small inline links
   and 9–10px mono micro-labels; both fail on a thumb and on a 390px screen,
   so raise the floor rather than restyling each component. */
@media (max-width: 767px) {
  /* every inline text link that is a row of its own gets a 44px touch box */
  .caps__all, .sig__go, .dprod__all, .socs__go,
  .ftr__mail, .ftr__person, .ftr__link,
  .khero__mail, .khero__wa, .loc__mail, .loc__map, .loc__p,
  .crumbs__i a, .sidx__n {
    min-height: 44px;
    display: flex;
    align-items: center;
  }
  /* footer link columns: space the rows so neighbouring targets don't collide */
  .ftr__li { padding: 0; }
  .ftr__li a, .faq__note a, .ftr__base a {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }

  /* micro-label floor — 9px mono is unreadable at arm's length */
  .drawer__no, .tw__n, .tw__s, .cat__lab, .cat__rail-h,
  .sidx__no, .spec, .ftr__person span {
    font-size: 0.6875rem;
  }
  .tw__l { font-size: 0.8125rem; }
}
