﻿/* ==========================================================================
   MOBILSISTEMI — base.css
   Reset, document, type scale, utilities, motion safety.
   ========================================================================== */

/* ---------- RESET ---------- */
*, *::before, *::after{ box-sizing: border-box; }

html{
  -webkit-text-size-adjust: 100%;
  /* Safari only, and harmless everywhere else: pulls an opening quote or
     bullet into the margin so the text edge stays optically straight. */
  hanging-punctuation: first last;
  /* Deliberately NOT scroll-behavior: smooth. That animates every scroll the
     browser performs — including the ones it performs on the user's behalf,
     such as restoring position on back — and is indistinguishable from a
     laggy page. Anchor jumps ask for smooth per call, in scroll.js. */
  scroll-padding-top: calc(var(--nav-h) + var(--s-5));  /* the pill must not cover an anchor */
}

body{
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font: 400 1.0625rem/1.6 var(--font-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  transition: background-color var(--dur-lg) linear, color var(--dur-md) linear;
}

h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd{ margin: 0; }

/* :where() keeps the reset at zero specificity. Written as `ul[class]` it
   scores 0,1,1 and silently outranks any component class that tries to set a
   margin on its own list — which is exactly how the breadcrumb lost its
   spacing. */
:where(ul[class], ol[class]){ list-style: none; margin: 0; padding: 0; }

img, picture, video, canvas, svg{ display: block; max-width: 100%; }
img{ height: auto; }

input, button, textarea, select{ font: inherit; color: inherit; }

button{
  background: none; border: 0; padding: 0;
  cursor: pointer; color: inherit;
}

a{ color: inherit; text-decoration: none; }

table{ border-collapse: collapse; width: 100%; }

:target{ scroll-margin-top: 8rem; }

/* ---------- SELECTION & FOCUS ---------- */
/* Ink on orange, not white: white on --orange is 2.62:1, the same failure
   documented in TODO.md §2.1, and selected text still has to be read. */
::selection{ background: var(--orange); color: var(--ink); }

:focus{ outline: none; }
:focus-visible{
  outline: 3px solid var(--orange);
  outline-offset: 3px;
  border-radius: var(--r-xs);
}
/* Focus ring stays visible against orange fills */
.btn--primary:focus-visible, .btn--icon:focus-visible{
  outline-color: var(--ink);
  box-shadow: 0 0 0 6px rgb(var(--paper-rgb) / .9);
}
body.on-dark .btn--primary:focus-visible,
body.on-dark .btn--icon:focus-visible{
  box-shadow: 0 0 0 6px rgb(20 20 20 / .9);
}

/* ---------- SKIP LINK ---------- */
.skip-link{
  position: fixed; top: 0; left: 0; z-index: 400;
  background: var(--ink); color: var(--paper);
  padding: var(--s-3) var(--s-5);
  font: 500 .78rem/1 var(--font-mono);
  letter-spacing: var(--track-mono-ui); text-transform: uppercase;
  transform: translateY(-120%);
  transition: transform var(--dur-sm) var(--ease-soft);
}
.skip-link:focus-visible{ transform: translateY(0); }

/* ==========================================================================
   TYPE SCALE — display leading below 1 is the defining rule of this build
   ========================================================================== */

/* Tracking is never written as a number here. Every size takes its value from
   the optical scale in tokens.css, so the relationship between size and
   tracking — tighter as it grows, looser for uppercase — holds across the
   whole site instead of being re-decided per component. */

.display-xl{
  font: 900 clamp(3.5rem, 11vw, 11rem)/0.82 var(--font-display);
  letter-spacing: var(--track-display-xl); text-transform: uppercase;
}
.display-l{
  font: 800 clamp(2.5rem, 6vw, 5.5rem)/0.88 var(--font-display);
  letter-spacing: var(--track-display-l); text-transform: uppercase;
}
.display-m{
  font: 800 clamp(2rem, 4vw, 3.5rem)/0.95 var(--font-display);
  letter-spacing: var(--track-display-m);
}
.display-s{
  font: 700 clamp(1.35rem, 2.2vw, 1.9rem)/1.05 var(--font-display);
  letter-spacing: var(--track-heading);
}
.editorial{
  font: 300 clamp(1.5rem, 3.2vw, 3rem)/1.18 var(--font-body);
  letter-spacing: var(--track-heading);
}
.body{ font: 400 1.0625rem/1.6 var(--font-body); letter-spacing: var(--track-body); }
.body-s{
  font: 400 .9375rem/1.55 var(--font-body);
  color: var(--fg-2);
  letter-spacing: var(--track-small);
}
.lede{
  font: 300 clamp(1.05rem, 1.5vw, 1.3rem)/1.55 var(--font-body);
  color: var(--fg-2); letter-spacing: var(--track-body);
}
.meta{
  font: 700 0.9375rem/1.4 var(--font-display);
  letter-spacing: var(--track-upper); text-transform: uppercase;
}
.eyebrow{
  font: 500 0.6875rem/1.5 var(--font-mono);
  letter-spacing: var(--track-mono-label); text-transform: uppercase; color: var(--fg-3);
}
.btn-label{
  font: 500 0.78rem/1 var(--font-mono);
  letter-spacing: var(--track-upper); text-transform: uppercase;
}
.mono{
  font: 400 .8125rem/1.5 var(--font-mono);
  letter-spacing: var(--track-small);
}

/* ---------- WRAPPING AND RAG ----------
   text-wrap: balance evens out the lines of a heading; pretty stops a
   paragraph ending on a single orphaned word. Both degrade to normal
   wrapping where unsupported, so there is nothing to gate. */
h1, h2, h3,
.display-xl, .display-l, .display-m, .display-s{ text-wrap: balance; }
p, li, .editorial, .lede{ text-wrap: pretty; }

/* ---------- NUMERALS ----------
   Tabular by default anywhere a number is a *value* — prices, areas,
   dimensions, counters, phone numbers, indices — so columns of them line up
   and a counting animation does not jitter as the glyph widths change.
   Running prose keeps proportional figures. */
.tabular,
.meta-row__value, .model-card__specs, .model-card__price,
.index-row__area, .index-row__dims, .stat__num, .counter,
.scroll-progress__num, .spec-list dd, .phone-card__number,
th, td{
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ---------- OPENTYPE ----------
   Ligatures and contextual alternates matter most at display sizes, where the
   default pairs in a heavy grotesque are visibly loose. Optical sizing lets
   the variable axis do its job where the font supports it. */
.display-xl, .display-l, .display-m, .display-s, .editorial{
  font-feature-settings: "liga" 1, "calt" 1;
  font-optical-sizing: auto;
}

/* Leading below 1 pulls the line box in tighter than the glyphs, so caps with
   diacritics (Č Ć Š Ž Đ) reach above it and collide with whatever sits
   directly before the heading. These pairings restore the gap without
   loosening the leading itself. */
.eyebrow + .display-xl,
.eyebrow + .display-l,
.eyebrow + .display-m,
.eyebrow + .display-s,
.eyebrow + .editorial{ margin-top: var(--s-4); }

.display-xl + .rule,
.display-l + .rule,
.display-m + .rule,
.display-s + .rule{ margin-top: var(--s-5); }

/* A last-resort guard for narrow screens. Serbian compounds get long, the
   display faces are set uppercase and heavy, and at 320px a single word can
   be wider than its column. Breaking a word is ugly; a page that scrolls
   sideways is worse, and this only ever fires when nothing else can give. */
@media (max-width: 480px){
  .display-xl, .display-l, .display-m, .display-s,
  .card__title, .model-card__name, .process__title, .menu__link{
    overflow-wrap: break-word;
  }
}

/* Mobile loosens the extreme leading — 0.82 collapses on narrow measures */
@media (max-width: 768px){
  .display-xl{ line-height: 0.92; letter-spacing: var(--track-display-l); }
  .display-l { line-height: 0.94; }
  .display-m { line-height: 1.0; }
}

/* Measure control */
.measure{ max-width: var(--maxw-text); }
.measure-tight{ max-width: var(--maxw-centered); }
.measure-centered{ max-width: var(--maxw-centered); margin-inline: auto; text-align: center; }

.text-muted{ color: var(--fg-3); }
.text-2{ color: var(--fg-2); }
.text-orange{ color: var(--orange-deep); }

/* ==========================================================================
   LAYOUT PRIMITIVES
   ========================================================================== */

.wrap{
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap--narrow{ max-width: 76rem; }

.section{
  position: relative;
  padding-block: var(--section-y);
}

/* Surface classes paint each section directly, so the page is correct on
   first paint and with JavaScript disabled. When bg-transition.js takes over
   it marks the body and the sections go transparent, letting the body colour
   lerp between them instead of cutting. */
.surface--paper{ background: var(--paper); }
.surface--warm { background: var(--paper-warm); }
.surface--steel{ background: var(--paper-steel); }
.surface--light{ background: var(--paper-light); }
.surface--dark { background: var(--dark); }

body.bg-managed .surface--paper,
body.bg-managed .surface--warm,
body.bg-managed .surface--steel,
body.bg-managed .surface--light,
body.bg-managed .surface--dark{ background: transparent; }
.section--tight{ padding-block: var(--section-y-s); }
.section--flush-top{ padding-top: 0; }

.grid{
  display: grid;
  grid-template-columns: repeat(var(--grid-cols), minmax(0, 1fr));
  gap: var(--grid-gap);
}
@media (max-width: 768px){
  .grid{ --grid-cols: 4; }
}

.stack > * + *{ margin-top: var(--flow, var(--s-4)); }

/* ---------- THE RED RULE — the strongest brand carrier ---------- */
.rule{
  display: block;
  height: 4px;
  background: var(--red);
  border: 0;
  /* <hr> carries margin-inline: auto from the UA sheet, which would centre
     every section rule instead of hanging it off the left margin. */
  margin: 0;
  width: 100%;
}
.rule--thin{ height: 3px; }
.rule--thick{ height: 5px; }

/* Section heading + its red rule, drawn left→right on reveal */
.section-head{
  position: relative;
  display: flex; flex-direction: column; gap: var(--s-4);
  margin-bottom: clamp(2.5rem, 5vw, 4.5rem);
  padding-top: var(--s-6);
  /* Asymmetry, deliberately. A heading centred in its own column is the
     default every template lands on; sitting it in the first seven of twelve
     and letting the remaining five stay empty is what makes the page read as
     laid out rather than filled. */
  max-width: min(100%, 58ch);
}

/* A hairline above every section eyebrow, running the full width of the
   content column. It is the cheapest device in editorial design for making a
   page read as structured, and it costs one pseudo-element. */
.section-head::before,
.page-head__inner::before{
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1px;
  background: var(--hairline);
}

/* ---------- SECTION NUMBERING ----------
   Automatic, from a counter, so adding or reordering a section cannot leave a
   stale number behind — which hand-written indices always eventually do.

   Scoped to section headers only. The hero's eyebrow is a company name, and
   the category blocks on the inner pages already carry their own index. */
main{ counter-reset: section; }
main [data-section]{ counter-increment: section; }
main [data-section] > .wrap > .section-head > .eyebrow::before{
  content: counter(section, decimal-leading-zero) " — ";
  color: var(--red);
}
.section-head .rule{
  width: clamp(64px, 8vw, 120px);
  transform-origin: left center;
}
[data-reveal] .rule, .rule[data-draw]{ transform: scaleX(0); }
.is-revealed .rule, .rule[data-draw].is-revealed{
  transform: scaleX(1);
  transition: transform var(--dur-xl) var(--ease-out) var(--dur-xs);
}

/* ---------- HAIRLINES ---------- */
.hairline{ height: 1px; background: var(--hairline); border: 0; width: 100%; }

/* ---------- WORD-BY-WORD REVEAL ---------- */
.word{
  color: var(--dim-text);
  transition: color var(--dur-md) var(--ease-out);
}
.word.is-lit{ color: var(--fg); }

/* ==========================================================================
   REVEAL — resting states. JS adds .is-revealed; no-JS shows everything.
   ========================================================================== */

[data-reveal]{
  opacity: 0;
  transform: translateY(32px);
  transition: opacity var(--dur-lg) var(--ease-out),
    transform var(--dur-lg) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}
[data-reveal].is-revealed{ opacity: 1; transform: none; }

/* ---------- display type wipes; it never fades ----------
   Fading large type in reads cheap. A wipe reads printed, and it is the same
   gesture the hero already uses, so the whole site reveals its headings one
   way.

   The insets are negative at both ends of the vertical axis on purpose.
   Display leading here is below 1, so the caron on Š and the descenders sit
   *outside* the border box; a clip that stopped at inset(0 ... 0) would shave
   them off — which is exactly the bug the hero headline had. */
html.js [data-reveal] :is(.display-xl, .display-l, .display-m, .editorial){
  clip-path: inset(-0.3em 0 100% 0);
  transform: translateY(12px);
}
html.js [data-reveal].is-revealed :is(.display-xl, .display-l, .display-m, .editorial){
  clip-path: inset(-0.3em 0 -0.22em 0);
  transform: none;
  transition: clip-path var(--dur-lg) var(--ease-out) var(--reveal-delay, 0ms),
              transform var(--dur-lg) var(--ease-out) var(--reveal-delay, 0ms);
}

/* …and the group around it must not fade, or the heading gets both. Guarded
   by @supports so a browser without :has() simply keeps the plain fade rather
   than showing nothing. */
@supports selector(:has(*)){
  html.js [data-reveal]:has(.display-xl, .display-l, .display-m, .editorial){
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce){
  html.js [data-reveal] :is(.display-xl, .display-l, .display-m, .editorial){
    clip-path: none;
    transform: none;
  }
}

/* Only hide things if JS is actually running. */
html:not(.js) [data-reveal]{ opacity: 1; transform: none; transition: none; }
html:not(.js) .rule{ transform: none !important; }
html:not(.js) [data-morph]{ clip-path: none !important; transform: none !important; }
html:not(.js) .preloader{ display: none; }
html:not(.js) .word{ color: inherit !important; }
/* The hero's closing red rule and its headline wipe are the two resting
   states that are neither [data-reveal] nor .rule, so they need saying
   separately. Without this the rule never draws — and the headline, before
   the gate added in sections.css, was clipped to nothing entirely. */
html:not(.js) .hero::after{ transform: scaleX(1) !important; }

/* ==========================================================================
   ACCESSIBILITY UTILITIES
   ========================================================================== */

.visually-hidden{
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap; border: 0;
}

[aria-hidden="true"]{ user-select: none; }

/* ==========================================================================
   REDUCED MOTION — mandatory
   ========================================================================== */

@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  [data-reveal]{ opacity: 1 !important; transform: none !important; }
  .rule{ transform: none !important; }
  [data-morph]{ clip-path: none !important; transform: none !important; }
  .preloader{ display: none !important; }
  .cursor{ display: none !important; }
  .word{ color: inherit !important; }
}

/* ==========================================================================
   B3 — MATERIAL
   ========================================================================== */

/* ---------- FILM GRAIN ----------
   One fixed overlay for the whole document. SVG turbulence rasterised once by
   the browser and composited on the GPU; it does not repaint on scroll and it
   is never animated.

   Under 5% opacity on purpose. Grain that you can consciously see is a
   texture effect; grain at this level is only felt, and stops large flat
   fields of --paper reading as flat digital colour.

   z-index 1 sits above the page background and below everything with its own
   stacking context, which is every section (they all isolate). */
body::after{
  content: "";
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: .035;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* Dark surfaces swallow grain, so it gets a little more there. */
body.on-dark::after{ opacity: .05; }

/* Static noise is uncomfortable for some people with vestibular or visual
   sensitivities even though it does not move, and it is meaningless on paper. */
@media (prefers-reduced-motion: reduce){ body::after{ display: none; } }
@media (prefers-contrast: more){ body::after{ display: none; } }
@media print{ body::after{ display: none; } }

/* ---------- IMAGE GRADE ----------
   Three renders that may not share a source will not sit together untreated.
   A single grade across all of them is what makes them read as one set. */
img.lqip,
.frame img,
.hero__slide img{
  filter: saturate(.94) contrast(1.04);
}

/* A very slight inner vignette holds the eye inside a full-bleed frame.
   Only on the hero and on 16:9 bands — on a 400px card it is invisible and
   costs a paint layer per card, of which the catalogue has twenty. */
.frame--16x9::before,
.hero__media::before{
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: radial-gradient(120% 100% at 50% 45%,
              transparent 55%, rgb(var(--ink-rgb) / .12) 100%);
}

/* ---------- SCROLLBAR ----------
   Desktop nicety, wrapped so it cannot affect anything that does not support
   it, and never applied to touch, where the OS bar is transient anyway. */
@supports selector(::-webkit-scrollbar){
  @media (hover: hover) and (pointer: fine){
    ::-webkit-scrollbar{ width: 10px; height: 10px; }
    ::-webkit-scrollbar-track{ background: transparent; }
    ::-webkit-scrollbar-thumb{
      background: var(--line);
      border: 3px solid var(--bg);
      border-radius: var(--r-pill);
    }
    ::-webkit-scrollbar-thumb:hover{ background: var(--muted-ink); }
    ::-webkit-scrollbar-button{ display: none; }
  }
}

/* ---------- HIGH CONTRAST ---------- */
@media (prefers-contrast: more){
  :root{ --line: rgb(var(--ink-rgb) / .55); --line-dark: rgb(255 255 255 / .45); }
  .frame, .card, .btn, .assistant__panel,
  input, select, textarea{ border-width: 2px; }
  :focus-visible{ outline-width: 3px; }
}
