/* ==========================================================================
   ARK ELECTRONIC — interaction layer

   Two jobs.

   1. Rules the approved prototype kept in an inline <style> block in every one of
      its twenty files rather than in the stylesheet. The theme-toggle icon
      swapping is the important one: without it both the sun and the moon render
      side by side, which is what the client saw in the header.

   2. The hover and motion pass. The pages carried the right structure but sat
      still — a catalogue of industrial control gear should feel engineered when
      you touch it, not like a printed page. Everything here is a transform or an
      opacity change on a compositor-friendly property, so none of it costs a
      layout pass, and all of it is switched off under prefers-reduced-motion.
   ========================================================================== */



@media (max-width: 640px) { .hide-sm { display: none; } }

/* ==========================================================================
   HOVER AND MOTION
   ========================================================================== */

/* ------------------------------------------------------------- product cards
   The card lifts and its photograph pushes in slightly — the picture moving a
   little more than its frame is what reads as depth rather than as a box sliding
   about. The border warms to the brand red so the whole card, not just the
   button, registers as the hit target. */
.p-card {
  transition:
    transform var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease),
    border-color var(--dur) var(--ease);
  will-change: transform;
}

.p-card:hover,
.p-card:focus-within {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
  border-color: var(--accent);
}

.p-card .media img {
  transition: transform 520ms var(--ease);
}

.p-card:hover .media img,
.p-card:focus-within .media img {
  transform: scale(1.06);
}

/* The badge tucks up out of the way as the card rises, so it does not collide
   with the growing photograph. */
.p-card .badge {
  transition: transform var(--dur) var(--ease);
}
.p-card:hover .badge { transform: translateY(-2px); }

/* ------------------------------------------------------------------- cards
   Feature and content cards get the same language, quieter. */
.card {
  transition:
    transform var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease),
    border-color var(--dur) var(--ease);
}

.card.feature:hover,
.card.feature:focus-within {
  transform: translateY(-3px);
  box-shadow: var(--shadow);
  border-color: var(--border-strong);
}

/* The icon in a feature card turns to the accent and tilts a degree. One degree
   is deliberate: enough to read as responsive, not enough to look broken. */
.card.feature .icon-box {
  transition:
    background-color var(--dur) var(--ease),
    color var(--dur) var(--ease),
    transform var(--dur) var(--ease);
}

.card.feature:hover .icon-box {
  background: var(--accent-soft);
  color: var(--accent-ink);
  transform: rotate(-1deg) scale(1.06);
}

/* --------------------------------------------------------------- buttons
   A primary button sinks 1px on press. The arrow inside it walks forward on
   hover, which is the cheapest way to say "this goes somewhere". */
.btn {
  transition:
    background-color var(--dur-micro) var(--ease),
    border-color var(--dur-micro) var(--ease),
    color var(--dur-micro) var(--ease),
    transform var(--dur-micro) var(--ease),
    box-shadow var(--dur-micro) var(--ease);
}

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

.btn-primary:hover { box-shadow: 0 6px 18px rgba(214, 40, 40, .28); }

.btn svg {
  transition: transform var(--dur) var(--ease);
}
.btn:hover svg { transform: translateX(-3px); }        /* RTL: forward is left */

/* A text button grows an underline from the start edge rather than switching one
   on, so the emphasis has direction. */
.btn-text {
  position: relative;
}
.btn-text::after {
  content: "";
  position: absolute;
  inset-block-end: 2px;
  inset-inline-start: 0;
  inline-size: 0;
  block-size: 1.5px;
  background: currentColor;
  transition: inline-size var(--dur) var(--ease);
}
.btn-text:hover::after { inline-size: 100%; }

/* ------------------------------------------------------------------- nav
   The same growing rule under a nav item, and the current page keeps it. */
.nav a {
  position: relative;
  transition: color var(--dur-micro) var(--ease);
}
/* components.css already draws this rule for the current page, inset by the
   link's own padding. Overriding the geometry made it start at the edge of the
   box and hang below the link — which is the underline "sticking out" on the
   active page. So keep its position exactly and animate scale instead of width:
   the rule grows from the start edge and lands where the static one already is. */
.nav a::after {
  content: "";
  position: absolute;
  inset-inline: var(--s-3);
  inset-block-end: 2px;
  block-size: 2px;
  background: var(--accent);
  border-radius: 2px;
  transform: scaleX(0);
  transform-origin: inline-start;
  transition: transform var(--dur) var(--ease);
}
.nav a:hover::after,
.nav a[aria-current="page"]::after { transform: scaleX(1); }

/* --------------------------------------------------------------- icon buttons
   The header icons rotate a little on hover — a lift panel's own controls have
   detents, and this is the same idea in a UI. */
.icon-btn {
  transition:
    background-color var(--dur-micro) var(--ease),
    border-color var(--dur-micro) var(--ease),
    color var(--dur-micro) var(--ease),
    transform var(--dur) var(--ease);
}
.icon-btn:hover {
  transform: translateY(-1px) rotate(-4deg);
  border-color: var(--accent);
  color: var(--accent-ink);
}
.icon-btn:active { transform: translateY(0) rotate(0); }

/* The basket count pops when it changes — driven by a class the page adds, not
   by :hover, because the number changing is the event worth noticing. */
.cart-count {
  transition: transform var(--dur) var(--ease);
}
.icon-btn:hover .cart-count { transform: scale(1.12); }

/* ------------------------------------------------------------------ chips
   Model chips are a comparison row: the one you are pointing at should stand
   forward of the others. */
.chip {
  transition:
    background-color var(--dur-micro) var(--ease),
    border-color var(--dur-micro) var(--ease),
    color var(--dur-micro) var(--ease),
    transform var(--dur-micro) var(--ease);
}
.chip:hover {
  transform: translateY(-2px);
  border-color: var(--accent);
  color: var(--accent-ink);
}

/* --------------------------------------------------------- filters and tree
   A filter row fills from the start edge on hover. The bar is 2px and coloured,
   so a long list of categories reads as a list of targets rather than text. */
.check,
.tree-link {
  position: relative;
  transition:
    background-color var(--dur-micro) var(--ease),
    color var(--dur-micro) var(--ease),
    padding-inline-start var(--dur-micro) var(--ease);
}

.check::before,
.tree-link::before {
  content: "";
  position: absolute;
  inset-block: 4px;
  inset-inline-start: 0;
  inline-size: 0;
  background: var(--accent);
  border-radius: 2px;
  transition: inline-size var(--dur-micro) var(--ease);
}

.check:hover::before,
.tree-link:hover::before,
.tree-link.is-current::before { inline-size: 2px; }

.check:hover,
.tree-link:hover {
  background: var(--surface-inset);
  padding-inline-start: 10px;
}

/* The chevron on a tree branch turns as it opens. */
.tree-chevron { transition: transform var(--dur) var(--ease); }
details[open] > summary .tree-chevron { transform: rotate(-90deg); }

/* --------------------------------------------------------------- finder opts
   The wizard's option cards are the whole interaction on that page, so they get
   the most pronounced treatment on the site. */
.opt {
  transition:
    transform var(--dur) var(--ease),
    border-color var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease),
    background-color var(--dur) var(--ease);
}
.opt:hover {
  transform: translateY(-4px);
  border-color: var(--accent);
  box-shadow: var(--shadow-lg);
}
.opt .icon-box {
  transition: transform var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.opt:hover .icon-box {
  transform: scale(1.1) rotate(-3deg);
  background: var(--accent-soft);
}

/* -------------------------------------------------------------- table rows
   A 330-row catalogue table needs the row under the cursor to be unmistakable. */
.tbl tbody tr {
  transition: background-color var(--dur-micro) var(--ease);
}
.tbl tbody tr:hover { background: var(--surface-inset); }
.tbl tbody tr.active { background: var(--accent-soft); }

/* ------------------------------------------------------------ gallery thumbs */
.thumbs button {
  transition:
    border-color var(--dur-micro) var(--ease),
    transform var(--dur-micro) var(--ease),
    opacity var(--dur-micro) var(--ease);
  opacity: .72;
}
.thumbs button:hover,
.thumbs button[aria-current="true"] {
  opacity: 1;
  transform: translateY(-2px);
  border-color: var(--accent);
}

/* ------------------------------------------------------------------ inputs */
.input,
.select,
textarea,
.field input,
.field textarea,
.field select {
  transition:
    border-color var(--dur-micro) var(--ease),
    box-shadow var(--dur-micro) var(--ease),
    background-color var(--dur-micro) var(--ease);
}

.input:focus,
.select:focus,
textarea:focus,
.field input:focus,
.field textarea:focus,
.field select:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
  outline: none;
}

/* ------------------------------------------------------------------- links */
.prose a,
.crumbs a {
  transition: color var(--dur-micro) var(--ease);
}
.prose a:hover,
.crumbs a:hover { color: var(--accent-ink); }

/* ------------------------------------------------------------- metric strip
   The numbers count as a group when the strip is pointed at — a small thing that
   makes the row read as one instrument panel rather than four separate figures. */
.metric { transition: background-color var(--dur) var(--ease); }
.metric:hover { background: var(--surface-inset); }
.metric-value { transition: transform var(--dur) var(--ease), color var(--dur) var(--ease); }
.metric:hover .metric-value { transform: scale(1.06); color: var(--accent-ink); }

/* ------------------------------------------------------------- dash cells
   The technical dashboard is the most "instrument" part of the site. Each cell
   picks up an accent rule on its start edge. */
.dash-cell {
  position: relative;
  transition: background-color var(--dur) var(--ease);
}
.dash-cell::before {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  inline-size: 0;
  background: var(--accent);
  transition: inline-size var(--dur) var(--ease);
}
.dash-cell:hover::before { inline-size: 3px; }

/* ---------------------------------------------------------------- reveal
   Driven by app.js: the class goes on at build time, `in` is added when the
   element scrolls into view. The safety net in the script adds `in` to
   everything after 2.5s, so a failed observer can never leave a page blank. */
.reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 520ms var(--ease), transform 520ms var(--ease);
}
.reveal.in {
  opacity: 1;
  transform: none;
}

/* ------------------------------------------------------------- partner rail
   An endless horizontal drift, as on the arka site. The track holds the list
   twice and translates by exactly -50%, so the second copy is under the cursor
   at the moment the first finishes and the loop has no seam.

   Paused on hover, because a name the reader is trying to read should stop. */
.rail {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}

.rail-track {
  display: flex;
  inline-size: max-content;
  gap: var(--s-6);
  animation: rail-drift var(--rail-duration, 42s) linear infinite;
}

.rail:hover .rail-track,
.rail:focus-within .rail-track { animation-play-state: paused; }

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

/* A logo is grey until pointed at, so the row reads as one texture and the one
   you are looking at separates from it. */
.rail .partner-chip {
  transition: filter var(--dur) var(--ease), opacity var(--dur) var(--ease),
              transform var(--dur) var(--ease);
  filter: grayscale(1);
  opacity: .62;
}
.rail .partner-chip:hover {
  filter: grayscale(0);
  opacity: 1;
  transform: translateY(-2px);
}

/* ==========================================================================
   HERO
   ========================================================================== */

/* The slides cross-fade in place. Absolute positioning inside a grid cell keeps
   them stacked without taking the image out of the document's height. */
.hero-slides {
  position: relative;
  display: grid;
}

.hero-slide {
  grid-area: 1 / 1;
  opacity: 0;
  transform: scale(1.04);
  transition: opacity 900ms var(--ease), transform 6000ms linear;
  pointer-events: none;
}

.hero-slide.is-active {
  opacity: 1;
  transform: scale(1);
  pointer-events: auto;
}

/* ==========================================================================
   Nothing above is load-bearing. Everything is switched off for anyone who has
   asked their system for less motion — including the hero, which then simply
   shows its first slide.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .p-card,
  .p-card .media img,
  .card,
  .card.feature .icon-box,
  .btn,
  .btn svg,
  .icon-btn,
  .chip,
  .opt,
  .opt .icon-box,
  .metric-value,
  .thumbs button,
  .hero-slide {
    transition: none !important;
    transform: none !important;
  }

  .rail-track { animation: none; }

  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* --------------------------------------------------------------- portraits
   The managers' photographs were cut out of business-card JPEGs, so they carry a
   little of the card's own ring at the edge. The border hides that seam and the
   grayscale-to-colour on hover matches the logo rail. */
