@layer pages {
  /* ===
     Page-specific composition: hero · bento · counters · catalog hero band ·
     timeline · contact · kvalitet · 404
     === */

  .hero {
    position: relative;
    display: flex;
    align-items: flex-end;
    /* `svh` is the small viewport height — the one that excludes retractable browser
       chrome, so the hero is genuinely full-screen on mobile. `vh` first as the
       fallback for engines that do not know the newer unit. */
    height: 100vh;
    height: 100svh;
    min-height: 560px;
    padding-block: calc(var(--header-h) + var(--s8)) var(--s7);
    overflow: hidden;
    background: var(--d-bg);
    color: var(--d-text);
    /* Header sits on top of the hero, so pull it up by its own height. */
    margin-block-start: calc(var(--header-h) * -1);
  }

  /* === hero slider
     Three photographs, revealed by a clip-path wipe led by a red cut line.
     Every moving part is a transform, a clip-path or an opacity — no layout work. */

  /* Deliberately no `z-index` here: that would make the slider a stacking context and
     trap the progress bars beneath the hero copy, which sits at z-index 7 — the bars
     would render but never receive a click. Its children order themselves inside the
     hero's own context instead. */
  .hero-slider {
    --slide-dur: 6500ms;
    --wipe-dur: 900ms;

    position: absolute;
    inset: 0;
    overflow: hidden;
  }

  .hero-slide {
    position: absolute;
    inset: 0;
    /* Closed by default: the wipe opens it from the left edge. With JS off only
       slide 1 carries `.is-active`, so exactly one photograph shows. */
    clip-path: inset(0 100% 0 0);
    transition: clip-path var(--wipe-dur) var(--e-in-out);
    will-change: clip-path;
  }

  .hero-slide.is-active { clip-path: inset(0 0 0 0); }

  /* The outgoing slide is already fully open; it just settles back to 1.0 behind
     the line while the incoming one is revealed over it. */
  .hero-slide.is-leaving { clip-path: inset(0 0 0 0); transition: none; }

  .hero-slide img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    transform: scale(1);
    will-change: transform;
  }

  .hero-slide.is-active img { animation: hero-kb var(--slide-dur) linear forwards; }
  .hero-slide.is-leaving img { animation: hero-kb-out var(--wipe-dur) var(--e-in-out) forwards; }

  @keyframes hero-kb {
    from { transform: scale(1); }
    to { transform: scale(1.06); }
  }

  /* Starts where the Ken Burns pass ended, so the settle is seamless rather than a
     2% snap at the moment of the cut. */
  @keyframes hero-kb-out {
    from { transform: scale(1.06); }
    to { transform: scale(1); }
  }

  /* The cut: a 2px red line that leads the wipe across the viewport. */
  .hero-slider__line {
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0;
    z-index: 4;
    width: 2px;
    background: var(--red);
    box-shadow: 0 0 22px 2px var(--red-glow);
    opacity: 0;
    pointer-events: none;
  }

  .hero-slider__line.is-cutting { animation: hero-cut var(--wipe-dur) var(--e-in-out); }

  @keyframes hero-cut {
    0% { transform: translateX(0); opacity: 0; }
    8% { opacity: 1; }
    92% { opacity: 1; }
    100% { transform: translateX(100vw); opacity: 0; }
  }

  /* Pausing freezes the bar fill and the Ken Burns at the same instant the JS timer
     stops, so the two can never drift apart. */
  .hero-slider[data-paused='true'] .hero-slide img,
  .hero-slider[data-paused='true'] .hero-slider__fill { animation-play-state: paused; }

  /* Legibility scrim. The headline sits over the darkest end; the ramp keeps the
     photograph readable as an image on the right. Verified programmatically against
     the lightest region each slide puts under the text — see build/hero-contrast.mjs. */
  .hero-slider::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 5;
    pointer-events: none;
    background:
      linear-gradient(to top, rgb(8 9 11 / 0.94) 0%, rgb(8 9 11 / 0.62) 38%, rgb(8 9 11 / 0.12) 72%),
      linear-gradient(90deg, rgb(8 9 11 / 0.82) 0%, rgb(8 9 11 / 0.45) 55%, rgb(8 9 11 / 0.25) 100%);
  }

  /* === edge arrows
     Bare drawn chevrons at the hero's mid-height, in the sketch's language: stroke
     only, a red glow instead of a background. The button is a 64×120 hit area; the
     44px glyph is all that is drawn.

     z-index 6 puts them over the scrim (5) and under the copy (7), so where the
     headline reaches an arrow the headline takes the click. */
  .hero-arrow {
    position: absolute;
    z-index: 6;
    inset-block-start: 50%;
    display: grid;
    place-items: center;
    width: 64px;
    height: 120px;
    margin-block-start: -60px;
    padding: 0;
    background: none;
    border: 0;
    color: var(--red);
    filter: drop-shadow(0 0 12px var(--red-glow));
    /* Hidden until the pointer moves, then 160ms in from its own edge. */
    opacity: 0;
    pointer-events: none;
    transition: opacity 200ms linear, transform 200ms var(--e-out);
  }

  .hero-arrow--prev { inset-inline-start: 28px; transform: translateX(-6px); }
  .hero-arrow--next { inset-inline-end: 28px; transform: translateX(6px); }

  /* Not merely invisible until the slider is live — absent. With JS disabled, or if the
     dynamic import fails, these are two buttons that do nothing, and a keyboard user
     would still tab through both of them. main.js sets `data-ready` only once
     hero-slider.js has bound them. */
  .hero-slider:not([data-ready='true']) .hero-arrow { display: none; }

  /* JS clears `data-idle` on movement and sets it again after 1400ms of stillness. */
  .hero-slider:not([data-idle='true']) .hero-arrow {
    opacity: 1;
    transform: translateX(0);
    pointer-events: auto;
    transition: opacity 160ms linear, transform 160ms var(--e-out);
  }

  /* A hovered or focused arrow stays regardless of the idle timer, and focus must be
     able to reach it even while the timer says idle. */
  .hero-arrow:hover,
  .hero-arrow:focus-visible {
    opacity: 1;
    transform: translateX(0);
    pointer-events: auto;
    color: var(--red-hot);
  }

  .hero-arrow:focus-visible { outline: 2px solid var(--red); outline-offset: 4px; }

  /* Measured, not guessed. The copy column starts at the gutter until the 1280px wrap
     stops growing, so a 64px arrow inset 28px only clears the headline from about
     1360px up. Below that both arrows tuck to the edge at 40px — narrower than a
     touch target, which is fine because a coarse pointer never sees them at all. */
  @media (max-width: 1359px) {
    .hero-arrow { width: 40px; }
    .hero-arrow--prev { inset-inline-start: 2px; }
    .hero-arrow--next { inset-inline-end: 2px; }
  }

  /* Touch: no arrows at all. hero-slider.js binds a swipe on the hero instead.

     Also gone below 1100px — the breakpoint where the header drops its nav and the
     hero copy takes the full column. Measured there, the headline runs to within 6px
     of the edge at 900px and under the arrow at 1024px, and a chevron drawn behind the
     headline is worse than no chevron on a narrow desktop window. The progress bars
     remain, and they are the control that was already there. */
  @media (pointer: coarse), (max-width: 1099px) {
    .hero-arrow { display: none; }
  }

  /* Reduced motion: no fade choreography. The arrows are simply there whenever the
     pointer is over the hero, and gone when it is not. */
  @media (prefers-reduced-motion: reduce) {
    .hero-arrow,
    .hero-slider:not([data-idle='true']) .hero-arrow { transition: none; transform: none; }
    .hero:hover .hero-arrow { opacity: 1; pointer-events: auto; }
  }

  /* Progress UI — three hairline bars, bottom-right of the hero. Above the copy
     layer (z-index 7) so the bars are genuinely clickable, not just visible. */
  .hero-slider__ui {
    position: absolute;
    z-index: 8;
    inset-block-end: var(--s7);
    inset-inline-end: var(--gutter);
    display: flex;
    gap: var(--s4);
  }

  .hero-slider__bar {
    display: grid;
    gap: var(--s2);
    width: clamp(48px, 7vw, 84px);
    padding: var(--s2) 0 0;
    background: none;
    border: 0;
    color: var(--d-muted);
    font-family: var(--f-mono);
    font-size: var(--t-xs);
    letter-spacing: 0.12em;
    text-align: left;
    transition: color var(--d-fast) var(--e-out);
  }

  .hero-slider__bar:hover { color: var(--d-text); }
  .hero-slider__bar[aria-current='true'] { color: var(--d-text); }

  .hero-slider__track {
    position: relative;
    height: 2px;
    background: color-mix(in srgb, var(--d-text) 26%, transparent);
    overflow: hidden;
  }

  .hero-slider__fill {
    position: absolute;
    inset: 0;
    background: var(--red);
    transform: scaleX(0);
    transform-origin: left center;
  }

  .hero-slider__bar[aria-current='true'] .hero-slider__fill {
    animation: hero-bar var(--slide-dur) linear forwards;
  }

  @keyframes hero-bar {
    from { transform: scaleX(0); }
    to { transform: scaleX(1); }
  }

  /* Reduced motion: slide 1 only, no Ken Burns, no autoplay, no line. The bars stay
     as manual controls and the fill simply reports which slide is showing. */
  @media (prefers-reduced-motion: reduce) {
    .hero-slide { transition: none; }
    .hero-slide img,
    .hero-slider__line { animation: none !important; }
    .hero-slider__line { opacity: 0; }
    .hero-slider__bar[aria-current='true'] .hero-slider__fill { animation: none; transform: scaleX(1); }
  }

  /* Machined grain over the photography — the same texture the rest of the site uses. */
  .hero__grain {
    position: absolute;
    inset: 0;
    z-index: 6;
    pointer-events: none;
    opacity: 0.5;
    background-image: repeating-linear-gradient(
      to bottom,
      rgb(255 255 255 / 0.028) 0 1px,
      transparent 1px 3px
    );
    mask-image: linear-gradient(to top, transparent, #000 30%, #000 70%, transparent);
  }

  .hero__inner { position: relative; z-index: 7; width: 100%; }

  .hero__title {
    font-size: var(--t-mega);
    font-weight: 800;
    line-height: 0.92;
    letter-spacing: -0.045em;
    text-transform: uppercase;
    max-width: 14ch;
  }

  /* The full stop is the cut. */
  .hero__dot { color: var(--red); }

  .hero__sub {
    max-width: 56ch;
    margin-block-start: var(--s5);
    font-size: clamp(16px, 2vw, 20px);
    line-height: 1.5;
    color: color-mix(in srgb, var(--d-text) 82%, transparent);
  }

  .hero__cta { display: flex; flex-wrap: wrap; gap: var(--s3); margin-block-start: var(--s6); }

  .hero__spec {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s3) var(--s6);
    margin-block-start: var(--s7);
    padding-block-start: var(--s4);
    border-block-start: 1px solid var(--d-line);
    list-style: none;
    padding-inline: 0;
    font-family: var(--f-mono);
    font-size: var(--t-xs);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--d-muted);
  }

  .hero__spec li { margin: 0; display: flex; align-items: center; gap: var(--s2); }
  .hero__spec li::before { content: ''; width: 5px; height: 5px; background: var(--red); }
  .hero__spec b { color: var(--d-text); font-weight: 500; }

  .searchband {
    position: relative;
    z-index: 4;
    padding-block: var(--s6);
    background: var(--d-surface);
    border-block-end: 1px solid var(--d-line);
  }

  .searchband__grid { display: grid; gap: var(--s4); align-items: center; }

  /* Grid items default to `min-width: auto`, which lets the search field's intrinsic
     minimum push the column past the viewport. This is what caused a 1px horizontal
     overflow on the homepage at 390px. */
  .searchband__grid > * { min-width: 0; }

  @media (min-width: 900px) {
    .searchband__grid { grid-template-columns: minmax(0, 1fr) auto; gap: var(--s6); }
  }

  .searchband__label {
    font-family: var(--f-mono);
    font-size: var(--t-xs);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--d-muted);
    margin-block-end: var(--s3);
  }

  /* === marquee spec ribbon (P5)
     Hairline rules above and below, mono uppercase, one slow pass. The track holds
     two identical runs and shifts by exactly -50%, so the loop has no seam. */

  .marquee {
    position: relative;
    overflow: hidden;
    padding-block: var(--s4);
    background: var(--bg);
    border-block: 1px solid var(--line);
    color: var(--muted);
    -webkit-user-select: none;
    user-select: none;
  }

  .marquee__track { display: flex; width: max-content; animation: marquee 46s linear infinite; }

  .marquee:hover .marquee__track { animation-play-state: paused; }

  .marquee__run { display: flex; flex: 0 0 auto; }

  .marquee__item {
    display: inline-flex;
    align-items: center;
    padding-inline: var(--s6);
    font-family: var(--f-mono);
    font-size: var(--t-xs);
    letter-spacing: 0.18em;
    text-transform: uppercase;
    white-space: nowrap;
  }

  /* The separator is the cut, not a bullet. */
  .marquee__item::after {
    content: '';
    width: 5px;
    height: 5px;
    margin-inline-start: var(--s6);
    background: var(--red);
  }

  @keyframes marquee {
    from { transform: translateX(0); }
    to { transform: translateX(-50%); }
  }

  /* Static under reduced motion — the strip still reads, it just does not travel. */
  @media (prefers-reduced-motion: reduce) {
    .marquee__track { animation: none; }
    .marquee { overflow-x: auto; scrollbar-width: none; }
  }

  /* === bento
     Six cells, fully tiled at every breakpoint — no empty grid areas.

       ≥1100px  12 columns:  [ Aluminijum 7×2 ][ Bakar 5 ]
                                              [ Mesing 5 ]
                             [ Bronza 4 ][ Plastika 4 ][ CTA 4 ]
       700–1099  2 columns:  Aluminijum full · Bakar|Mesing · Bronza|Plastika · CTA full
       <700px    1 column
     Auto-placement resolves those spans without holes, so no `dense` is needed. */

  .bento {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--s4);
    grid-auto-rows: minmax(260px, auto);
  }

  /* Tablet: two columns. The first cell always spans both; the last one does too
     whenever it would otherwise sit alone in a half-empty row — which is exactly when
     the cell count is even, because the hero has already taken a whole row. */
  @media (min-width: 700px) and (max-width: 1099.98px) {
    .bento { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .bento > :first-child { grid-column: 1 / -1; }
    .bento[data-cells='2'] > :last-child,
    .bento[data-cells='4'] > :last-child,
    .bento[data-cells='6'] > :last-child { grid-column: 1 / -1; }
  }

  /* Desktop: 12 columns. The spans are chosen per cell count so the last row is always
     complete — build/templates/blocks.mjs mirrors this table to compute `sizes`. */
  @media (min-width: 1100px) {
    .bento { grid-template-columns: repeat(12, minmax(0, 1fr)); }
    .bento > * { grid-column: span 4; }
    .bento > :first-child { grid-column: span 7; grid-row: span 2; }
    .bento > :nth-child(2),
    .bento > :nth-child(3) { grid-column: span 5; }

    /* Two cells: side by side, and no double-height hero to leave a hole under. */
    .bento[data-cells='2'] > * { grid-column: span 6; grid-row: span 1; }

    /* Four: the fourth takes the whole third row. */
    .bento[data-cells='4'] > :nth-child(4) { grid-column: span 12; }

    /* Five: the last two split it. */
    .bento[data-cells='5'] > :nth-child(4),
    .bento[data-cells='5'] > :nth-child(5) { grid-column: span 6; }
  }

  /* --- material card
     Photograph as a cover layer, gradient scrim over it, copy on the scrim. */

  .mat-card {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    min-height: 260px;
    padding: var(--s6);
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    color: var(--d-text);
    text-decoration: none;
    isolation: isolate;
    transition: border-color var(--d-base) var(--e-out), box-shadow var(--d-base) var(--e-out);
  }

  .mat-card:hover {
    color: var(--d-text);
    border-color: color-mix(in srgb, var(--tint) 60%, var(--line));
    /* Tint reads as an edge glow, never as a fill. */
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--tint) 34%, transparent),
                0 18px 50px color-mix(in srgb, var(--tint) 16%, transparent);
  }

  .mat-card__media { position: absolute; inset: 0; z-index: 0; overflow: hidden; background: var(--d-raise); }

  /* Brand imagery is graded (P4) — this is not a catalog product photo, so the
     consistent look matters more than the true material colour. */
  .mat-card__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    filter: saturate(0.86) contrast(1.09) brightness(0.98);
    transition: transform var(--d-cine) var(--e-out);
  }

  .mat-card:hover .mat-card__media img { transform: scale(1.04); }

  /* The scrim. Copy sits on this, so the text contrast does not depend on how the
     photograph happens to be exposed. */
  .mat-card__media::after {
    content: '';
    position: absolute;
    inset: 0;
    background:
      linear-gradient(to top, rgb(8 9 11 / 0.95) 0%, rgb(8 9 11 / 0.78) 38%, rgb(8 9 11 / 0.38) 72%, rgb(8 9 11 / 0.22) 100%),
      linear-gradient(90deg, rgb(8 9 11 / 0.55), rgb(8 9 11 / 0.18));
  }

  /* The cut: a red tick that draws across the top edge on hover. */
  .mat-card__media::before {
    content: '';
    position: absolute;
    z-index: 1;
    inset-block-start: 0;
    inset-inline: 0;
    height: 2px;
    background: var(--red);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform var(--d-base) var(--e-out);
  }

  .mat-card:hover .mat-card__media::before,
  .mat-card:focus-visible .mat-card__media::before { transform: scaleX(1); }

  .mat-card__body { position: relative; z-index: 2; display: grid; gap: var(--s3); }

  .mat-card__eyebrow {
    font-family: var(--f-mono);
    font-size: var(--t-xs);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    font-variant-numeric: tabular-nums;
    color: color-mix(in srgb, var(--d-text) 72%, transparent);
  }

  .mat-card__eyebrow::before {
    content: '';
    display: inline-block;
    width: 18px;
    height: 2px;
    margin-inline-end: var(--s2);
    vertical-align: 0.28em;
    background: var(--tint);
  }

  .mat-card__name {
    font-family: var(--f-display);
    font-size: clamp(22px, 2.4vw, 30px);
    font-weight: 700;
    letter-spacing: -0.03em;
    line-height: 1.05;
  }

  .mat-card__desc {
    max-width: 46ch;
    font-size: var(--t-sm);
    color: color-mix(in srgb, var(--d-text) 76%, transparent);
  }

  .mat-card__tags { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--s2); }
  .mat-card__tags li { margin: 0; }
  .mat-card__tags .tag { color: color-mix(in srgb, var(--d-text) 70%, transparent); border-color: rgb(255 255 255 / 0.22); }

  .mat-card--hero { min-height: 420px; }
  .mat-card--hero .mat-card__name { font-size: clamp(30px, 3.4vw, 46px); }
  .mat-card--hero .mat-card__desc { font-size: var(--t-body); max-width: 52ch; }

  /* Red cut tick above the hero card's copy — the same lead-in rule the section
     eyebrows use, at card scale. */
  .mat-card--hero .mat-card__body::before {
    content: '';
    position: absolute;
    inset-block-start: -16px;
    inset-inline-start: 0;
    width: 34px;
    height: 2px;
    background: var(--red);
  }

  /* --- CTA tile: completes the last row rather than leaving a hole. */

  .bento-cta {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    gap: var(--s3);
    min-height: 260px;
    padding: var(--s6);
    background: var(--surface);
    border: 1px solid var(--red);
    border-radius: var(--radius);
    text-decoration: none;
    color: inherit;
    overflow: hidden;
    transition: background var(--d-base) var(--e-out);
  }

  .bento-cta:hover { background: var(--raise); color: inherit; }

  .bento-cta__eyebrow {
    font-family: var(--f-mono);
    font-size: var(--t-xs);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    font-variant-numeric: tabular-nums;
    color: var(--accent-text);
  }

  .bento-cta__title {
    font-family: var(--f-display);
    font-size: clamp(20px, 2.1vw, 26px);
    font-weight: 700;
    letter-spacing: -0.03em;
    line-height: 1.1;
  }

  .bento-cta__desc { font-size: var(--t-sm); color: var(--muted); }

  .bento-cta__go {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    margin-block-start: var(--s2);
    font-family: var(--f-mono);
    font-size: var(--t-xs);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--accent-text);
  }

  .bento-cta__go span { transition: transform var(--d-base) var(--e-out); }
  .bento-cta:hover .bento-cta__go span { transform: translateX(5px); }

  .counters {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1px;
    background: var(--line);
    border: 1px solid var(--line);
    border-radius: var(--radius);
  }

  @media (min-width: 860px) {
    .counters { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  }

  .counter { padding: var(--s6) var(--s5); background: var(--bg); }

  .counter__n {
    display: block;
    font-family: var(--f-display);
    font-size: clamp(34px, 5vw, 62px);
    font-weight: 800;
    letter-spacing: -0.045em;
    line-height: 1;
    font-variant-numeric: tabular-nums;
  }

  .counter__n span { color: var(--red); }

  /* === odometer (P6)
     Each digit is a reel of 0…n; the reel starts showing 0 and translates up until
     the target digit lands. Later columns settle after earlier ones, which is what
     makes it read as a mechanism rather than as a fade. */

  .odo { display: inline-flex; align-items: flex-start; }
  .odo__sep { display: inline-block; }

  .odo__col {
    display: inline-block;
    overflow: hidden;
    height: 1em;
    line-height: 1;
  }

  .odo__reel {
    display: block;
    white-space: pre;
    line-height: 1;
    transform: translateY(0);
    transition: transform 900ms var(--e-out);
    transition-delay: calc(var(--i, 0) * 90ms);
  }

  /* The reel holds `--digit + 1` glyphs; landing on the last one means moving up by
     exactly `--digit` line boxes. */
  .odo.is-rolling .odo__reel { transform: translateY(calc(var(--digit, 0) * -1em)); }

  @media (prefers-reduced-motion: reduce) {
    .odo__reel { transition: none; transform: translateY(calc(var(--digit, 0) * -1em)); }
  }

  .counter__k {
    display: block;
    margin-block-start: var(--s3);
    font-family: var(--f-mono);
    font-size: var(--t-xs);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--muted);
  }

  /* === catalog hero band
     Dark band on top of a light page. The transition is a hard edge plus one
     red cut line — never a gradient fade. */

  .band {
    position: relative;
    padding-block: calc(var(--header-h) + var(--s7)) var(--s8);
    margin-block-start: calc(var(--header-h) * -1);
    background: var(--d-bg);
    color: var(--d-text);
    overflow: hidden;
  }

  /* Faint machined grid: vertical hairlines at the 12-column rhythm. */
  .band::before {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    background-image: repeating-linear-gradient(
      to right,
      var(--d-line) 0 1px,
      transparent 1px calc(100% / 12)
    );
    opacity: 0.5;
    mask-image: linear-gradient(to bottom, transparent, #000 40%, transparent);
  }

  /* === page-hero
     The photographic backdrop behind an inner page's dark band. One implementation,
     reused everywhere: cover image, overlay, grain. The overlay bottoms out at the
     page background colour so the band melts into the body rather than ending on a
     visible edge — the red seam below it is the only edge that shows. */
  .page-hero {
    position: absolute;
    inset: 0;
    z-index: 0;
    overflow: hidden;
    pointer-events: none;
    /* Belt and braces against a slow decode: the band is dark before the photo lands,
       so the white H1 is never briefly on nothing. */
    background: var(--d-bg);
  }

  .page-hero__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    /* Same grade as every other photograph on the site. */
    filter: saturate(0.86) contrast(1.09) brightness(0.98);
  }

  /* Two layers, for two different jobs.

     The vertical one is the brief's: it melts the band into the page background at the
     bottom edge. On its own it measured 2.0–2.6:1 for the breadcrumbs and the eyebrow,
     which live at the top where it is thinnest — those are 12px muted type over the
     brightest part of a workshop photograph.

     The horizontal one fixes that the same way the homepage hero does: all the copy is
     left-aligned inside 52rem, so darkening the left keeps the photograph readable as a
     photograph on the right while giving the small type a ground it can sit on.
     Measured after: 4.6–15:1 on every page type. See tests/banner-contrast.spec.mjs. */
  .page-hero::after {
    content: '';
    position: absolute;
    inset: 0;
    background:
      linear-gradient(90deg, rgb(8 9 11 / 0.82) 0%, rgb(8 9 11 / 0.55) 48%, rgb(8 9 11 / 0.2) 100%),
      linear-gradient(180deg, rgb(8 9 11 / 0.58) 0%, rgb(8 9 11 / 0.82) 62%, #08090b 100%);
  }

  .page-hero__grain {
    position: absolute;
    inset: 0;
    z-index: 1;
    opacity: 0.5;
    background-image: repeating-linear-gradient(
      to bottom,
      rgb(255 255 255 / 0.028) 0 1px,
      transparent 1px 3px
    );
    mask-image: linear-gradient(to top, transparent, #000 30%, #000 70%, transparent);
  }

  /* The machined column grid rides over the photograph, not under it. Stated flatly
     rather than behind `:has()` — the layering is harmless on a band with no photo. */
  .band::before { z-index: 1; }

  .band__inner { position: relative; z-index: 2; }

  /* Standalone pages — usluge, kvalitet, o-nama, kontakt — get a banner tall enough to
     read as a photograph. Category and product bands keep the height they had and
     simply gain the image layer. */
  .band--tall { min-height: clamp(340px, 44vh, 540px); display: flex; align-items: center; }

  .band__grid { display: grid; gap: var(--s6); align-items: end; }

  @media (min-width: 900px) {
    .band__grid { grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); gap: var(--s8); }
  }

  .band__title { margin-block-start: var(--s4); }
  .band__lede { margin-block-start: var(--s4); max-width: 58ch; color: color-mix(in srgb, var(--d-text) 80%, transparent); }

  .band__media {
    position: relative;
    aspect-ratio: 4 / 3;
    border: 1px solid var(--d-line);
    border-radius: var(--radius);
    overflow: hidden;
  }

  /* Section imagery: same grade as the bento (P4), plus a cool graphite tint so the
     mixed white balance of the scraped library reads as one set. */
  .band__media img { width: 100%; height: 100%; object-fit: cover; filter: saturate(0.86) contrast(1.09) brightness(0.98); }

  .band__media::before,
  .service__media::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    background: #8e97a3;
    mix-blend-mode: multiply;
    opacity: 0.14;
  }

  /* The tint appears as a 2px top edge on the hero image. */
  .band__media::after {
    content: '';
    position: absolute;
    inset-block-start: 0;
    inset-inline: 0;
    height: 2px;
    background: var(--tint);
  }

  /* The seam: hard edge where dark meets light. */
  .band-seam { height: 2px; background: var(--red); }

  .service {
    display: grid;
    gap: var(--s5);
    align-items: center;
    padding-block: var(--s8);
    border-block-end: 1px solid var(--line);
  }

  @media (min-width: 900px) {
    .service { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s8); }
    .service:nth-child(even) .service__media { order: -1; }
  }

  .service__media {
    position: relative;
    aspect-ratio: 16 / 10;
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: var(--radius);
  }

  .service__media img { width: 100%; height: 100%; object-fit: cover; filter: saturate(0.86) contrast(1.09) brightness(0.98); }

  .service__idx {
    font-family: var(--f-mono);
    font-size: var(--t-xs);
    letter-spacing: 0.12em;
    color: var(--accent-text);
  }

  .service__list { color: var(--muted); font-size: var(--t-sm); }

  .timeline { position: relative; }

  .timeline__track {
    display: flex;
    gap: var(--s5);
    padding-block: var(--s6);
    padding-inline-end: var(--gutter);
    overflow-x: auto;
    scroll-snap-type: x proximity;
    overscroll-behavior-x: contain;
    scrollbar-width: thin;
  }

  .timeline__item {
    flex: 0 0 min(340px, 82vw);
    scroll-snap-align: start;
    padding-block-start: var(--s5);
    border-block-start: 1px solid var(--line);
    position: relative;
  }

  .timeline__item::before {
    content: '';
    position: absolute;
    inset-block-start: -1px;
    inset-inline-start: 0;
    width: 34px;
    height: 2px;
    background: var(--red);
  }

  .timeline__year {
    font-family: var(--f-display);
    font-size: clamp(28px, 3.4vw, 44px);
    font-weight: 800;
    letter-spacing: -0.04em;
    line-height: 1;
    font-variant-numeric: tabular-nums;
  }

  .timeline__title { margin-block-start: var(--s3); font-family: var(--f-display); font-size: 1.05rem; font-weight: 600; }
  .timeline__desc { margin-block-start: var(--s2); color: var(--muted); font-size: var(--t-sm); }

  /* Reduced motion: the same markup stacks vertically. The track is a plain scroll
     container with tabindex, so it is keyboard-operable either way. */
  @media (prefers-reduced-motion: reduce) {
    .timeline__track { flex-direction: column; overflow-x: visible; padding-inline-end: 0; }
    .timeline__item { flex: 1 1 auto; width: 100%; }
  }

  /* Three equal-height cards. The tracks stretch, each card fills its track, and the
     contact rows are pushed to the bottom with `margin-top: auto` — so the middle card
     listing WhatsApp and Viber cannot make itself taller than the other two. */
  .contact-grid {
    display: grid;
    grid-template-columns: 1fr;
    align-items: stretch;
    gap: var(--s5);
  }

  @media (min-width: 900px) {
    .contact-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  }

  .contact-card {
    display: flex;
    flex-direction: column;
    gap: var(--s3);
    height: 100%;
    padding: var(--s6);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    border-block-start: 2px solid var(--red);
  }

  .contact-card__role {
    font-family: var(--f-mono);
    font-size: var(--t-xs);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--accent-text);
  }

  .contact-card__name { font-family: var(--f-display); font-size: var(--t-h3); font-weight: 700; letter-spacing: -0.02em; }
  .contact-card__note { color: var(--muted); font-size: var(--t-sm); }

  .contact-card__links {
    display: grid;
    gap: var(--s2);
    margin-block-start: auto;
    padding-block-start: var(--s5);
    border-block-start: 1px solid var(--line);
  }

  .contact-card__links a {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    font-family: var(--f-mono);
    font-size: var(--t-sm);
    letter-spacing: 0.03em;
    text-decoration: none;
    word-break: break-word;
  }

  .contact-card__links a::before { content: ''; flex: 0 0 auto; width: 5px; height: 5px; background: var(--red); }

  .hours { width: 100%; border-collapse: collapse; }

  .hours th,
  .hours td {
    padding: var(--s3) 0;
    text-align: left;
    border-block-end: 1px solid var(--line);
    font-size: var(--t-sm);
  }

  .hours th { font-weight: 500; }
  .hours td { text-align: right; font-family: var(--f-mono); color: var(--muted); }
  .hours tr.is-today th, .hours tr.is-today td { color: var(--text); }
  .hours tr.is-today th::after { content: ' · danas'; color: var(--accent-text); font-family: var(--f-mono); font-size: var(--t-xs); }

  .map-slot {
    position: relative;
    aspect-ratio: 16 / 10;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
    display: grid;
    place-items: center;
  }

  .map-slot iframe { width: 100%; height: 100%; border: 0; }

  .inquiry-summary {
    padding: var(--s5);
    background: var(--raise);
    border: 1px solid var(--line);
    border-inline-start: 2px solid var(--red);
    border-radius: var(--radius);
  }

  .inquiry-summary table { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
  .inquiry-summary th, .inquiry-summary td { padding: var(--s2) 0; text-align: left; border-block-end: 1px solid var(--line); }
  .inquiry-summary td:last-child { text-align: right; font-family: var(--f-mono); }

  .atest-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--s4); }

  /* Placeholder slot for a certificate scan the client has not supplied yet.
     Deliberately looks unfinished — never a fabricated document. */
  .atest-slot {
    position: relative;
    aspect-ratio: 3 / 4;
    display: grid;
    place-content: center;
    gap: var(--s3);
    padding: var(--s5);
    text-align: center;
    background: var(--surface);
    border: 1px dashed var(--line);
    border-radius: var(--radius);
    color: var(--muted);
  }

  .atest-slot::before {
    content: '';
    position: absolute;
    inset-block-start: 0;
    inset-inline: 0;
    height: 1px;
    background: var(--red);
    opacity: 0.4;
  }

  .atest-slot__label {
    font-family: var(--f-mono);
    font-size: var(--t-xs);
    letter-spacing: 0.1em;
    text-transform: uppercase;
  }

  .atest-slot__state { font-size: var(--t-xs); color: color-mix(in srgb, var(--muted) 78%, transparent); }

  .e404 {
    display: grid;
    place-content: center;
    gap: var(--s6);
    min-height: 78svh;
    padding-block: var(--s9);
    text-align: center;
  }

  .e404__code {
    position: relative;
    font-family: var(--f-display);
    font-size: clamp(88px, 22vw, 240px);
    font-weight: 800;
    letter-spacing: -0.06em;
    line-height: 0.85;
  }

  /* The cut severs the 404. */
  .e404__code::after {
    content: '';
    position: absolute;
    inset-block-start: 52%;
    inset-inline: -6%;
    height: 2px;
    background: var(--red);
    transform: scaleX(0);
    transform-origin: left center;
    animation: cut-in var(--d-cine) var(--e-out) 260ms forwards;
  }

  @keyframes cut-in { to { transform: scaleX(1); } }

  @media (prefers-reduced-motion: reduce) {
    .e404__code::after { animation: none; transform: scaleX(1); }
  }

  .e404__links {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--s2);
    list-style: none;
    padding: 0;
  }

  .e404__links li { margin: 0; }

  .cta-band {
    display: grid;
    gap: var(--s5);
    align-items: center;
    padding: var(--s8) var(--s6);
    background: var(--surface);
    border: 1px solid var(--line);
    border-block-start: 2px solid var(--red);
    border-radius: var(--radius);
  }

  @media (min-width: 860px) {
    .cta-band { grid-template-columns: minmax(0, 1fr) auto; gap: var(--s8); }
  }

  .cta-band__title { font-family: var(--f-display); font-size: clamp(24px, 3vw, 38px); font-weight: 700; letter-spacing: -0.03em; }
  .cta-band__desc { margin-block-start: var(--s3); color: var(--muted); max-width: 58ch; }
  .cta-band__actions { display: flex; flex-wrap: wrap; gap: var(--s3); }

  .related { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: var(--s4); }

  .related__item {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    padding: var(--s5);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    text-decoration: none;
    color: inherit;
    transition: border-color var(--d-fast) var(--e-out);
  }

  .related__item:hover { border-color: var(--red); color: inherit; }

  .related__name { font-family: var(--f-display); font-size: 1.02rem; font-weight: 600; letter-spacing: -0.015em; }
  .related__meta { font-family: var(--f-mono); font-size: var(--t-xs); letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }

  /* === print (P12)
     B2B buyers print dimension tables and take them to the shop floor. Make that
     look intentional: black on white, every row visible with real borders, and the
     page URL and phone number in the footer of the printout so the sheet can be
     traced back to where it came from. */

  @media print {
    :root {
      --bg: #fff;
      --surface: #fff;
      --raise: #fff;
      --text: #000;
      --muted: #333;
      --line: #999;
      --gutter: 0;
      --header-h: 0px;
    }

    .site-header,
    .site-footer,
    .mobile-menu,
    .mega,
    .dock,
    .assistant,
    .inquiry-drawer,
    .drawer-scrim,
    .cursor-ring,
    .scroll-progress,
    .page-wipe,
    .preloader,
    .hero-slider,
    .hero__grain,
    .skip-link,
    .marquee,
    .footer-mark,
    .map-slot,
    .filters,
    .cell-act,
    .catalog__foot .btn,
    .cta-band,
    .related,
    .searchband { display: none !important; }

    body { background: #fff; color: #000; font-size: 11pt; }

    /* Nothing is hidden by pagination or by a reveal that never fired. */
    [data-reveal] { opacity: 1 !important; transform: none !important; clip-path: none !important; }
    [data-reveal] > * { clip-path: none !important; }
    .catalog-table tbody tr[data-page-hidden] { display: table-row !important; }
    .catalog__scroll { overflow: visible !important; }
    .hero { min-height: 0; height: auto; padding-block: 0 var(--s5); margin-block-start: 0; color: #000; background: #fff; }

    .catalog-table { width: 100%; border-collapse: collapse; font-size: 9.5pt; }

    .catalog-table th,
    .catalog-table td {
      border: 1px solid #999 !important;
      padding: 4pt 6pt !important;
      color: #000;
    }

    .catalog-table thead {
      display: table-header-group; /* repeat the header on every printed page */
      position: static;
      width: auto;
      height: auto;
      clip-path: none;
      overflow: visible;
    }

    .catalog-table thead th { background: #eee; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
    .catalog-table tbody tr { page-break-inside: avoid; }
    .chip { border-color: #999; color: #000; }

    h1, h2, h3 { page-break-after: avoid; color: #000; }
    a { color: #000; text-decoration: none; }
    .prose a::after { content: ' (' attr(href) ')'; font-size: 8pt; color: #555; }

    /* The provenance line, printed once at the end of the sheet. */
    main::after {
      content: 'nikomadoo.com · Nikoma d.o.o. Novi Sad · Temerinski put 81 · 021 64 11 380 · office@nikomadoo.com';
      display: block;
      margin-block-start: 12pt;
      padding-block-start: 6pt;
      border-block-start: 1px solid #999;
      font-family: var(--f-mono);
      font-size: 8pt;
      color: #333;
    }

    @page { margin: 14mm 12mm; }
  }
}