.portrait {
  inline-size: 132px;
  block-size: 132px;
  border-radius: 50%;
  object-fit: cover;
  margin-inline: auto;
  margin-block-end: var(--s-4);
  border: 3px solid var(--border);
  transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.card:hover .portrait {
  border-color: var(--accent);
  transform: scale(1.04);
}

@media (prefers-reduced-motion: reduce) {
  .portrait { transition: none; }
  .card:hover .portrait { transform: none; }
}

/* -------------------------------------------------------- ticket thread
   A staff reply is marked by an accent edge rather than by being pushed to the
   other side. A chat layout that alternates alignment reads badly in a
   right-to-left page carrying Latin part numbers — the eye loses the thread. */
.card.is-staff {
  border-inline-start: 3px solid var(--accent);
  background: var(--surface-inset);
}

/* Signed amounts in the wallet ledger. */
.yes { color: #15803D; font-weight: 600; }
.no  { color: var(--accent-ink); font-weight: 600; }

/* Dark is the site's default; only the switch in the header turns it light. */
:root:not([data-theme="light"]) .yes { color: #4ADE80; }
:root[data-theme="dark"] .yes { color: #4ADE80; }

/* --------------------------------------------------------------- hero caption
   Names the product in each frame. Sits inside the figure so it fades with the
   slide rather than snapping between labels. */
.hero-slide { margin: 0; }
.hero-cap {
  margin-block-start: var(--s-3);
  text-align: center;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text-muted);
  letter-spacing: .01em;
}

/* ------------------------------------------------------------- comparison
   The winning cell in a numeric row. Marked with a badge as well as a tint,
   because colour alone is not available to every reader. */
.tbl td.is-best {
  background: var(--accent-soft);
  font-weight: 600;
}

/* A chip that is currently on, in the comparison filter. */
.chip.is-on {
  border-color: var(--accent);
  color: var(--accent-ink);
  background: var(--accent-soft);
}

/* ---------------------------------------------------------- article figure
   The photograph on a blog card. Same gradient ground as a product card so the
   cut-out panel shots sit on something rather than floating on the card. */
.post-figure {
  aspect-ratio: 4 / 3;
  display: grid;
  place-items: center;
  overflow: hidden;
  border-radius: var(--radius-sm);
  margin-block-end: var(--s-4);
  padding: var(--s-4);
  background: radial-gradient(ellipse at 50% 35%, var(--surface-2) 0%, var(--surface-inset) 100%);
}
.post-figure img {
  max-block-size: 100%;
  inline-size: auto;
  object-fit: contain;
  transition: transform 520ms var(--ease);
}
.card:hover .post-figure img { transform: scale(1.05); }

@media (prefers-reduced-motion: reduce) {
  .post-figure img { transition: none; }
  .card:hover .post-figure img { transform: none; }
}

/* ------------------------------------------------------------ leader cards
   The business card is the artwork; the card around it is only a frame, so it
   carries no padding at the top and the image sits flush. */
.card.leader {
  padding: 0;
  overflow: hidden;
}

.leader-card {
  inline-size: 100%;
  block-size: auto;
  display: block;
  transition: transform 520ms var(--ease);
}

.card.leader:hover .leader-card { transform: scale(1.03); }

.leader-body {
  padding: var(--s-4) var(--s-5) var(--s-5);
  border-block-start: 1px solid var(--border);
}

@media (prefers-reduced-motion: reduce) {
  .leader-card { transition: none; }
  .card.leader:hover .leader-card { transform: none; }
}

/* ---------------------------------------------------- enquiry subject card
   The part an enquiry is about, shown to the customer before they write. It is
   also what the operator sees quoted back in the ticket, so both ends of the
   conversation are looking at the same part number. */
.enquiry-subject {
  display: flex;
  gap: var(--s-4);
  align-items: center;
  padding: var(--s-4);
  border: 1px solid var(--border);
  border-inline-start: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--surface-inset);
  margin-block-end: var(--s-4);
}

.enquiry-figure {
  inline-size: 76px;
  block-size: 76px;
  flex: none;
  display: grid;
  place-items: center;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--surface);
}
.enquiry-figure img { max-inline-size: 100%; max-block-size: 100%; object-fit: contain; }
.enquiry-subject > div:last-child > * + * { margin-block-start: 2px; }

/* ------------------------------------------------------------ language switch
   Three two-letter links. Compact because the header already carries a theme
   toggle, a basket, an account and a CTA — a dropdown would be one more thing
   to open for a choice most visitors make once. */
.lang-switch {
  display: flex;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.lang-option {
  padding: 4px 8px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .03em;
  border-radius: 4px;
  color: var(--text-muted);
  text-decoration: none;
  transition: background-color var(--dur-micro) var(--ease), color var(--dur-micro) var(--ease);
}

.lang-option:hover { color: var(--text); background: var(--surface-inset); }
.lang-option.is-on { background: var(--accent); color: #fff; }

@media (max-width: 640px) { .lang-switch { display: none; } }

/* ------------------------------------------------------------ panel extras */
.recovery-codes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 6px;
  margin-block-start: 10px;
  padding: 0;
  list-style: none;
  font-family: ui-monospace, Menlo, Consolas, monospace;
}
.recovery-codes li {
  padding: 6px 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  text-align: center;
}

.secret-box {
  padding: 14px;
  margin-block: 12px;
  border: 1px dashed var(--border-strong);
  border-radius: 8px;
  background: var(--surface-inset);
  text-align: center;
}
.secret-value { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 18px; letter-spacing: .08em; }

.pill-warn { background: var(--warn-soft); color: var(--warn); }

/* ==========================================================================
   LANGUAGE MENU
   ========================================================================== */

/* A globe, the current language in its own script, and a panel for the rest.
   Built on <details>, so it opens with no JavaScript and the keyboard behaviour
   is the browser's. */
.lang {
  position: relative;
}

.lang-current {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-muted);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  list-style: none;
  user-select: none;
  transition:
    border-color var(--dur-micro) var(--ease),
    color var(--dur-micro) var(--ease),
    background-color var(--dur-micro) var(--ease);
}

/* The default disclosure triangle, gone in every engine. */
.lang-current::-webkit-details-marker { display: none; }
.lang-current::marker { content: ""; }

.lang-current svg { inline-size: 16px; block-size: 16px; flex: none; }

.lang-current:hover,
.lang[open] .lang-current {
  color: var(--text);
  border-color: var(--border-strong);
  background: var(--surface-inset);
}

.lang-caret {
  inline-size: 13px !important;
  block-size: 13px !important;
  opacity: .6;
  transition: transform var(--dur) var(--ease);
}
.lang[open] .lang-caret { transform: rotate(180deg); }

/* The panel. Anchored to the end edge so it opens inward from the header rather
   than off the side of the viewport. */
/* Hidden until opened, stated explicitly.
   `<details>` hides its own children, but an absolutely positioned one escapes
   that: the closed menu was still being laid out 190px wide, off the edge of a
   375px screen, which is where the page's sideways scroll came from. It was also
   reachable by keyboard while invisible. */
.lang:not([open]) .lang-menu { display: none; }

.lang-menu {
  position: absolute;
  inset-block-start: calc(100% + 6px);
  inset-inline-end: 0;
  z-index: 60;
  min-inline-size: 190px;
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  animation: lang-open 160ms var(--ease);
}

@keyframes lang-open {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

.lang-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 10px;
  border-radius: 6px;
  color: var(--text);
  font-size: 14px;
  text-decoration: none;
  transition: background-color var(--dur-micro) var(--ease);
}

.lang-item:hover { background: var(--surface-inset); }

/* The two-letter code is the anchor the eye scans; the full name is the label. */
.lang-code {
  min-inline-size: 26px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .05em;
  color: var(--text-muted);
}

.lang-label { flex: 1; }

.lang-item.is-on { background: var(--accent-soft); }
.lang-item.is-on .lang-code { color: var(--accent-ink); }
.lang-tick { inline-size: 15px; block-size: 15px; color: var(--accent); flex: none; }

/* Below the tablet break the header is already crowded; the name goes and the
   globe alone opens the menu. */
@media (max-width: 1024px) {
  .lang-current .lang-name,
  .lang-current .lang-caret { display: none; }
  .lang-current { padding: 8px; }
}

@media (prefers-reduced-motion: reduce) {
  .lang-menu { animation: none; }
  .lang-caret { transition: none; }
}

/* ==========================================================================
   METRIC COUNTERS
   ========================================================================== */

/* The figures count up from zero when the strip scrolls into view, with a
   growth marker beside them. Driven by app.js; the final value is already in
   the markup, so with JavaScript off — or reduced motion — the number is simply
   there, which is the only acceptable failure mode for a factual figure. */
.metric-value {
  display: inline-flex;
  align-items: baseline;
  gap: 1px;
  font-variant-numeric: tabular-nums;
}

.metric-plus {
  font-size: .62em;
  font-weight: 700;
  color: var(--accent);
  transform: translateY(-.35em);
  opacity: 0;
  transition: opacity 320ms var(--ease) 420ms;
}

.metric.is-counted .metric-plus { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .metric-plus { opacity: 1; transition: none; }
}

/* ==========================================================================
   LEADERSHIP CARDS
   ========================================================================== */

/* Portrait, name, role, and a direct line. On a B2B site someone looking at the
   technical manager usually wants to talk to the technical manager, so the
   contact is the point of the card rather than a footnote. */
.leader-card-v2 {
  position: relative;
  overflow: hidden;
  text-align: center;
  padding-block-start: var(--s-7);
}

/* A brand rule across the top. It is what stops three portraits on white from
   reading as a stock-photo grid. */
.leader-accent {
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  block-size: 4px;
  background: linear-gradient(90deg, var(--accent), var(--accent-hover));
}

.leader-portrait {
  inline-size: 116px;
  block-size: 116px;
  border-radius: 50%;
  object-fit: cover;
  margin-inline: auto;
  margin-block-end: var(--s-4);
  border: 3px solid var(--surface);
  box-shadow: 0 0 0 1px var(--border), var(--shadow);
  transition: box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.leader-card-v2:hover .leader-portrait {
  transform: translateY(-2px);
  box-shadow: 0 0 0 2px var(--accent), var(--shadow-lg);
}

.leader-role {
  color: var(--accent-ink);
  font-weight: 600;
  font-size: 0.9375rem;
  margin-block: 2px var(--s-2);
}

.leader-contact {
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: center;
  margin-block-start: var(--s-4);
  padding-block-start: var(--s-4);
  border-block-start: 1px solid var(--border);
}

/* Each line is the whole hit target, not just the icon — a 16px icon is a hard
   thing to tap and these are meant to be tapped from a phone. */
.leader-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border-radius: 999px;
  font-size: 0.875rem;
  color: var(--text-muted);
  text-decoration: none;
  transition: background-color var(--dur-micro) var(--ease), color var(--dur-micro) var(--ease);
}

.leader-link svg { inline-size: 16px; block-size: 16px; flex: none; }

.leader-link:hover {
  background: var(--accent-soft);
  color: var(--accent-ink);
}

@media (prefers-reduced-motion: reduce) {
  .leader-portrait { transition: none; }
  .leader-card-v2:hover .leader-portrait { transform: none; }
}

/* ==========================================================================
   LIQUID GLASS — the public site

   The client asked for the panel's material to appear on the site too, "in the
   right places". The right places are the ones where glass has something to do:
   a surface only reads as glass when what is behind it moves or varies. Over a
   flat section it is a lighter rectangle, and every extra blurred layer costs a
   compositor pass on the phones this site is mostly read on.

   So it is used on five surfaces and no others:

     1. the header, which content scrolls under
     2. the mobile nav, which drops over the page
     3. the language menu, which floats over whatever is beneath it
     4. the catalogue rail and filter panel, which sit still while the grid moves
     5. the sticky mobile CTA, which the page runs beneath

   Everything else on the site keeps its solid surface, deliberately. The hero's
   trust strip was a sixth candidate and was dropped: it sits in the hero's text
   column over a flat band, so there is nothing behind it to refract — it would
   have been a slightly lighter rectangle bought with a compositor pass.

   ── What separates glass from frosted plastic ────────────────────────────
   The header already had `backdrop-filter: blur(14px)` and read as a milky bar.
   Three things were missing: saturation, so colour behind it stays colour rather
   than going grey; a specular edge, brighter along the top than the bottom; and
   a shadow that is cast rather than drawn, so the bar sits above the page instead
   of being ruled off from it.

   ── Both spellings, on purpose ───────────────────────────────────────────
   There is no build step on this host to add the prefix, and Safari before 18
   only understands `-webkit-backdrop-filter` — which is most of the iPhones this
   client's installers carry into a lift shaft.
   ========================================================================== */

:root {
  --glass:        rgba(255, 255, 255, 0.72);
  --glass-strong: rgba(255, 255, 255, 0.86);
  --glass-nav:    rgba(255, 255, 255, 0.97);
  --glass-edge:   rgba(255, 255, 255, 0.9);
  --glass-top:    rgba(255, 255, 255, 0.95);
  --glass-cast:   rgba(13, 27, 42, 0.10);
  --glass-blur:   blur(20px) saturate(180%);
}

/* Dark theme: the same material over a dark ground. The tint goes to the band
   navy rather than to black — black over a navy page reads as a hole, not as a
   pane — and the specular edge drops to a hint, because a bright white hairline
   on a dark surface is a drawn line, not a highlight. */
:root[data-theme="dark"] {
  --glass:        rgba(13, 27, 42, 0.72);
  --glass-strong: rgba(13, 27, 42, 0.88);
  --glass-nav:    rgba(13, 27, 42, 0.97);
  --glass-edge:   rgba(255, 255, 255, 0.12);
  --glass-top:    rgba(255, 255, 255, 0.16);
  --glass-cast:   rgba(0, 0, 0, 0.4);
}

/* Dark is the site's default; only the switch in the header turns it light. */
:root:not([data-theme="light"]) {
  --glass:        rgba(13, 27, 42, 0.72);
  --glass-strong: rgba(13, 27, 42, 0.88);
  --glass-nav:    rgba(13, 27, 42, 0.97);
  --glass-edge:   rgba(255, 255, 255, 0.12);
  --glass-top:    rgba(255, 255, 255, 0.16);
  --glass-cast:   rgba(0, 0, 0, 0.4);
}

/* ── 1. the header ───────────────────────────────────────────────────────── */
.header {
  background: var(--glass);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border-block-end: 1px solid var(--glass-edge);
  box-shadow: inset 0 1px 0 var(--glass-top), 0 8px 24px -20px var(--glass-cast);
}

/* ── 2. the mobile nav ───────────────────────────────────────────────────── */
/* The one glass surface that gets its own, denser tint. The drawer covers the hero
   photograph, and at the shared strength the image came through hard enough that
   the links were work to read. A navigation drawer is the last place to trade
   legibility for material — so it keeps the blur and the specular edge and drops
   most of the transparency. */
@media (max-width: 1080px) {
  .nav {
    background: var(--glass-nav);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border-block-end: 1px solid var(--glass-edge);
    box-shadow: inset 0 1px 0 var(--glass-top), 0 18px 40px -24px var(--glass-cast);
  }
}

/* ── 3. the language menu ────────────────────────────────────────────────── */
.lang-menu {
  background: var(--glass-strong);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border-color: var(--glass-edge);
  box-shadow: inset 0 1px 0 var(--glass-top), 0 18px 40px -22px var(--glass-cast);
}

/* ── 4. the catalogue rail and the filter panel ──────────────────────────── */
/* Only while they are actually sticky. Below the breakpoint they go static and
   scroll with the page, and a pane that moves with what is behind it has nothing
   to refract — so it goes back to being a solid card. */
@media (min-width: 901px) {
  .filters {
    background: var(--glass);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border-color: var(--glass-edge);
    box-shadow: inset 0 1px 0 var(--glass-top), 0 10px 30px -26px var(--glass-cast);
  }
}

@media (min-width: 60rem) {
  .catalogue-rail {
    background: var(--glass);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border-color: var(--glass-edge);
    box-shadow: inset 0 1px 0 var(--glass-top), 0 10px 30px -26px var(--glass-cast);
  }
}

/* ── 5. the sticky mobile CTA ────────────────────────────────────────────── */
.sticky-cta {
  background: var(--glass-strong);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border-block-start: 1px solid var(--glass-edge);
  box-shadow: 0 -10px 30px -22px var(--glass-cast);
}

/* ── when the browser cannot do it ───────────────────────────────────────── */
/* Without the blur, a 72%-opaque bar over scrolling text is unreadable. Anything
   that cannot composite one falls back to the solid surface it had before. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .header, .nav, .lang-menu, .filters, .catalogue-rail, .sticky-cta {
    background: var(--surface);
  }
}

/* Reduced transparency is an accessibility setting, not a preference: some people
   cannot read text over a moving background at all. */
@media (prefers-reduced-transparency: reduce) {
  .header, .nav, .lang-menu, .filters, .catalogue-rail, .sticky-cta {
    background: var(--surface);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

/* ==========================================================================
   MOBILE

   Measured rather than guessed. Every page on the site was scrolling sideways by
   16px on a 375px screen, and bisecting the DOM — hide each element, see whether
   the document narrows — put it in one place: the header row. A logo plus five
   44px controls plus a call-to-action is 391px of content, and no amount of
   `overflow-x: hidden` fixes a header that does not fit; it only hides the fact
   that it does not.

   So the language and theme controls moved into the nav, which is already a
   drawer below 1080px. What is left in the header on a phone is identity, the
   basket, the account and the way to everything else.
   ========================================================================== */

/* ── the tools, wherever they end up ─────────────────────────────────────── */
.nav-tools {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin-inline-start: var(--s-3);
}

@media (max-width: 1080px) {
  /* In the drawer: a row of their own under the links, separated by a rule so
     they read as settings rather than as two more destinations. */
  .nav-tools {
    margin-inline-start: 0;
    margin-block-start: var(--s-2);
    padding-block-start: var(--s-3);
    border-block-start: 1px solid var(--border);
    gap: var(--s-3);
  }

  /* The globe collapses to an icon in the header, but there is room for the
     language's own name once it is in the drawer — and a name is a better target
     than a 16px globe. */
  .nav-tools .lang-current .lang-name,
  .nav-tools .lang-current .lang-caret { display: inline-flex; }

  /* Opening downward from a control near the bottom of a drawer puts the menu
     off the screen. In the drawer it is an ordinary block that pushes the rest
     down, which is what a phone wants anyway. */
  .nav-tools .lang { inline-size: 100%; }
  .nav-tools .lang-current { inline-size: 100%; }
  .nav-tools .lang-menu {
    position: static;
    inline-size: 100%;
    margin-block-start: var(--s-2);
    box-shadow: none;
    animation: none;
  }
}

/* ── the header row on a phone ───────────────────────────────────────────── */
@media (max-width: 560px) {
  /* 36px of logo is legible and 24px of it is not, but the wordmark is wide —
     capping the width lets it scale down rather than pushing the controls off. */
  .brand img { block-size: 30px; max-inline-size: 40vw; object-fit: contain; }

  .header-inner { gap: var(--s-2); }
  .header-actions { gap: 2px; }

  /* Still 44px of touch target; the padding comes off the icon, not the target. */
  .icon-btn { inline-size: 44px; block-size: 44px; }
}

/* ── touch targets ───────────────────────────────────────────────────────── */
/* Anything you tap gets 44px of height. Below that, a miss is likelier than a
   hit on a phone held in one hand, and every miss on a filter or a quantity
   control is a page reload the visitor did not ask for. */
@media (pointer: coarse) {
  .btn, .chip, .tab, .filters label, .lang-item, .nav a {
    min-block-size: 44px;
  }

  .filters label, .nav a { display: flex; align-items: center; }

  input[type="checkbox"], input[type="radio"] {
    inline-size: 20px;
    block-size: 20px;
  }
}

/* ── forms ───────────────────────────────────────────────────────────────── */
/* iOS zooms the whole page when a field with text under 16px is focused, and it
   does not zoom back out. Every input on the site is at least 16px on a phone. */
@media (max-width: 640px) {
  input, select, textarea { font-size: 16px; }

  /* A two-column form at 375px is two columns of nothing. */
  .form-grid, .grid.g-2, .grid.g-3, .grid.g-4 { grid-template-columns: minmax(0, 1fr); }
}

/* ── tables ──────────────────────────────────────────────────────────────── */
/* A specification table cannot be made narrow — the numbers are the content. It
   scrolls inside its own frame instead, so the page itself never does. */
@media (max-width: 760px) {
  .table-wrap, .tbl-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
  }
  .table-wrap table, .tbl-wrap table { min-inline-size: 34rem; }
}

/* ── no blanket clip on the root ─────────────────────────────────────────── */
/* `html { overflow-x: clip }` was here as a safety net and had to come out. On a
   right-to-left page it left the document parked at a scroll offset it would not
   come back from: the content sat 253px off to one side, clipped, with no way to
   reach it. A safety net that hides the page is worse than the overflow it was
   catching — and it also hides the next real overflow from ever being noticed.

   Overflow gets found and fixed at its source instead. */

/* Long Latin runs inside Persian: part numbers, URLs, email addresses. */
.lat, .num { overflow-wrap: anywhere; }

/* ==========================================================================
   THE HEADER IN THREE LANGUAGES

   The nav was built and measured in Persian, where every label is one short
   word. In English "Product finder" and "About us" are two, and flexbox did what
   it is supposed to: it shrank the items until they wrapped. Two links went to
   two lines each, the header grew from 72px to 96px, and everything in it stopped
   lining up.

   A navigation label is not prose — it never wants to wrap. Saying so is the fix;
   the widths that follow are about making sure the row that cannot wrap still fits.
   ========================================================================== */
.nav a { white-space: nowrap; }

/* Below this the labels no longer fit on one line, so the drawer takes over.
   1080 was measured against Persian; English needed about 140px more for the same
   eight destinations, so it became 1220.

   One breakpoint for every language was the original argument — it cannot drift out
   of step — and four more languages made it wrong. Measured against the same eight
   destinations, taking Persian as 100: Arabic 96, English 106, Russian and Turkish
   110, Azerbaijani 116, Armenian 127. At 1280px the Russian header ran 413px past the
   right edge of the window and took the cart, the account and the call-to-action with
   it, off screen and unreachable.

   The labels were shortened first, because a nav item that reads "Получить
   консультацию" is a sentence where every other language has a word, and that is
   worth fixing whatever the breakpoint is. What remains is genuine: Armenian and
   Azerbaijani are simply longer languages, and the rule below gives them the drawer
   sooner rather than letting the header break. */
@media (max-width: 1220px) {
  .nav-toggle { display: grid; }

  .nav {
    position: fixed;
    inset: var(--header-h) 0 auto 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: var(--s-3);
    margin: 0;
    display: none;
    background: var(--glass-nav);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border-block-end: 1px solid var(--glass-edge);
    box-shadow: inset 0 1px 0 var(--glass-top), 0 18px 40px -24px var(--glass-cast);
  }

  .nav.open { display: flex; }

  .nav-tools {
    margin-inline-start: 0;
    margin-block-start: var(--s-2);
    padding-block-start: var(--s-3);
    border-block-start: 1px solid var(--border);
    gap: var(--s-3);
  }
  .nav-tools .lang,
  .nav-tools .lang-current { inline-size: 100%; }
  .nav-tools .lang-current .lang-name,
  .nav-tools .lang-current .lang-caret { display: inline-flex; }
  .nav-tools .lang-menu {
    position: static;
    inline-size: 100%;
    margin-block-start: var(--s-2);
    box-shadow: none;
    animation: none;
  }
}

/* ==========================================================================
   THE MOBILE DOCK

   The shape the client sent as a reference: a rounded bar floating just above the
   bottom edge, five destinations, icon over label.

   It is not decoration. The top of a phone screen is the part a thumb cannot
   comfortably reach, and until now the only way into this catalogue from a phone
   was a hamburger in exactly that corner — so every journey started with a stretch
   and a second tap. The five things a visitor arrives wanting are now one thumb
   movement away, and the drawer keeps everything else.

   Floating rather than edge-to-edge on purpose. A bar welded to the bottom of the
   screen reads as part of the browser; one with air around it reads as part of the
   page, and the gap is where the page shows through the glass — which is what makes
   it look like glass rather than like a grey strip.
   ========================================================================== */

.dock {
  display: none;
  position: fixed;
  inset-inline: max(12px, env(safe-area-inset-left)) max(12px, env(safe-area-inset-right));
  inset-block-end: calc(12px + env(safe-area-inset-bottom));
  z-index: 95;

  grid-template-columns: repeat(5, 1fr);
  gap: 2px;
  padding: 6px;

  border-radius: 22px;
  background: var(--glass-nav);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-edge);
  box-shadow:
    inset 0 1px 0 var(--glass-top),
    0 12px 32px -10px var(--glass-cast),
    0 2px 8px -4px var(--glass-cast);
}

@media (max-width: 1220px) {
  .dock { display: grid; }

  /* The page has to end above the dock, or the last line of the footer sits under
     it. Measured rather than guessed: 64px of dock plus the floating gap. */
  body { padding-block-end: calc(84px + env(safe-area-inset-bottom)); }
}

/* ── an item ─────────────────────────────────────────────────────────────── */
.dock-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;

  /* 52px of height inside a 64px bar: the whole cell is the target, which is what
     makes a five-across bar usable one-handed rather than a row of small icons. */
  min-block-size: 52px;
  padding: 6px 2px;

  border: 0;
  border-radius: 16px;
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: 10.5px;
  font-weight: 500;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;

  transition:
    background-color var(--dur-micro) var(--ease),
    color var(--dur-micro) var(--ease),
    transform var(--dur-micro) var(--ease);
}

.dock-item svg {
  inline-size: 22px;
  block-size: 22px;
  flex: none;
}

.dock-item > span:last-child {
  /* Five Persian labels across a 375px screen. Any that will not fit is trimmed
     rather than allowed to wrap and shove the icons out of line. */
  inline-size: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dock-item:hover { color: var(--text); text-decoration: none; }

/* Pressed, not hovered. A phone has no hover, and the only feedback that means
   anything on a touch screen is the one that happens under the finger. */
.dock-item:active { transform: scale(.94); background: var(--surface-inset); }

.dock-item:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* ── where you are ───────────────────────────────────────────────────────── */
/* The filled pill from the reference. Colour alone would not carry it: on the
   glass, at 10px, a red label beside four grey ones is a small difference. The
   filled shape is legible at a glance and at arm's length. */
.dock-item.is-on {
  background: var(--accent);
  color: #fff;
}

/* The menu cell while its sheet is open: it is the button that closes it again. */
.dock-item[aria-expanded="true"] {
  background: var(--surface-inset);
  color: var(--text);
}

/* ── the basket count ────────────────────────────────────────────────────── */
.dock-icon { position: relative; display: grid; place-items: center; }

.dock-count {
  position: absolute;
  inset-block-start: -4px;
  inset-inline-end: -8px;
  min-inline-size: 16px;
  padding-inline: 4px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
  box-shadow: 0 0 0 2px var(--glass-nav);
}

/* On the active basket item the badge would be red on red. */
.dock-item.is-on .dock-count { background: #fff; color: var(--accent); }

/* ── giving way to the product page's own bar ────────────────────────────── */
/* On a product page the bottom belongs to "add to basket". Two stacked bars is
   worse than either, and when someone is looking at a product the thing they came
   to do beats the way back to the catalogue. */
body.has-sticky-cta .dock { display: none; }

/* ── the top of the page, with the dock carrying the load ────────────────── */
@media (max-width: 1220px) {
  /* The basket and the account moved into the dock, so the header keeps the
     identity, the way into the drawer, and nothing else. It was five controls and
     a call-to-action in 375px; it is two now. */
  .header-actions .icon-btn[href*="cart"],
  .header-actions .icon-btn[href*="account"] { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .dock-item { transition: none; }
  .dock-item:active { transform: none; }
}

/* ═══════════════════════════════════════════════════ THE FULL-BLEED HERO
   Five client banners under a header that starts transparent.

   The artwork is the layout. Each render puts its subject on the left and leaves the
   right-hand third near-white with a soft shadow across it — a space designed to be
   written on. So there is no scrim on desktop: covering that third with a gradient
   would dull the one part of the image that was made for the words.
   ------------------------------------------------------------------------- */

.hero-full {
  position: relative;
  /* Starts under the header, not beneath it. It used to reach up behind a transparent
     header, which read as one composition on the equipment renders — and put the
     header across the managing directors' heads on the photograph. */
  margin-block-start: 0;
  /* The renders are 7680x2016 masters now, so height is no longer rationed by what
     the file can stand being stretched to. It follows the artwork's own strip
     proportion instead: enough for the copy, and not so tall that a 3.8:1 banner has
     to be cropped to a square. */
  min-block-size: clamp(460px, 30vw, 580px);
  display: grid;
  isolation: isolate;
  /* The renders' own unlit navy. The artwork stops at its native width on a wide
     screen and this carries on from it, so there is no edge to see. */
  background: #030B17;
}

.hero-pane {
  grid-area: 1 / 1;
  position: relative;
  display: grid;
  align-content: center;
  /* Room for the arrows and the trust bar below; the header is no longer on top. */
  padding-block: var(--s-6) 116px;
  opacity: 0;
  visibility: hidden;
  transition: opacity .7s ease;
}

.hero-pane.is-active { opacity: 1; visibility: visible; }

.hero-art {
  position: absolute;
  inset-block: 0;
  /* Physical right, not inset-inline-end: the equipment is on the right of the
     render in every language, the same reason the copy uses margin-right. */
  right: 0;
  left: auto;
  z-index: -1;
  display: block;
  inline-size: 100%;
}

/* The artwork's left half is already dark and empty, which is where the words go.
   This only deepens it, and only at the very edge, for the narrow desktop frames
   where the crop brings the cabinet closer to the text than the render intended.
   Physical `to right`: the copy is on the left in every language. */
.hero-pane::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(to right,
    color-mix(in srgb, #05090F 72%, transparent),
    color-mix(in srgb, #05090F 30%, transparent) 46%,
    transparent 68%);
}

/* The managing directors' slide is a photograph of a bright room, composed onto a navy
   field that already carries the copy — the people are what the slide is for. So its
   scrim stays over that field and fades out before the photograph begins: the old
   one ran across their faces and left the slide looking dim and muddy. */
.hero-pane--photo::before {
  background: linear-gradient(to right,
    color-mix(in srgb, #05090F 45%, transparent),
    transparent 40%);
}

/* The first slide keeps the supplied logos intact. Its heading and action sit in
   the clear space below them, then move below the picture like the other slides. */
.hero-pane--art-only { background: #f4f7fa; }
.hero-pane--art-only::before { content: none; }
.hero-pane--art-only .hero-art { z-index: 0; }
.hero-pane--art-only .hero-art img { object-fit: cover; object-position: center; }
.hero-pane--art-only .h-display {
  margin: 0;
  color: #071e4b;
  font-size: clamp(23px, 2.1vw, 32px);
  line-height: 1.4;
  text-align: center;
  text-shadow: 0 1px 12px rgb(255 255 255 / .75);
}
.hero-art-only-action {
  position: absolute;
  z-index: 2;
  left: 50%;
  inset-block-end: 122px;
  transform: translateX(-50%);
  inline-size: min(88vw, 700px);
  display: grid;
  justify-items: center;
  gap: 12px;
}
.hero-art-only-action .btn { box-shadow: 0 12px 30px rgb(11 42 75 / .22); white-space: nowrap; }
.hero-art-mobile-copy { display: none; }

/* On a laptop-sized frame the crop brings the directors under the headline. Easing
   the picture a few pixels to the right keeps the first face clear of the words; the
   margin it spends is the one beside the Arkel mark, which stays whole. */
@media (min-width: 1001px) {
  .hero-pane--photo .hero-art img { object-position: 96% center; }
}

/* Up to 1220px the headline still reaches the left-hand flag, whose white lettering
   fights it. So the flag darkens behind the words there — never the people, who start
   further right than this gradient goes. */
@media (min-width: 1001px) and (max-width: 1220px) {
  .hero-pane--photo::before {
    background: linear-gradient(to right,
      color-mix(in srgb, #05090F 70%, transparent),
      color-mix(in srgb, #05090F 45%, transparent) 30%,
      transparent 44%);
  }
}

/*
   Legibility for the navigation, without touching the picture.

   This was a white wash over the top of the banner, and at the strength it needed to
   guarantee contrast it read as an opaque header with the hero starting underneath —
   exactly the thing the transparent header was for. Washing the artwork to make text
   on top of it readable is solving the wrong half.

   So the artwork is left alone and the text carries its own contrast: a shadow tight
   enough to be invisible where the banner is bright and enough to hold an edge where
   it is not. It is removed the moment the header solidifies, because then it is text
   on a surface.

   ── Why these do not follow the theme ─────────────────────────────────
   Every banner is an unlit machine room now, so what is behind this header is dark
   whichever theme the visitor has chosen. In light mode the header's own colours are
   dark text on a light surface — which over these banners would be a dark navigation
   on a dark photograph. So while it sits over the artwork it is white, and it goes
   back to the theme's colours the moment it solidifies.
*/
/* Desktop only. Under 1221px the header is the solid bar the menu screen hangs from,
   and a header that is transparent and white-lettered over the banner is a bar that
   cannot also be the thing the menu opens beneath. */
@media (min-width: 1221px) {
.has-full-hero .header:not(.is-stuck) .nav a,
.has-full-hero .header:not(.is-stuck) .nav-tools,
.has-full-hero .header:not(.is-stuck) .header-actions .icon-btn,
.has-full-hero .header:not(.is-stuck) .brand {
  color: #fff;
  text-shadow: 0 1px 3px rgba(5, 9, 15, .55);
}

/* The wordmark has a white cut of its own; the dark-on-transparent one disappears. */
.has-full-hero .header:not(.is-stuck) .brand .logo-light { display: none; }
.has-full-hero .header:not(.is-stuck) .brand .logo-dark  { display: block; }

.has-full-hero .header:not(.is-stuck) .lang-current,
.has-full-hero .header:not(.is-stuck) .theme-track {
  color: #fff;
  border-color: rgba(255, 255, 255, .28);
  background: rgba(10, 18, 30, .45);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

.has-full-hero .header:not(.is-stuck) .theme-thumb {
  background: rgba(255, 255, 255, .22);
  border-color: rgba(255, 255, 255, .5);
}

.has-full-hero .header:not(.is-stuck) .theme-sun,
.has-full-hero .header:not(.is-stuck) .theme-moon { color: rgba(255, 255, 255, .55); }
.has-full-hero .header:not(.is-stuck) .theme-moon { color: #fff; }
:root[data-theme="light"] .has-full-hero .header:not(.is-stuck) .theme-moon { color: rgba(255, 255, 255, .55); }
:root[data-theme="light"] .has-full-hero .header:not(.is-stuck) .theme-sun { color: #fff; }
}

.hero-art img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  /* The equipment lives on the right of every one of these banners, and the left is
     the dark space the copy sits in. So when the frame is narrower than the artwork,
     it is that empty left edge that gets cropped — never the products. */
  object-position: right center;
}

.hero-inner { inline-size: 100%; }

/*
   Physical `margin-right`, deliberately, not `margin-inline-end`.

   The empty half is on the left of the artwork in every language, because it is part
   of the image. A logical property would flip this in Persian and Arabic and drop the
   headline straight onto the control panel.
*/
.hero-copy {
  margin-right: auto;
  margin-left: 0;
  max-inline-size: 44ch;
  display: grid;
  gap: var(--s-4);
  justify-items: start;
}

.hero-copy .h-display { margin: 0; }
.hero-copy .lead { margin: 0; }

/* Light in both themes, for the same reason the header is: every banner is an
   unlit machine room, so the page's own text colours — dark ink in light mode —
   would be a dark headline on a dark photograph. */
.hero-copy { color: #fff; }
.hero-copy .h-display {
  color: #fff;
  font-size: clamp(26px, 3.2vw, 38px);
  text-shadow: 0 2px 12px rgba(5, 9, 15, .45);
}
.hero-copy .eyebrow { color: #F0857D; text-shadow: 0 1px 6px rgba(5, 9, 15, .6); }
.hero-copy .lead { color: rgba(255, 255, 255, .86); text-shadow: 0 1px 6px rgba(5, 9, 15, .4); }

/* Even lines instead of one word left hanging on the last: the column is narrow
   enough that a two-line lead otherwise ends on a single «ندارند.» */
.hero-copy .h-display { text-wrap: balance; }
.hero-copy .lead { text-wrap: pretty; }

/* ── the controls ────────────────────────────────────────────────────────
   Two arrows and a count, sitting under the copy on the physical left. They are
   white on glass rather than themed: what is behind them is a photograph of an
   unlit machine room, in every theme. */
.hero-rail {
  position: absolute;
  inset-block-end: 64px;
  inset-inline: 0;
  z-index: 2;
  display: flex;
}

/* An auto margin on the physical right keeps the controls under the copy — which
   is on the left — whichever direction the page reads. */
.hero-nav {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin-right: auto;
}

.hero-arrow {
  inline-size: 46px;
  block-size: 46px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, .38);
  border-radius: 999px;
  background: rgba(8, 14, 24, .45);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  color: #fff;
  cursor: pointer;
  transition: background-color var(--dur-micro) var(--ease),
              border-color var(--dur-micro) var(--ease),
              transform var(--dur-micro) var(--ease);
}

.hero-arrow:hover {
  background: rgba(8, 14, 24, .72);
  border-color: #fff;
  transform: translateY(-1px);
}

.hero-arrow:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
.hero-arrow svg { inline-size: 20px; block-size: 20px; display: block; }

/* The row already mirrors in Persian and Arabic, so the chevrons mirror with it:
   otherwise "previous" ends up on the right still pointing left. */
[dir="rtl"] .hero-arrow svg { transform: scaleX(-1); }

.hero-count {
  min-inline-size: 52px;
  text-align: center;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  color: rgba(255, 255, 255, .82);
  text-shadow: 0 1px 3px rgba(5, 9, 15, .5);
}

.hero-trust-bar {
  position: absolute;
  inset-block-end: 0;
  inset-inline: 0;
  z-index: 2;
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  border-block-start: 1px solid var(--border);
}

.hero-full .hero-trust {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4) var(--s-6);
  padding-block: 14px;
  font-size: 13px;
  color: var(--ink-2);
}

.hero-full .hero-trust span { display: inline-flex; align-items: center; gap: 6px; }
.hero-full .hero-trust svg { inline-size: 15px; block-size: 15px; color: var(--ok, #1F8A4C); }

/* ── the phone ────────────────────────────────────────────────────────────
   Not the desktop composition squeezed. On a 375px screen the artwork was being
   cropped to a quarter of itself and blown up to fill a 630px-tall frame: the
   equipment fell outside the crop and what was left was an out-of-focus wall.

   So on a phone the banner is a picture — its own 16:9 crop of the equipment, whole,
   at the width of the screen — and the words sit under it on the page's own surface.
   No scrim, nothing cropped, and the text is read against a background rather than a
   photograph.

   Portrait tablets too, up to 1000px. There the desktop crop kept only the right half
   of each banner — all equipment — and laid the copy straight across it; on the
   managing directors' photograph the headline sat on their faces. The picture above
   the words is the layout that works at that width as well. */
@media (max-width: 1000px) {
  .hero-full {
    min-block-size: 0;
    background: var(--surface);
  }

  .hero-pane {
    align-content: start;
    padding-block: 0;
  }

  .hero-art {
    position: static;
    inline-size: 100%;
    aspect-ratio: 16 / 9;
    -webkit-mask-image: none;
    mask-image: none;
  }

  .hero-art img { object-position: center; }

  /* Both scrims go: nothing is written over the picture any more. */
  .hero-pane::before { content: none; }
  .hero-pane::after { content: none; }

  .hero-inner { padding-block: var(--s-5) 0; }

  /* Back to the page's own colours, because this is text on a surface again. */
  .hero-copy {
    margin: 0;
    max-inline-size: none;
    gap: var(--s-3);
    color: var(--text);
  }

  .hero-copy .h-display {
    color: var(--text);
    font-size: clamp(24px, 6.4vw, 30px);
    text-shadow: none;
  }

  .hero-copy .lead { color: var(--text-muted); text-shadow: none; }
  .hero-copy .eyebrow { color: var(--accent-ink); }

  .hero-art-only-action { position: static; transform: none; margin-inline: auto; }
  .hero-pane--art-only .h-display { color: #071e4b; text-shadow: none; }
  .hero-art-only-action .btn { inline-size: min(100%, 280px); justify-content: center; }

  .hero-rail {
    position: static;
    margin-block: var(--s-4) var(--s-1);
  }

  .hero-nav { margin-inline: auto; }

  .hero-arrow {
    color: var(--text);
    border-color: var(--border-strong);
    background: var(--surface-inset);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }

  .hero-count { color: var(--text-muted); text-shadow: none; }

  .hero-trust-bar { position: static; }

  .hero-full .hero-trust { gap: var(--s-2) var(--s-4); font-size: 11.5px; padding-block: 10px; }
}

/* ── the phone: cards ─────────────────────────────────────────────────────
   Up to 720px the banners are a row of cards you swipe along, the next one showing at
   the edge so the row says it moves. Each card is a whole 16:9 picture — nothing is
   cropped — with its words and its one button under it, on the page's own surface.

   The artwork is made for a wide screen: subject on one side, room for words on the
   other. Wrapping that into a portrait frame throws half of it away, which is what the
   full-screen poster would have had to do. A card keeps all of it.

   Above 720px the wrapper below is `display: contents` and the desktop and tablet
   layouts are exactly what they were. */
.hero-track { display: contents; }
.hero-dots { display: none; }

@media (max-width: 720px) {
  .hero-full {
    display: block;
    background: var(--bg);
    padding-block-start: env(safe-area-inset-top);
  }

  .hero-track {
    display: flex;
    gap: var(--s-3);
    padding: var(--s-3) var(--s-4) var(--s-2);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--s-4);
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .hero-track::-webkit-scrollbar { display: none; }

  .hero-pane {
    flex: 0 0 min(86%, 22rem);
    scroll-snap-align: center;
    display: flex;
    flex-direction: column;
    /* Every card is on screen; the cross-fade belongs to the desktop. */
    opacity: 1;
    visibility: visible;
    transition: none;
    border: 1px solid var(--border);
    border-radius: 20px;
    background: var(--surface);
    box-shadow: var(--shadow);
    overflow: hidden;
  }

  /* `z-index: -1` is the desktop's way of putting the picture behind the words, and a
     flex item honours it even when it is not positioned — which sent the picture behind
     the card's own background. */
  .hero-art { flex: none; z-index: auto; }

  .hero-inner,
  .hero-inner.container {
    flex: 1;
    display: flex;
    flex-direction: column;
    padding: var(--s-4);
  }

  .hero-copy { flex: 1; display: flex; flex-direction: column; gap: var(--s-2); }
  .hero-copy .eyebrow { font-size: 12px; }

  .hero-copy .h-display,
  .hero-copy .lead {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .hero-copy .h-display { font-size: 19px; line-height: 1.55; -webkit-line-clamp: 3; }
  .hero-copy .lead { font-size: 13.5px; line-height: 1.85; -webkit-line-clamp: 3; margin-block-end: var(--s-3); }

  .hero-copy .btn { margin-block-start: auto; inline-size: 100%; justify-content: center; min-block-size: 48px; }
  .hero-pane--art-only .hero-inner { min-block-size: 0; }
  .hero-pane--art-only .hero-art-only-action {
    flex: 1;
    inline-size: 100%;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--s-2);
  }
  .hero-pane--art-only .hero-art-mobile-copy { display: block; }
  .hero-pane--art-only .eyebrow { color: var(--accent-ink); font-size: 12px; text-align: start; }
  .hero-pane--art-only .h-display {
    color: var(--text);
    font-size: 19px;
    line-height: 1.55;
    text-align: start;
  }
  .hero-pane--art-only .lead {
    margin: 0 0 var(--s-3);
    color: var(--text-muted);
    font-size: 13.5px;
    line-height: 1.85;
    text-align: start;
  }
  .hero-pane--art-only .hero-art-only-action .btn {
    margin-block-start: auto;
    inline-size: 100%;
    min-block-size: 48px;
    justify-content: center;
  }

  .hero-rail { margin-block: var(--s-1) 0; }
  .hero-nav { display: none; }
  .hero-dots { display: flex; justify-content: center; gap: 16px; inline-size: 100%; padding-block: var(--s-2); }

  .hero-dot {
    position: relative;
    inline-size: 8px; block-size: 8px;
    padding: 0; border: 0; border-radius: 999px;
    background: var(--border-strong);
    cursor: pointer;
    transition: inline-size .25s var(--ease), background-color .25s var(--ease);
  }
  /* A 24px target around an 8px dot, and the gap above is sized so that the targets of two
     neighbours meet and do not overlap (8 + 16 = 24). It was 14 x 44 with a gap of 6, which
     was narrower than a fingertip and, with ten slides, still ran to 140px of dots. */
  .hero-dot::after { content: ""; position: absolute; inset: -8px; }
  .hero-dot[aria-current="true"] { inline-size: 22px; background: var(--accent); }
}

@media (max-width: 720px) and (prefers-reduced-motion: reduce) {
  .hero-dot { transition: none; }
}

/* ═══════════════════════════════════════════ HEADER OVER THE HERO
   Transparent while the banner is behind it, solid once it is not.

   Only on pages that declare a full-bleed hero. Everywhere else the header sits over
   a breadcrumb on white and has always been opaque.
   ------------------------------------------------------------------------- */

@media (min-width: 1221px) {
.has-full-hero .header {
  background: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  border-block-end-color: transparent;
  box-shadow: none;
  transition: background .3s ease, border-color .3s ease, box-shadow .3s ease;
}

/* `is-stuck` is set by the scroll listener rather than by a scroll-driven animation,
   because the pane it has to react to is the hero's height and that is not a
   fixed number. */
.has-full-hero .header.is-stuck {
  background: var(--glass);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border-block-end-color: var(--glass-edge);
  box-shadow: inset 0 1px 0 var(--glass-top), 0 8px 24px -20px var(--glass-cast);
}
}

/* ═══════════════════════════════════════════════════ CATEGORY STRIP
   Circles with a label — the shape every Iranian shopper already knows. Five product
   categories with a photograph of the product, then three icons.
   ------------------------------------------------------------------------- */

.cat-strip-wrap {
  border-block-end: 1px solid var(--border);
  background: var(--surface);
}

.cat-strip {
  display: flex;
  gap: clamp(8px, 3vw, 28px);
  justify-content: center;
  list-style: none;
  margin: 0;
  padding: var(--s-5) 0;
  overflow-x: auto;
  scrollbar-width: none;
  scroll-snap-type: x proximity;
}

.cat-strip::-webkit-scrollbar { display: none; }

.cat-strip > li { flex: 0 0 auto; scroll-snap-align: center; }

.cat-strip a {
  display: grid;
  justify-items: center;
  align-content: start;
  gap: 10px;
  inline-size: 104px;
  text-decoration: none;
  color: var(--ink);
  font-size: 12.5px;
  line-height: 1.4;
  text-align: center;
}

.cat-strip-icon {
  display: grid;
  place-items: center;
  inline-size: 84px;
  block-size: 84px;
  border-radius: 50%;
  background: var(--surface-2, #f1f4f7);
  transition: transform .25s ease, box-shadow .25s ease;
}

.cat-strip img { inline-size: 100%; block-size: 100%; display: block; }

/* A product photograph rather than an icon: on a white disc in both themes, since a
   black ARCUBE on a dark ground would disappear, and contained rather than cropped,
   because a tall cabinet cut to a circle is half a cabinet. */
.cat-strip-icon.is-photo {
  background: #fff;
  box-shadow: inset 0 0 0 1px var(--border);
  padding: 9px;
}
.cat-strip-icon.is-photo img { object-fit: contain; }

.cat-strip a:hover .cat-strip-icon,
.cat-strip a:focus-visible .cat-strip-icon {
  transform: translateY(-3px);
  box-shadow: 0 10px 22px -12px color-mix(in srgb, var(--ink) 55%, transparent);
}

.cat-strip a:focus-visible { outline: 2px solid var(--brand, #D62828); outline-offset: 4px; border-radius: 12px; }

@media (max-width: 720px) {
  .cat-strip { justify-content: flex-start; padding-block: var(--s-4); }
  .cat-strip a { inline-size: 80px; font-size: 11.5px; }
  .cat-strip-icon { inline-size: 62px; block-size: 62px; }
  .cat-strip-icon.is-photo { padding: 6px; }
}

/* ═══════════════════════════════════════════ SECTIONS ON THEIR OWN ARTWORK
   The two mid-page banners. Same composition as the hero — subject left, space
   right — so the card sits in that space rather than over the products.
   ------------------------------------------------------------------------- */

.band-art {
  position: relative;
  isolation: isolate;
  background-image: var(--band-art);
  background-size: cover;
  background-position: left center;
  background-repeat: no-repeat;
}

.band-art > .container { position: relative; }

.band-art > .container > .card,
.band-art > .container > .between {
  margin-left: auto;
  margin-right: 0;
  max-inline-size: 60ch;
}

@media (max-width: 860px) {
  /* Narrow enough that the artwork's empty third has been cropped away, so the card
     needs its own ground rather than the picture's. */
  .band-art::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: color-mix(in srgb, var(--surface) 88%, transparent);
  }

  .band-art > .container > .card,
  .band-art > .container > .between { margin-left: 0; max-inline-size: none; }
}

/* ═══════════════════════════════════════════════════ THE INSTAGRAM BAND
   The supplied image contains the account and QR code. Keep every edge of the
   artwork visible at all widths, with the single action just below the QR. */

.ig-band {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--surface-2, #f4f6f8);
}

.ig-art {
  display: block;
  inline-size: 100%;
  block-size: auto;
}

.ig-cta {
  position: absolute;
  left: 78%;
  top: 71%;
  transform: translateX(-50%);
  z-index: 1;
  min-inline-size: 220px;
  min-block-size: 54px;
  justify-content: center;
  font-size: 16px;
  white-space: nowrap;
}

@media (max-width: 600px) {
  .ig-cta {
    left: min(78%, calc(100% - 88px));
    top: 71%;
    inline-size: min(170px, 46vw);
    min-inline-size: 0;
    min-block-size: 44px;
    padding: 7px 9px;
    font-size: clamp(11px, 3.2vw, 13px);
    line-height: 1.25;
    text-align: center;
    white-space: normal;
  }
}

/* ── flags in the language switcher ──────────────────────────────────────
   Drawn as SVG rather than emoji: Windows renders emoji flags as the two-letter
   country code, and a large share of this site's visitors are on Windows.

   `.lang-current svg` above sizes every svg in the trigger to 16px square, which
   would squash a 3:2 flag into a square. These two rules win on specificity. */
.lang .flag {
  inline-size: 21px !important;
  block-size: 14px !important;
  flex: none;
  border-radius: 2px;
  display: block;
}
.lang-menu .flag { inline-size: 24px !important; block-size: 16px !important; }

/* The two-letter code is gone from the menu — the flag is the thing the eye scans
   now, and a code beside a flag beside a name is three labels for one choice. */
.lang-item .lang-label { flex: 1; }

/* Seven languages is a taller menu than three. Capped and scrollable rather than
   running off the bottom of a phone. */
.lang-menu {
  max-block-size: min(70vh, 420px);
  overflow-y: auto;
}

/* ── scripts the bundled fonts do not carry ──────────────────────────────
   IRANSansX has Arabic and Persian; Roboto is subset to Latin-1 by its own
   unicode-range. Neither has Cyrillic, Armenian, or — and this already affected
   Turkish before any of these languages were added — ğ and ş.

   Left to chance, a word gets the bundled font for the letters it has and a system
   font for the rest, which is visible as a jump in weight mid-word. Naming the
   system stack first for those languages keeps each word in one typeface. */
:root:lang(ru), [lang="ru"],
:root:lang(hy), [lang="hy"],
:root:lang(az), [lang="az"],
:root:lang(tr), [lang="tr"] {
  font-family: system-ui, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* Arabic keeps IRANSansX, which draws the script properly, and is the one new
   language the bundled font already covers. */
:root:lang(ar), [lang="ar"] {
  font-family: 'IRANSansX', system-ui, 'Segoe UI', Tahoma, sans-serif;
}

/* The label takes its own direction, so Arabic reads correctly, but the row does not
   — otherwise the flag jumps to the other edge and the column goes ragged. */
.lang-item .lang-label { text-align: start; }


/* ── the header fits, or it collapses — measured, never guessed ──────────
   Two hand-picked breakpoints failed here before this. The reason both failed is the
   same: how much room a nav needs depends on the language, and there are seven of
   them. Measured at a 1320px container, the header needs:

       Arabic 1239 · Persian 1289 · English 1373 · Russian 1422
       Turkish 1426 · Azerbaijani 1500 · Armenian 1633

   Five of the seven do not fit, English included. A media query cannot know any of
   this — it sees a viewport width and nothing about the words in it — so the decision
   is made in JavaScript by measuring the nav against the space it has, and this class
   is what that measurement sets. See `the header's own width` in app.js.

   The 1220px query below stays as the no-JavaScript floor: with no script the site
   behaves as it did before, drawer under 1220 and a row above it. */
.header-inner.is-tight .nav-toggle { display: grid; }

.header-inner.is-tight .nav {
  position: fixed;
  inset: var(--header-h) 0 auto 0;
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  padding: var(--s-3);
  margin: 0;
  display: none;
  background: var(--glass-nav);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border-block-end: 1px solid var(--glass-edge);
  box-shadow: inset 0 1px 0 var(--glass-top), 0 18px 40px -24px var(--glass-cast);
}

.header-inner.is-tight .nav.open { display: flex; }

/* The header is the same width as the page, and stays that way.

   It was widened to 1600px to buy room for the longer languages. That is the wrong
   lever: on a wide screen Persian then had 1600px of header for 1229px of content, so
   the nav drifted away from the logo and sat in the middle of nothing, and the header
   no longer lined up with the column beneath it.

   The right lever is the type. A nav that is 6% too wide should be set 6% smaller —
   the spacing stays exactly as designed and only the label shrinks, which nobody
   notices. `--nav-scale` is that factor, worked out per language in app.js. */
.header-inner { max-inline-size: var(--container); }

/* The scale. 1 for every language that already fits, so Persian and Arabic render at
   exactly the size and spacing they were drawn at and nothing about them changes. */
.header-inner { --nav-scale: 1; }

/*
 * Each value restates the one it scales.
 *
 * A first version wrote `gap: calc(var(--s-5) * var(--nav-scale))` — and the nav's gap
 * is `--s-1`, which is 4px, not 24. That one line put 20px between every pair of links,
 * nine times over: 180px added to every nav in every language, which is most of the
 * reason nothing fitted and all of the reason the spacing looked wrong.
 *
 * So the base is written out beside the scale, and at scale 1 each of these is exactly
 * what components.css already said.
 */
.header-inner .nav { gap: calc(var(--s-1) * var(--nav-scale)); }

.header-inner .nav > a {
  font-size: calc(15px * var(--nav-scale));
  padding-inline: calc(var(--s-3) * var(--nav-scale));
}

/* The call to action is the other wide thing in the row, and it is a button, so the
   padding scales with the label rather than the label rattling in a fixed box. */
.header-inner .header-actions .btn {
  font-size: calc(0.875rem * var(--nav-scale));
  padding-inline: calc(var(--s-4) * var(--nav-scale));
}

/* The gap between the three blocks is NOT scaled. It is the spacing between the logo,
   the nav and the buttons — the thing a reader sees as the header's rhythm — and
   shrinking it to win 20px would be trading the look for a rounding error. */

/* The language's name, dropped everywhere rather than below a breakpoint.

   It is the widest part of that control at about 60px, and the flag beside it already
   says which language this is — which is the entire reason the flag was drawn. Kept
   for screen readers through the label on the summary. */
.lang-current .lang-name { display: none; }

/* The nav must NOT be allowed to shrink. Two attempts at a "guard" here made things
   worse, and both are worth writing down because they failed in opposite directions.

   The first was `.header-inner { max-inline-size: 100% }`. `max-inline-size` IS
   `max-width`, and `.header-inner` is also `.container`, which caps itself at 1200px
   and centres on that cap — so a later rule at equal specificity simply replaced it.
   Every desktop header went full-bleed: logo hard against one edge, call to action
   against the other.

   The second was `min-inline-size: 0` on the nav. A flex item defaults to
   `min-width: auto`, which is what stops it shrinking below its own content; setting
   it to zero let the nav shrink while its links, which are `white-space: nowrap`,
   could not — so they spilled out of the box and sat on top of the brand and the
   actions. The header looked scrambled in every language whose nav is longer than
   English, which is all five of the ones added after launch.

   And it defeated the check that was supposed to catch it: the page stopped
   overflowing *because* the nav was overlapping instead of pushing. The test passed
   for the same reason the page was broken.

   So: nothing here. The nav keeps its default `min-width: auto` and is never squeezed.
   What stops the header overflowing is the breakpoints above, sized from measurement
   per language, and HeaderFitTest asserts that no two header children overlap rather
   than that the document does not scroll sideways. */

/* ==========================================================================
   THE MENU, AS A SCREEN
   ==========================================================================
   Phones and tablets: tapping the menu opens a full-height screen under the bar,
   the way the Avanama site does it — big rows that slide in one after another, the
   one action the page wants below them, the settings last, and the page behind it
   held still.

   What this replaces was a sheet that rose from the dock. It was capped at 70% of the
   screen height and sat above a bar that was itself in the way, so on a short phone
   half the destinations were off the edge of the picture. A screen cannot be off the
   edge: it is as tall as the display and scrolls inside itself if the display is short.

   The top bar is back on phones, too. It had been removed on the argument that the
   dock covers navigation — true, and the logo went with it, which is how a brand ends
   up "seen too little". The bar carries the logo and the one button; everything else
   stays in the dock and the menu.

   ── Why the header carries no blur here ────────────────────────────────
   A `backdrop-filter` on an ancestor makes every `position: fixed` descendant resolve
   against that ancestor instead of the viewport. The menu is a descendant of the
   header, so with the glass on it the screen anchored itself to a 64px-tall box and
   was "open", painted, and almost entirely off the page. The bar is a near-opaque
   panel at these sizes instead — and the blur lives on the menu itself, which is not
   inside anything that has one.

   The transparent-over-the-banner state of the header is desktop-only for the same
   reason, and is declared above inside `min-width: 1221px`.
   ========================================================================== */

/* The call to action and the closing logo exist only where the menu is a screen. */
.nav .nav-cta,
.nav .nav-brand { display: none; }

/* Three bars that become a cross. The button says what it will do next. */
.menu-ico path {
  transform-box: fill-box;
  transform-origin: center;
  transition: transform .28s var(--ease), opacity .18s var(--ease);
}
[aria-expanded="true"] > .menu-ico .bar-1 { transform: translateY(5px) rotate(45deg); }
[aria-expanded="true"] > .menu-ico .bar-2 { opacity: 0; transform: scaleX(.2); }
[aria-expanded="true"] > .menu-ico .bar-3 { transform: translateY(-5px) rotate(-45deg); }

.nav-toggle[aria-expanded="true"] { background: var(--surface-inset); color: var(--text); }

@keyframes nav-row-in {
  from { opacity: 0; transform: translateX(var(--nav-in, 18px)); }
  to   { opacity: 1; transform: none; }
}

@media (max-width: 1220px) {
  /* ── the bar: solid, no blur (see above) ─────────────────────────────── */
  .header,
  .header.is-stuck {
    background: var(--glass-nav);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }

  /* ── the screen ──────────────────────────────────────────────────────── */
  .header .header-inner .nav {
    --nav-in: 18px;

    position: fixed;
    inset: var(--header-h) 0 0 0;
    z-index: 1;

    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    margin: 0;
    padding-block: var(--s-2) calc(var(--s-7) + env(safe-area-inset-bottom));
    padding-inline: max(20px, env(safe-area-inset-left), env(safe-area-inset-right));

    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;

    border: 0;
    border-radius: 0;
    box-shadow: none;
    background: color-mix(in srgb, var(--bg) 94%, transparent);
    -webkit-backdrop-filter: blur(22px) saturate(160%);
    backdrop-filter: blur(22px) saturate(160%);

    /* Closed is `visibility: hidden`, which takes the links out of the tab order and
       the accessibility tree exactly as `display: none` did, and lets it fade. The
       delay on visibility is what keeps it painted while the fade runs. */
    visibility: hidden;
    opacity: 0;
    transform: translateY(-10px);
    pointer-events: none;
    transition: opacity .24s var(--ease), transform .24s var(--ease), visibility 0s linear .24s;
  }

  [dir="ltr"] .header .header-inner .nav { --nav-in: -18px; }

  .header .header-inner .nav.open {
    visibility: visible;
    opacity: 1;
    transform: none;
    pointer-events: auto;
    transition-delay: 0s;
  }

  /* Everything in it arrives in turn: the same 45ms step Avanama uses, which is slow
     enough to read as a sequence and quick enough that the last row is not waited for. */
  .nav.open > * {
    animation: nav-row-in .42s cubic-bezier(.28, .11, .32, 1) both;
    animation-delay: calc(70ms + var(--i, 0) * 45ms);
  }
  .nav > :nth-child(1) { --i: 0; }
  .nav > :nth-child(2) { --i: 1; }
  .nav > :nth-child(3) { --i: 2; }
  .nav > :nth-child(4) { --i: 3; }
  .nav > :nth-child(5) { --i: 4; }
  .nav > :nth-child(6) { --i: 5; }
  .nav > :nth-child(7) { --i: 6; }

  /* ── a row ───────────────────────────────────────────────────────────── */
  .header .header-inner .nav > a {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-3);
    min-block-size: 56px;
    padding: 0 var(--s-3);
    border-radius: 0;
    border-block-end: 1px solid var(--border);
    border-inline-start: 3px solid transparent;
    background: none;
    color: var(--text-muted);
    font-size: clamp(1.1875rem, 5vw, 1.375rem);
    font-weight: 600;
    white-space: normal;
  }

  .header .header-inner .nav > a:hover,
  .header .header-inner .nav > a:active {
    background: var(--surface-inset);
    color: var(--text);
  }

  /* Where you are: the ink, the weight, a bar on the leading edge and a tint. Colour
     alone would not carry it for everyone who reads this menu. */
  .header .header-inner .nav > a[aria-current="page"] {
    background: var(--accent-soft);
    border-inline-start-color: var(--accent);
    color: var(--text);
    font-weight: 800;
  }

  /* The underline the desktop nav draws is for a row of words, not for this. */
  .nav > a::after { display: none; }

  /* A chevron at the far end, drawn rather than typed, so it follows the language's
     direction with one flip instead of a different glyph per locale. */
  .nav > a::before {
    content: "";
    order: 2;
    flex: none;
    inline-size: 22px;
    block-size: 22px;
    background: currentColor;
    opacity: .5;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 6 6 6-6 6'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 6 6 6-6 6'/%3E%3C/svg%3E") center / contain no-repeat;
  }
  [dir="rtl"] .nav > a::before { transform: scaleX(-1); }

  /* ── below the rows: the action, the settings, the signature ─────────── */
  .nav .nav-cta {
    --i: 7;
    display: block;
    order: 1;
    margin-block-start: var(--s-5);
  }
  .nav .nav-cta .btn {
    min-block-size: 56px;
    border-radius: 16px;
    font-size: 1.0625rem;
  }

  .header .header-inner .nav .nav-tools {
    --i: 8;
    order: 2;
    display: grid;
    gap: var(--s-3);
    margin: var(--s-4) 0 0;
    padding: 0;
    border: 0;
  }
  .nav-tools .lang-current,
  .nav-tools .theme-switch { min-block-size: 52px; }

  .nav .nav-brand {
    --i: 9;
    order: 3;
    display: flex;
    justify-content: center;
    margin-block-start: var(--s-6);
    pointer-events: none;
  }
  .nav .nav-brand img { block-size: 64px; inline-size: auto; opacity: .95; }

  /* ── the page behind it, held still ──────────────────────────────────── */
  html.nav-open,
  html.nav-open body { overflow: hidden; }
  html.nav-open { scrollbar-gutter: stable; }

  /* The dock would show through the screen, and it has nothing to say while the
     screen is open — the cross in the bar is the way back. */
  .dock { transition: opacity .2s var(--ease), transform .24s var(--ease); }
  html.nav-open .dock { opacity: 0; transform: translateY(24px); pointer-events: none; }
}

/* ── the bar on a phone ──────────────────────────────────────────────────── */
@media (max-width: 720px) {
  :root { --header-h: 64px; }

  .header-inner { gap: var(--s-3); }

  /* The logo at a size that can be read: it is a two-line lockup, and at the 30px it
     had on a phone the Latin line under the Persian was a smudge. */
  .brand img { block-size: 46px; max-inline-size: 60vw; object-fit: contain; }
}

@media (prefers-reduced-motion: reduce) {
  .menu-ico path { transition: none; }

  .header .header-inner .nav { transform: none; transition: none; }
  .nav.open > * { animation: none; }
  .dock { transition: none; }
}

/* ── thumb-sized targets on a phone ───────────────────────────────────────── 
   The breadcrumb's links were 24px tall, the social icons 38px and the phone numbers
   25px: each a target a thumb has to aim at. The look is unchanged; the tappable area
   grows to 44px. */
@media (max-width: 720px) {
  .crumbs a { display: inline-flex; align-items: center; min-block-size: 44px; }
  .socials a { inline-size: 44px; block-size: 44px; }
  .contact-phones a { display: inline-flex; align-items: center; min-block-size: 44px; }
  .footer ul a { display: inline-flex; align-items: center; min-block-size: 38px; }
}

/* ==========================================================================
   THE INTRO — the logo, once per tab
   ==========================================================================
   The client's point was that the brand is seen too little. This is the first thing a
   visitor sees on the first page of a visit: the wordmark drawn in, a line filling
   under it, and the screen rolling up off the page.

   ── What it deliberately is not ─────────────────────────────────────────
   It is not gated on anything loading. A loader that waits for `load` is a screen that
   can be stuck if one image stalls, and this one has to be safe to ship on a host the
   client reviews from Iran. So the whole thing is one CSS timeline that ends by itself
   at 1.65s whether or not any script ever runs; a tap ends it sooner.

   It exists only when the head script says so — the first page in a tab, no reduced-
   motion preference, not a crawler or an audit run, whose screenshots it would cover.
   With no JavaScript it does not exist at all.

   The two lines of the lockup are the same bitmap twice, each clipped to its half at the
   empty band between them (rows 97–123 of 153), so the Persian and the Latin can arrive
   separately without cutting a new asset.
   ========================================================================== */
.intro { display: none; }

html.intro-on .intro {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: grid;
  place-items: center;
  overflow: hidden;
  background: var(--bg);
  animation: intro-out .5s cubic-bezier(.7, 0, .2, 1) 1.15s forwards;
}

/* A warm patch of the brand's red behind the mark, so it is not a logo on a void. */
html.intro-on .intro::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(520px 320px at 50% 46%, color-mix(in srgb, var(--accent) 16%, transparent), transparent 70%);
  opacity: 0;
  animation: intro-glow .9s ease .05s forwards;
}

.intro-stage {
  position: relative;
  display: grid;
  justify-items: center;
  gap: 22px;
  animation: intro-stage-out .28s ease 1.0s forwards;
}

.intro-logo {
  position: relative;
  inline-size: min(300px, 64vw);
  aspect-ratio: 426 / 153;
}
.intro-part { position: absolute; inset: 0; }
.intro-part img { display: block; inline-size: 100%; block-size: 100%; }

.intro-logo .logo-dark { display: none; }
:root[data-theme="dark"] .intro-logo .logo-light { display: none; }
:root[data-theme="dark"] .intro-logo .logo-dark  { display: block; }
:root:not([data-theme="light"]) .intro-logo .logo-light { display: none; }
:root:not([data-theme="light"]) .intro-logo .logo-dark  { display: block; }

/* The Persian arrives first, wiped in from the right as it is read; the Latin line
   follows from the left. Hidden until their turn, so there is no flash of the whole
   lockup before the animation starts. */
.intro-fa {
  clip-path: inset(0 0 28% 100%);
  animation: intro-fa .62s cubic-bezier(.22, .61, .36, 1) .12s forwards;
}
.intro-en {
  clip-path: inset(72% 100% 0 0);
  animation: intro-en .55s cubic-bezier(.22, .61, .36, 1) .44s forwards;
}

.intro-bar {
  inline-size: min(300px, 64vw);
  block-size: 3px;
  border-radius: 3px;
  background: color-mix(in srgb, var(--text) 12%, transparent);
  overflow: hidden;
}
.intro-bar i {
  display: block;
  block-size: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: right center;
  animation: intro-bar .95s cubic-bezier(.4, 0, .2, 1) .15s forwards;
}
[dir="ltr"] .intro-bar i { transform-origin: left center; }

/* A tap ends it: a quick fade rather than the curtain, which would be a hurry. */
html.intro-on .intro.is-skipped { animation: intro-skip .25s ease forwards; }

@keyframes intro-glow  { to { opacity: 1; } }
@keyframes intro-fa    { from { transform: translateY(8px); } to { clip-path: inset(0 0 28% 0); transform: none; } }
@keyframes intro-en    { to { clip-path: inset(72% 0 0 0); } }
@keyframes intro-bar   { to { transform: scaleX(1); } }
@keyframes intro-stage-out { to { opacity: 0; transform: scale(1.04); } }
@keyframes intro-out   { from { visibility: visible; } to { transform: translateY(-100%); visibility: hidden; } }
@keyframes intro-skip  { from { visibility: visible; } to { opacity: 0; visibility: hidden; } }

@media print { .intro { display: none !important; } }

/* ==========================================================================
   THE LOGO, LARGER
   The client's note was that the brand is seen too little, on a phone and on a desktop.
   The header's logo was 36px, the footer's 34px: a wordmark with a Latin line under it
   at a size where that line was a smudge. The header goes to 42px — the nav measures
   itself against whatever the brand takes (see "the header's own width" in app.js), so
   a wider logo costs the nav a point of type at most — and the footer's to 56px.
   ========================================================================== */
@media (min-width: 721px) {
  .brand img { block-size: 42px; }
}
.footer .brand img { block-size: 56px; }

/* ── two things the first look at the menu screen turned up ──────────────── */
@media (max-width: 1220px) {
  /* `.nav a` is `color: muted`, and it matches the call to action too — the button's
     own white lost to it by one element of specificity and read as pink on red. */
  .nav .nav-cta .btn,
  .nav .nav-cta .btn:hover,
  .nav .nav-cta .btn:active {
    color: var(--btn-fg);
    background: var(--btn-bg);
  }
  .nav .nav-cta .btn:hover { background: var(--accent-hover); }

  /* A phone has no hover, only the one the last tap left behind — and on the button that
     opens the menu it was a tilted, red-outlined cross. */
  .nav-toggle:hover,
  .nav-toggle:active {
    transform: none;
    border-color: transparent;
    color: var(--text);
  }
  .nav-toggle[aria-expanded="true"],
  .nav-toggle[aria-expanded="true"]:hover { border-color: var(--border); }
}

/* ==========================================================================
   THE BRAND BAND, AND THE LOGO WHERE THERE IS NO HEADER TO CARRY IT
   One rule swaps the logo with the theme for every place that is not the header: the
   dark file is the white wordmark and the light one the navy, and which shows follows
   the visitor's theme exactly as `.brand` does.
   ========================================================================== */
.logo-swap { display: block; }
.logo-swap img { display: block; margin-inline: auto; inline-size: auto; }
.logo-swap .logo-dark { display: none; }
:root[data-theme="dark"] .logo-swap .logo-light { display: none; }
:root[data-theme="dark"] .logo-swap .logo-dark  { display: block; }
:root:not([data-theme="light"]) .logo-swap .logo-light { display: none; }
:root:not([data-theme="light"]) .logo-swap .logo-dark  { display: block; }

.brand-band {
  border-block-start: 1px solid var(--border);
  background:
    radial-gradient(640px 260px at 50% 0%, color-mix(in srgb, var(--accent) 10%, transparent), transparent 70%),
    var(--surface-inset);
  padding-block: var(--s-8);
}
.brand-band-inner { display: grid; justify-items: center; gap: var(--s-6); text-align: center; }
.brand-band-logo img { block-size: clamp(96px, 24vw, 150px); }
.brand-band-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--s-3); }

.auth-logo { margin-block-end: var(--s-5); }
.auth-logo img { block-size: 72px; }

@media (max-width: 1220px) {
  /* The dock floats over the bottom of the page; the band keeps clear of it the way the
     footer does, by the body's own padding — nothing extra needed here. */
  .brand-band { padding-block: var(--s-7); }
}

/* ==========================================================================
   THE TWO BRAND BANNERS, UNDER THE CATEGORY STRIP
   Arkel's range on one side, the new agency's on the other: same size, same shape, same
   weight, so neither is "the main one". Each is the brand's own colour — Arkel's blue,
   Liftena's cyan — whatever the page's theme, because it is the brand being shown.
   ========================================================================== */
.brand-banners {
  padding-block: var(--s-5) var(--s-2);
  background: var(--surface);
}
.brand-banners-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-4);
}
@media (max-width: 800px) { .brand-banners-grid { grid-template-columns: minmax(0, 1fr); } }

.brand-banner {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--s-4);
  min-block-size: 232px;
  padding: var(--s-5) var(--s-6);
  overflow: hidden;
  border-radius: var(--radius-lg);
  color: #fff;
  text-decoration: none;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.brand-banner:hover, .brand-banner:focus-visible {
  transform: translateY(-3px);
  box-shadow: 0 22px 40px -24px rgba(5, 12, 24, .75);
  text-decoration: none;
}
.brand-banner:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }

.brand-banner.is-arkel {
  background:
    radial-gradient(440px 240px at 4% 112%, rgba(255, 138, 36, .30), transparent 70%),
    linear-gradient(135deg, #0C3E92 0%, #082C69 58%, #061D49 100%);
}
.brand-banner.is-liftena {
  background:
    radial-gradient(440px 240px at 96% 112%, rgba(10, 154, 205, .38), transparent 70%),
    linear-gradient(135deg, #0A2234 0%, #0B3550 58%, #0A4565 100%);
}

/* Lift-shaft lines, fading out towards the words. */
.brand-banner::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: repeating-linear-gradient(90deg, rgba(255, 255, 255, .06) 0 1px, transparent 1px 38px);
  -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 80%);
  mask-image: linear-gradient(to right, transparent 0%, #000 80%);
  pointer-events: none;
}
[dir="rtl"] .brand-banner::after {
  -webkit-mask-image: linear-gradient(to left, transparent 0%, #000 80%);
  mask-image: linear-gradient(to left, transparent 0%, #000 80%);
}

.brand-banner-logo img { display: block; block-size: 40px; inline-size: auto; }
.brand-banner-copy { display: grid; gap: var(--s-2); }
.brand-banner-eyebrow { font-size: .8125rem; font-weight: 700; letter-spacing: .02em; color: rgba(255, 255, 255, .72); }
.is-arkel .brand-banner-eyebrow { color: #FFB066; }
.is-liftena .brand-banner-eyebrow { color: #6FD3F5; }
.brand-banner-title { font-size: clamp(1.25rem, 2.2vw, 1.625rem); line-height: 1.5; font-weight: 800; }
.brand-banner-text {
  max-inline-size: 46ch;
  color: rgba(255, 255, 255, .78);
  font-size: .9375rem;
  line-height: 1.8;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.brand-banner-cta {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-block-size: 44px;
  padding: 0 var(--s-5);
  border-radius: 999px;
  background: #fff;
  color: #0B1B2C;
  font-size: .9375rem;
  font-weight: 700;
}
.brand-banner-cta svg { inline-size: 18px; block-size: 18px; transition: transform var(--dur) var(--ease); }
[dir="rtl"] .brand-banner-cta svg { transform: scaleX(-1); }
.brand-banner:hover .brand-banner-cta svg { transform: translateX(3px); }
[dir="rtl"] .brand-banner:hover .brand-banner-cta svg { transform: scaleX(-1) translateX(3px); }

@media (max-width: 560px) {
  .brand-banner { min-block-size: 214px; padding: var(--s-5); }
}
@media (prefers-reduced-motion: reduce) {
  .brand-banner, .brand-banner-cta svg { transition: none; }
  .brand-banner:hover, .brand-banner:focus-visible { transform: none; }
}

/* ═══════════════════════════════════════════════ HOME: THE PROJECT RING ═══
   The featured projects' posters on a cylinder that turns a step at a time.

   Written so the page is whole without the script: a row of posters that scrolls
   sideways and snaps, each one a link. The script adds `.is-live`, and only then does the
   row become a ring — a stage with perspective, a track that turns, posters laid round it.
   Physical `left`/`margin-left` inside the ring on purpose: 3D transforms have no
   writing direction, so the geometry must not mirror with the page. */
.home-projects { overflow-x: clip; }

.ring { --card-w: clamp(176px, 22vw, 296px); --ring-ms: 900ms; --dir: 1; }
/* The next poster comes round from the side the page reads towards. */
[dir="rtl"] .ring { --dir: -1; }

.ring-stage {
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
  padding-block: var(--s-3) var(--s-4);
  -webkit-overflow-scrolling: touch;
}
.ring-track {
  display: flex;
  gap: var(--s-4);
  inline-size: max-content;
  margin: 0;
  padding: 0 var(--gutter);
  list-style: none;
}
.ring-flat .ring-track { inline-size: auto; justify-content: center; flex-wrap: wrap; }
.ring-item { flex: 0 0 var(--card-w); scroll-snap-align: center; }

.ring-card {
  display: block;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow);
  -webkit-tap-highlight-color: transparent;
}
.ring-card img { display: block; inline-size: 100%; block-size: 100%; object-fit: cover; }
.ring-card:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }

/* ── live: the ring ─────────────────────────────────────────────────────── */
.ring.is-live {
  /* From the axis to a poster's face: where they just touch, plus a little air. */
  --r: calc(var(--card-w) * var(--reach) * 1.42);
}
.ring.is-live .ring-stage {
  position: relative;
  overflow: visible;
  scroll-snap-type: none;
  padding: 0;
  block-size: calc(var(--card-w) * 1.25 + var(--s-6));
  perspective: 2000px;
  perspective-origin: 50% 42%;
  touch-action: pan-y;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
}
.ring.is-live .ring-stage.is-dragging { cursor: grabbing; }

.ring.is-live .ring-track {
  position: absolute;
  inset: 0;
  display: block;
  inline-size: auto;
  padding: 0;
  transform-style: preserve-3d;
  /* The front poster sits on the stage's own plane, so it is drawn at its true size. */
  transform: translateZ(calc(var(--r) * -1)) rotateY(var(--turn, 0deg));
  transition: transform var(--ring-ms) cubic-bezier(.22, .61, .36, 1);
}
.ring.is-live .ring-stage.is-dragging .ring-track { transition: none; }

.ring.is-live .ring-item {
  position: absolute;
  inset-block-start: var(--s-3);
  left: 50%;
  margin-left: calc(var(--card-w) / -2);
  inline-size: var(--card-w);
  transform: rotateY(calc(var(--i) * var(--dir) * 360deg / var(--n))) translateZ(var(--r));
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  opacity: .38;
  transition: opacity 600ms var(--ease);
}
.ring.is-live .ring-item.is-near  { opacity: .72; }
.ring.is-live .ring-item.is-front { opacity: 1; }
.ring.is-live .ring-item:not(.is-front) .ring-card { box-shadow: var(--shadow-sm); }
.ring.is-live .ring-item.is-front .ring-card { box-shadow: var(--shadow-lg); }

/* ── caption, arrows, dots ──────────────────────────────────────────────── */
.ring-meta {
  display: grid;
  justify-items: center;
  gap: var(--s-4);
  margin-block-start: var(--s-5);
}
.ring-meta[hidden] { display: none; }

.ring-caption { display: grid; gap: 2px; justify-items: center; text-align: center; min-block-size: 3.4em; }
.ring-title {
  color: var(--text);
  font-size: 1.125rem;
  font-weight: 700;
  line-height: 1.5;
  text-decoration: none;
}
.ring-title:hover { color: var(--accent-ink); text-decoration: underline; text-underline-offset: 4px; }
.ring-place { color: var(--text-muted); font-size: .9375rem; }

.ring-controls { display: flex; align-items: center; flex-wrap: wrap; justify-content: center; gap: var(--s-3); }

.ring-btn {
  display: grid;
  place-items: center;
  inline-size: 44px;
  block-size: 44px;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  transition: border-color var(--dur-micro) var(--ease), color var(--dur-micro) var(--ease), background var(--dur-micro) var(--ease);
}
.ring-btn svg { inline-size: 20px; block-size: 20px; }
.ring-btn:hover { border-color: var(--accent); color: var(--accent-ink); }
.ring-btn:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
/* The arrows are drawn for a page that reads right to left; in a left-to-right one they turn round. */
[dir="ltr"] .ring-btn[data-ring-prev] svg,
[dir="ltr"] .ring-btn[data-ring-next] svg { transform: scaleX(-1); }
.ring-toggle .ico-play { display: none; }
.ring-toggle[aria-pressed="true"] .ico-pause { display: none; }
.ring-toggle[aria-pressed="true"] .ico-play { display: block; }

.ring-dots { display: flex; align-items: center; gap: 10px; margin: 0; padding: 0 var(--s-2); list-style: none; }
.ring-dot {
  position: relative;
  inline-size: 10px;
  block-size: 10px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: var(--border-strong);
  cursor: pointer;
  transition: inline-size var(--dur) var(--ease), background var(--dur) var(--ease);
}
/* A tap target that is not ten pixels wide. */
.ring-dot::after { content: ''; position: absolute; inset: -17px -6px; }
.ring-dot[aria-current="true"] { inline-size: 28px; background: var(--accent); }
.ring-dot:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }

.home-projects-all { display: flex; justify-content: center; margin: var(--s-6) 0 0; }

@media (max-width: 640px) {
  .ring { --card-w: min(56vw, 236px); }
  .ring.is-live { --r: calc(var(--card-w) * var(--reach) * 1.3); }
  .ring.is-live .ring-stage { perspective: 1300px; }
}

@media (prefers-reduced-motion: reduce) {
  .ring.is-live .ring-track,
  .ring.is-live .ring-item { transition: none; }
}

/* ---------------------------------------------------------- touch targets
   The audit measured about twenty targets under 44px on a phone: the category tree
   (33px), the "clear" text button (25px), the breadcrumb links (24px, on every page)
   and the poster dots (8px). Real height where the layout can take it; an invisible
   hit area around the dots, which have to stay small to read as dots. */
@media (max-width: 720px), (pointer: coarse) {
  .tree-link { min-block-size: 44px; }

  .btn-text {
    min-block-size: 44px;
    padding-inline: var(--s-2);
    display: inline-flex;
    align-items: center;
  }
  /* The underline that grows on hover sits under the words, not under the hit area. */
  .btn-text::after { inset-block-end: 10px; }

  .crumbs { padding-block: var(--s-1); }
  .crumbs a {
    display: inline-flex;
    align-items: center;
    min-block-size: 44px;
    padding-inline: 2px;
  }

  .poster-dot { position: relative; }
  .poster-dot::after { content: ""; position: absolute; inset: -18px -7px; }
}
