/* ═══════════════════════════════════════════════════════════════════════════
   Layout and components.

   Selectors are single-class and BEM-ish on purpose. Mixing `.section` with
   `p`/`div` element selectors is how padding rules silently cancel each other.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Shell ─────────────────────────────────────────────────────────────────
   RTL, so the rail sits on the RIGHT. Using `grid-template-columns` with the
   rail first works because the document direction flips it — no `direction`
   overrides, no physical properties. */
.shell {
  display: grid;
  /* `minmax(0, 1fr)`, NOT `1fr`. A bare `1fr` track is implicitly `minmax(auto, 1fr)`, and
     that `auto` floor means the track cannot shrink below its content's min-content width —
     so one wide table pushes the whole page sideways even though the table sits inside its
     own `overflow-x` container. Measured: 736px-wide rail and stage on a 390px viewport. */
  grid-template-columns: var(--rail-w) minmax(0, 1fr);
  min-height: 100dvh;
}

/* ── Rail ──────────────────────────────────────────────────────────────────
   The five beats as a numbered vertical sequence. Numbering is used here
   because procurement genuinely IS a sequence — these are the client's own
   five published steps — not because numbered markers look editorial. */
.rail {
  background: var(--ink);
  border-inline-start: 1px solid var(--ink-3);
  display: flex; flex-direction: column; align-items: center;
  padding-block: var(--s-4) var(--s-5);
  gap: var(--s-2);
  position: sticky; top: 0; height: 100dvh;
  z-index: 40;
}

.rail__mark {
  width: 34px; height: 34px;
  margin-block-end: var(--s-5);
  opacity: .95;
}

.rail__step {
  --step-fg: var(--ink-dim);
  appearance: none; background: none; border: 0;
  color: var(--step-fg);
  font: inherit; cursor: pointer;
  width: 100%; padding: var(--s-2) var(--s-1);
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  position: relative;
  transition: color .18s var(--ease);
}
.rail__step:hover { --step-fg: var(--ink-text); }

.rail__num {
  width: 26px; height: 26px; border-radius: var(--r-pill);
  border: 1px solid currentColor;
  display: grid; place-items: center;
  font-size: var(--t-xs); font-weight: 500;
  font-variant-numeric: tabular-nums;
  transition: background .18s var(--ease), border-color .18s var(--ease), color .18s var(--ease);
}
.rail__label {
  font-size: 10px; line-height: 1.3; text-align: center;
}

.rail__step[aria-current='true'] { --step-fg: var(--gold-lit); }
.rail__step[aria-current='true'] .rail__num {
  background: var(--gold); border-color: var(--gold); color: var(--ink);
  font-weight: 700;
}
/* The active marker: a gold bar on the rail's inner edge. */
.rail__step[aria-current='true']::before {
  content: ''; position: absolute;
  inset-inline-start: 0; inset-block: 6px;
  width: 2px; background: var(--gold-lit);
}

/* A step that is not reachable yet says so on hover rather than looking broken. */
.rail__step[disabled] { opacity: .38; cursor: not-allowed; }

.rail__spacer { flex: 1; }

.rail__util {
  appearance: none; background: none; border: 1px solid var(--ink-3);
  color: var(--ink-dim); border-radius: var(--r-ui);
  width: 34px; height: 30px; cursor: pointer;
  display: grid; place-items: center; font-size: var(--t-xs);
  transition: border-color .18s var(--ease), color .18s var(--ease);
}
.rail__util:hover { border-color: var(--gold); color: var(--gold-lit); }

/* ── Stage ─────────────────────────────────────────────────────────────────*/
.stage {
  position: relative;
  padding: var(--s-6) var(--s-7) var(--s-8);
  max-width: 1360px; width: 100%;
  margin-inline: auto;
}

.stage__head { margin-block-end: var(--s-5); }

/* NO letter-spacing on Arabic.
   Arabic is cursive — the letters JOIN. CSS tracking inserts space between joined forms
   and breaks the connection, so «الخطوة» renders as disconnected letterforms. Tracking on
   small-caps labels is a Latin typographic convention that does not transfer, and applying
   it here was the single most Latin-first thing in this stylesheet: 81 instances across
   every screen. text-transform: uppercase is a no-op in Arabic for the same reason. */
.eyebrow {
  font-size: var(--t-xs);
  color: var(--gold); text-transform: none;   /* uppercase is a no-op in Arabic */
  margin-block-end: var(--s-2);
  display: flex; align-items: center; gap: var(--s-2);
}
.eyebrow::after {
  content: ''; flex: 1; height: 1px;
  background: linear-gradient(to left, var(--gold-deep), transparent);
}

.stage__title {
  font-family: var(--font-ui);
  font-size: var(--t-2xl); font-weight: 700; line-height: var(--lh-tight);
  margin: 0 0 var(--s-2); color: var(--ink-text);
  /* No optical tightening either. Negative tracking is a Latin display-type habit; on a
     cursive script it pulls joined letters into each other. This is the largest Arabic
     text on every screen, so it is the worst place to do it — and at -.01em it sat just
     under the audit threshold, which is exactly how it survived. */
}
.stage__sub {
  color: var(--ink-dim); font-size: var(--t-md);
  margin: 0; max-width: 68ch;
}

/* ── Paper document ────────────────────────────────────────────────────────
   The signature surface. Most of the screen area is this. */
.doc {
  background: var(--paper);
  color: var(--paper-text);
  border-radius: var(--r-doc);
  box-shadow: var(--shadow-doc);
  position: relative;
  overflow: hidden;
}

/* The letterhead rule: his three gold stops, left to right. */
.doc::before {
  content: ''; position: absolute; inset-block-start: 0; inset-inline: 0;
  height: 3px;
  background: linear-gradient(to left, var(--gold-deep), var(--gold), var(--gold-lit));
}

.doc__head {
  padding: var(--s-5) var(--s-6) var(--s-4);
  border-block-end: 1px solid var(--paper-rule);
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--s-5); flex-wrap: wrap;
}

.doc__title {
  font-family: var(--font-doc);
  font-size: var(--t-xl); line-height: 1.3; margin: 0;
  color: var(--paper-text); font-weight: 400;
}
.doc__meta {
  font-size: var(--t-sm); color: var(--paper-dim);
  margin: var(--s-1) 0 0; display: flex; gap: var(--s-4); flex-wrap: wrap;
}
.doc__body { padding: var(--s-5) var(--s-6) var(--s-6); }
.doc__foot {
  padding: var(--s-4) var(--s-6);
  border-block-start: 1px solid var(--paper-rule);
  background: var(--paper-2);
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4); flex-wrap: wrap;
}

/* ── Micro-interaction ─────────────────────────────────────────────────────
   Feedback on touch, not decoration. Every rule here is transform, opacity or
   colour — nothing that moves a neighbour, because the CLS budget is 0.1 and
   most of it is already committed. */

/* A pressed control must acknowledge the press within a frame. 200ms is the
   feedback threshold this build holds itself to; a button that looks inert for
   longer gets pressed twice. */
.btn { transition: background var(--d-fast) var(--ease), border-color var(--d-fast) var(--ease), color var(--d-fast) var(--ease), transform var(--d-fast) var(--ease); }
.btn:active:not(:disabled) { transform: translateY(1px); }

/* The letterhead rule wipes on as the document lands, left to right, in his own
   three gold stops. It is the first thing that happens on a new document and it
   is the brand mark doing it — a detail worth one line of CSS. */
.doc::before { transform-origin: left center; animation: rule-draw var(--d-slow) var(--ease-paper) both; }
@keyframes rule-draw { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* ── Sheet (data table on paper) ───────────────────────────────────────────*/
.sheet { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.sheet caption { text-align: start; padding-block-end: var(--s-3); color: var(--paper-dim); font-size: var(--t-sm); }

.sheet th {
  text-align: start; font-weight: 500; font-size: var(--t-xs);
  color: var(--paper-dim);
  padding: var(--s-2) var(--s-3);
  border-block-end: 1px solid var(--paper-text);
  white-space: nowrap;
}
.sheet td {
  padding: var(--s-3);
  border-block-end: 1px solid var(--paper-rule);
  vertical-align: baseline;
}
.sheet tbody tr:hover { background: var(--paper-2); }
.sheet tbody tr:last-child td { border-block-end: 0; }

.sheet__name { font-weight: 500; }
.sheet__spec { color: var(--paper-dim); font-size: var(--t-xs); display: block; margin-block-start: 2px; }
.sheet .num { text-align: end; white-space: nowrap; }

.sheet__scope-row td {
  background: var(--paper-2);
  font-weight: 500; font-size: var(--t-xs);
  color: var(--gold-deep);
  border-block-end: 1px solid var(--paper-rule);
}

.sheet__total td {
  border-block-start: 2px solid var(--paper-text);
  border-block-end: 0;
  font-weight: 700; font-size: var(--t-base);
  padding-block: var(--s-3);
}
.sheet__subtotal td { color: var(--paper-dim); border-block-end: 0; padding-block: 3px; }

/* ── Chips and tags ────────────────────────────────────────────────────────*/
.chip {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--t-xs); line-height: 1;
  padding: 5px 9px; border-radius: var(--r-pill);
  border: 1px solid currentColor;
  white-space: nowrap;
}
.chip--gold   { color: var(--gold-deep); background: rgba(184,139,46,.10); }
.chip--flag   { color: var(--flag);      background: rgba(156,74,51,.09); }
.chip--quiet  { color: var(--paper-dim); border-color: var(--paper-rule); }
.chip--onink  { color: var(--gold-lit);  border-color: var(--ink-3); background: var(--ink-2); }
.chip--dim    { color: var(--ink-dim);   border-color: var(--ink-3); }

/* The «تقديري» marker. It appears wherever a number is an estimate rather than
   a quote, because "never display an invented price as researched" is the
   fastest way this demo could lose its credibility. */
.est {
  font-size: var(--t-xs); color: var(--gold-deep);
  border: 1px dashed var(--gold-deep); border-radius: var(--r-ui);
  padding: 1px 5px; margin-inline-start: 6px; white-space: nowrap;
}

/* ── Buttons ───────────────────────────────────────────────────────────────*/
.btn {
  appearance: none; font: inherit; font-weight: 500;
  cursor: pointer; border-radius: var(--r-ui);
  padding: 10px 20px; border: 1px solid transparent;
  display: inline-flex; align-items: center; gap: var(--s-2);
  transition: background .16s var(--ease), border-color .16s var(--ease), color .16s var(--ease), transform .08s var(--ease);
  line-height: 1.2;
  min-height: 40px;              /* WCAG 2.5.8 target size */
}
.btn:active { transform: translateY(1px); }

.btn--primary {
  background: linear-gradient(to left, var(--gold-deep), var(--gold));
  color: #1A1408; font-weight: 700;
  border-color: var(--gold-deep);
}
.btn--primary:hover { background: linear-gradient(to left, var(--gold), var(--gold-lit)); }

.btn--ghost { background: transparent; border-color: var(--ink-3); color: var(--ink-text); }
.btn--ghost:hover { border-color: var(--gold); color: var(--gold-lit); }

.btn--paper { background: transparent; border-color: var(--paper-rule); color: var(--paper-text); }
.btn--paper:hover { border-color: var(--gold-deep); color: var(--gold-deep); background: rgba(184,139,46,.07); }

.btn--sm { padding: 6px 12px; font-size: var(--t-sm); min-height: 32px; }

/* A disabled control must say what would enable it. The title is set in JS and
   the cursor change is the affordance. */
.btn[disabled] { opacity: .45; cursor: not-allowed; }
.btn[disabled]:active { transform: none; }

/* ── Fields ────────────────────────────────────────────────────────────────*/
.field { display: block; margin-block-end: var(--s-4); }
.field__label {
  display: block; font-size: var(--t-sm); font-weight: 500;
  margin-block-end: var(--s-2); color: var(--ink-text);
}
.field__hint { display: block; font-size: var(--t-xs); color: var(--ink-dim); margin-block-start: 4px; }
.field__req { color: var(--gold); margin-inline-start: 3px; }

.input, .textarea, .select {
  width: 100%; font: inherit; color: var(--ink-text);
  background: var(--ink-2); border: 1px solid var(--ink-3);
  border-radius: var(--r-ui); padding: 10px 13px;
  transition: border-color .16s var(--ease), background .16s var(--ease);
  min-height: 42px;
}
.input:hover, .textarea:hover, .select:hover { border-color: var(--ink-dim); }
.input:focus, .textarea:focus, .select:focus { border-color: var(--gold); background: var(--ink); outline-offset: 1px; }
.input::placeholder, .textarea::placeholder { color: var(--ink-dim); opacity: .8; }
.textarea { resize: vertical; min-height: 96px; line-height: var(--lh-body); }

.field--invalid .input, .field--invalid .textarea { border-color: var(--flag-soft); }
.field__error { color: var(--flag-soft); font-size: var(--t-xs); margin-block-start: 4px; display: block; }

/* ── Panels on chrome ──────────────────────────────────────────────────────*/
.panel {
  background: var(--ink-2); border: 1px solid var(--ink-3);
  border-radius: var(--r-ui); padding: var(--s-5);
}
.panel__title {
  font-size: var(--t-sm); font-weight: 700; color: var(--ink-text);
  margin: 0 0 var(--s-3); display: flex; align-items: center; gap: var(--s-2);
}

/* ── Stat ──────────────────────────────────────────────────────────────────
   Deliberately not a card grid. A stat is a number with a rule under it. */
.stats { display: flex; flex-wrap: wrap; gap: var(--s-6); }
.stat { min-width: 108px; }
.stat__v {
  font-size: var(--t-xl); font-weight: 700; line-height: 1.1;
  color: var(--gold-lit); font-variant-numeric: tabular-nums;
}
.stat__k { font-size: var(--t-xs); color: var(--ink-dim); margin-block-start: 3px; }
.stat--paper .stat__v { color: var(--gold-deep); }
.stat--paper .stat__k { color: var(--paper-dim); }

/* ── Empty and restricted states ───────────────────────────────────────────
   RUBRIC row 11. A correct-but-reduced view must explain itself where the user
   is looking, or it is indistinguishable from a broken one. */
.empty {
  padding: var(--s-7) var(--s-5); text-align: center;
  border: 1px dashed var(--ink-3); border-radius: var(--r-ui);
  color: var(--ink-dim);
}
.empty--paper { border-color: var(--paper-rule); color: var(--paper-dim); }
.empty__t { font-weight: 700; color: var(--ink-text); margin-block-end: var(--s-2); }
.empty--paper .empty__t { color: var(--paper-text); }
.empty__b { font-size: var(--t-sm); max-width: 46ch; margin-inline: auto; }

/* "Showing 17 of 380 — scope: your records only". Stated inline, never in a
   tooltip, because the number is only trustworthy if the scope is next to it. */
.scope {
  font-size: var(--t-xs); color: var(--ink-dim);
  padding: var(--s-2) 0; display: flex; align-items: center; gap: var(--s-2);
}
.scope--paper { color: var(--paper-dim); }

/* ── Skeleton ──────────────────────────────────────────────────────────────
   Never a spinner on an empty screen. The rule is ≤200 ms to ANY visible
   change, not to finished content. */
.skel {
  background: linear-gradient(90deg, var(--ink-2) 25%, var(--ink-3) 50%, var(--ink-2) 75%);
  background-size: 300% 100%;
  animation: skel 1.3s ease-in-out infinite;
  border-radius: var(--r-ui);
}
.skel--paper {
  background: linear-gradient(90deg, var(--paper-2) 25%, var(--paper-rule) 50%, var(--paper-2) 75%);
  background-size: 300% 100%;
}
@keyframes skel { from { background-position: 100% 0; } to { background-position: -200% 0; } }

/* ── The stamp ─────────────────────────────────────────────────────────────
   THE SIGNATURE ELEMENT. Saudi commercial documents are stamped, and «ترسية»
   (award) is the moment a procurement decision becomes real. So awarding a
   package lands a seal on the document — rotated slightly, as a hand would. */
/* The footer is the seal's home once a package is awarded. */
.stamp {
  --stamp-size: 124px;
  /* IN FLOW, at every width. Not absolutely positioned.
     Three placements were tried against live content. Bottom-right covered the winning
     supplier's name. Bottom-left covered «تنبيه على مدة التوريد» and the Royal Decree M/126
     cancellation notice under it — 6,314 px², the seal celebrating the award sitting on the
     only real legal text on the screen. Moving it into the footer with reserved padding
     fixed that and then covered the «افتح لوحة المشروع» button instead, 4,567 px² at 900px,
     because the footer wraps and the reservation assumed it would not.
     Every one of those failed the same way: the position was a bet on what the surrounding
     content would be. A flex item cannot overlap its siblings, so it is not a bet. The
     620px rule reached this conclusion for phones first; it was always true at every width. */
  flex: 0 0 auto;
  width: var(--stamp-size); height: var(--stamp-size);
  border: 3px double var(--flag);
  border-radius: 50%;
  color: var(--flag);
  display: grid; place-items: center; text-align: center;
  transform: rotate(-14deg);
  opacity: .82;
  pointer-events: none;
  mix-blend-mode: multiply;
  animation: stamp-land .42s var(--ease) both;
}
.stamp__in {
  border: 1px solid var(--flag); border-radius: 50%;
  width: calc(var(--stamp-size) - 22px); height: calc(var(--stamp-size) - 22px);
  display: grid; place-items: center; align-content: center; gap: 2px;
  padding: var(--s-2);
}
.stamp__t { font-family: var(--font-doc); font-size: var(--t-md); line-height: 1.2; }
/* Digits only, so tracking is correct here — this is the one place it survives. */
.stamp__d { font-size: 9px; letter-spacing: .06em; font-variant-numeric: tabular-nums; }

@keyframes stamp-land {
  0%   { opacity: 0; transform: rotate(-14deg) scale(2.4); }
  70%  { opacity: .95; transform: rotate(-14deg) scale(.94); }
  100% { opacity: .82; transform: rotate(-14deg) scale(1); }
}

/* ── Utility ───────────────────────────────────────────────────────────────*/
.row { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
.row--between { justify-content: space-between; }
.col { display: flex; flex-direction: column; gap: var(--s-3); }
/* `minmax(320px, 1fr)` forces a 320px floor even at 390px viewport, which — once the
   stage and panel padding are added — pushed the page 212px wider than the screen.
   `min(320px, 100%)` lets the track collapse instead of overflowing. */
/* Seven archetypes in a 3-wide grid leaves the seventh stranded alone beside a large
   empty gap. A narrower track packs 4 per row at desktop, so it reads 4+3 — no lone card,
   and denser, which suits the direction better than sparse cards do. */
.grid-arch {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr));
  gap: var(--s-3);
}

/* The opener's five beats. Explicit column counts rather than `auto-fit`, because auto-fit
   sized them at 250px and fitted four across a 1256px stage — leaving the fifth alone on a
   second row, so the five steps read as four with an afterthought. The counts below always
   divide five into balanced rows: 5, then 3+2, then 2+2+1, then one per row. */
.grid-beats {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--s-3);
}
@media (max-width: 1300px) { .grid-beats { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 760px)  { .grid-beats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px)  { .grid-beats { grid-template-columns: minmax(0, 1fr); } }

.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); gap: var(--s-5); }
.mt-4 { margin-block-start: var(--s-4); }
.mt-5 { margin-block-start: var(--s-5); }
.mt-6 { margin-block-start: var(--s-6); }
.dim { color: var(--ink-dim); }
.dim-paper { color: var(--paper-dim); }
.sm { font-size: var(--t-sm); }
.xs { font-size: var(--t-xs); }
.hide { display: none !important; }

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

/* A skip link that stays clipped WHILE FOCUSED is worse than no skip link.
   It is the first Tab stop on the page, so a keyboard user's very first keystroke lands on
   a 1×1 box at -1px: focus appears to vanish and the page looks like it swallowed it.
   Measured before this rule — `a.sr-only  box 1x1 @-1`. */
.sr-only:focus,
.sr-only:focus-visible {
  position: fixed;
  inset-block-start: var(--s-3);
  inset-inline-start: var(--s-3);
  width: auto; height: auto;
  margin: 0; padding: 10px 18px;
  clip: auto; overflow: visible; white-space: normal;
  z-index: 100;
  background: var(--ink-2);
  color: var(--gold-lit);
  border: 1px solid var(--gold);
  border-radius: var(--r-ui);
  font-size: var(--t-sm); font-weight: 500;
  box-shadow: 0 8px 24px -8px rgba(0, 0, 0, .7);
}

/* ── Responsive ────────────────────────────────────────────────────────────
   Tested at 390 and 1440. Wide content scrolls inside its own container — the
   page body must never scroll horizontally. */
/* `min-width: 0` is the flex/grid counterpart of the `minmax(0,1fr)` fix above: without
   it, a scroll container that is itself a flex or grid item still refuses to shrink below
   its content. Wide content scrolls inside here; the page body never scrolls sideways. */
.scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; min-width: 0; max-width: 100%; }

/* A table inside a scroll container must keep a readable width and SCROLL, not reflow.
   With `width:100%` alone, every cell wrapped at 390px and the supplier list grew to
   7,869px of stacked text — technically no overflow, practically unusable. Giving the
   table a floor turns that back into a normal horizontally-scrollable table. */
.scroll-x > .sheet { min-width: 680px; }

/* ── Document tables on a phone ────────────────────────────────────────────
   Horizontal scroll is the right answer for a BROWSING table (the supplier
   register, the comparison matrix): you skim names, then scroll for detail.

   It is the WRONG answer for a DOCUMENT — a bill of materials or a tax invoice.
   There the amounts ARE the content, and hiding every number behind a sideways
   scroll while showing only item names makes the invoice unreadable at exactly
   the moment it matters. He opens the demo on his phone.

   So `.sheet--doc` stacks into labelled lines below 620px. The label comes from
   `data-label` on the cell, which keeps the desktop markup a normal table. */
@media (max-width: 620px) {
  .scroll-x > .sheet--doc { min-width: 0; }

  .sheet--doc thead { display: none; }
  /* `caption` MUST be in this list. Left as `display: table-caption` while its table is
     `display: block`, it collapses to its minimum width and renders one character per
     line — a narrow vertical ribbon of Arabic letters above the table. */
  .sheet--doc, .sheet--doc caption, .sheet--doc tbody, .sheet--doc tfoot,
  .sheet--doc tr, .sheet--doc td {
    display: block; width: 100%;
  }
  .sheet--doc tbody tr {
    padding-block: var(--s-3);
    border-block-end: 1px solid var(--paper-rule);
  }
  .sheet--doc tbody tr:last-child { border-block-end: 0; }
  .sheet--doc td { border: 0; padding: 2px var(--s-1); }

  /* The numeric cells become a label/value row so the figure keeps its meaning. */
  .sheet--doc td[data-label] {
    display: flex; justify-content: space-between; gap: var(--s-3);
    font-variant-numeric: tabular-nums;
  }
  .sheet--doc td[data-label]::before {
    content: attr(data-label);
    color: var(--paper-dim); font-size: var(--t-xs); font-weight: 400;
  }
  .sheet--doc .num { text-align: end; }

  /* Scope headers and totals stay full-width bars. */
  .sheet--doc .sheet__scope-row td,
  .sheet--doc .sheet__subtotal td,
  .sheet--doc .sheet__total td { display: flex; justify-content: space-between; gap: var(--s-3); }
  .sheet--doc .sheet__scope-row td[colspan]::before,
  .sheet--doc .sheet__subtotal td[colspan]::before,
  .sheet--doc .sheet__total td[colspan]::before { content: none; }
  .sheet--doc .sheet__total { border-block-start: 2px solid var(--paper-text); padding-block-start: var(--s-2); }
}
.scroll-x { scrollbar-width: thin; }

/* Make the affordance visible: a hairline gradient on the leading edge tells the reader
   there is more table off-screen, which a bare cut-off does not. */
@media (max-width: 900px) {
  .scroll-x { position: relative; }
  .doc .scroll-x::after {
    content: ''; position: sticky; inset-inline-end: 0; inset-block: 0;
    display: block; width: 18px; margin-inline-start: -18px;
    background: linear-gradient(to right, transparent, rgba(0,0,0,.10));
    pointer-events: none;
  }
}

@media (max-width: 900px) {
  .shell { grid-template-columns: minmax(0, 1fr); }
  .rail {
    flex-direction: row; height: auto; position: sticky; inset-block-start: 0;
    border-inline-start: 0; border-block-end: 1px solid var(--ink-3);
    padding: var(--s-2) var(--s-3); gap: 0;
    overflow-x: auto; scrollbar-width: none;
  }
  .rail::-webkit-scrollbar { display: none; }
  .rail__mark { margin-block-end: 0; margin-inline-end: var(--s-3); width: 26px; height: 26px; flex: none; }
  .rail__step { flex-direction: row; gap: 6px; width: auto; flex: none; padding: var(--s-2) var(--s-3); }
  .rail__step[aria-current='true']::before { inset-block-end: 0; inset-block-start: auto; inset-inline: 4px; width: auto; height: 2px; }
  .rail__label { font-size: var(--t-xs); }
  .rail__spacer { flex: 1 0 var(--s-4); }

  /* Below ~560px, number-beside-label stops fitting: the rail measured 588px inside a 390px
     viewport, so «العروض» and «المشروع» sat off the end behind a scroll with no affordance
     signalling it. The rail IS the claim that there are five steps — a client swiping the
     demo on his phone saw three and nothing to suggest more, and the two hidden ones are the
     payoff screens. Stack the number over the label again, as the desktop rail does, and
     tighten the padding: all five then fit inside 390px with room to spare. The horizontal
     scroll stays as a backstop for anything narrower still. */
  @media (max-width: 560px) {
    .rail { padding-inline: var(--s-2); }
    .rail__mark { margin-inline-end: var(--s-2); }
    .rail__step { flex-direction: column; gap: 2px; padding: var(--s-1) 5px; }
    .rail__label { font-size: 9px; }
    .rail__num { width: 22px; height: 22px; }
  }

  .stage { padding: var(--s-5) var(--s-4) var(--s-8); }
  .stage__title { font-size: var(--t-xl); }
  .doc__head, .doc__body, .doc__foot { padding-inline: var(--s-4); }
  .stamp { --stamp-size: 96px; }
}

@media (max-width: 560px) {
  .stats { gap: var(--s-4); }
  .stat__v { font-size: var(--t-lg); }
}

/* ── Tour ──────────────────────────────────────────────────────────────────
   Deliberately NOT a full-screen overlay.

   driver.js and friends put a blocking layer above the app, which makes every
   control underneath read as dead to a click-crawler — and dead ends are a hard
   gate. So the spotlight is a ring drawn with box-shadow, `pointer-events:none`
   throughout, and only the card itself is interactive. The page stays live and
   the client can leave the tour by just using the app. */
#tour { position: fixed; inset: 0; z-index: 90; pointer-events: none; }

.tour__spot {
  position: fixed;
  border: 1px solid var(--gold-lit);
  border-radius: var(--r-ui);
  box-shadow: 0 0 0 9999px rgba(10, 8, 6, .58);
  transition: top .3s var(--ease), left .3s var(--ease),
              width .3s var(--ease), height .3s var(--ease);
  pointer-events: none;
}

.tour__card {
  position: fixed;
  width: min(360px, calc(100vw - 24px));
  background: var(--ink-2);
  border: 1px solid var(--gold-deep);
  border-radius: var(--r-ui);
  padding: var(--s-4);
  box-shadow: 0 18px 48px -12px rgba(0, 0, 0, .8);
  pointer-events: auto;
  transition: top .3s var(--ease), inset-inline-start .3s var(--ease);
}
.tour__n { font-size: var(--t-xs); color: var(--gold); font-variant-numeric: tabular-nums; }
.tour__t { margin: var(--s-1) 0 var(--s-2); font-size: var(--t-md); font-weight: 700; color: var(--ink-text); }
.tour__b { margin: 0 0 var(--s-4); font-size: var(--t-sm); color: var(--ink-dim); line-height: var(--lh-body); }
.tour__row { display: flex; gap: var(--s-2); justify-content: space-between; }

/* The unit is a separate column on desktop and folded into the quantity on a phone,
   so exactly one of the two is visible at any width. Showing both rendered "1 قطعة"
   next to "قطعة". */
.only-sm { display: none; }
@media (max-width: 620px) {
  .hide-sm { display: none !important; }
  .only-sm { display: inline; }
}

/* ── Site footer ───────────────────────────────────────────────────────────
   Placed in the CONTENT column, not spanning the rail. `.shell` is a two-column
   grid whose first column is the rail; without explicit placement a third grid
   item lands under the rail in column 1. At <=900px the shell collapses to one
   column and the footer follows it. */
.siteftr {
  grid-column: 2;
  border-block-start: 1px solid var(--ink-3);
  padding: var(--s-6) var(--s-7) var(--s-7);
}
@media (max-width: 900px) { .siteftr { grid-column: 1; padding-inline: var(--s-4); } }

.siteftr__in { max-width: 1360px; margin-inline: auto; }

.siteftr__brand {
  display: flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-sm); font-weight: 700; color: var(--ink-text);
}
.siteftr__brand img { opacity: .9; }

.siteftr__note {
  margin: var(--s-3) 0 0;
  font-size: var(--t-sm); color: var(--ink-dim);
  max-width: 76ch; line-height: var(--lh-body);
}

/* The demo disclosure lives here as well as on the screens that need it, so it is
   never more than one glance away wherever he happens to be looking. */
.siteftr__demo {
  margin: var(--s-3) 0 0;
  font-size: var(--t-xs); color: var(--gold-deep);
}

/* ── Per-item offers on a phone ────────────────────────────────────────────
   The offer line is price · origin · supplier · lead/warranty. On a narrow
   screen those wrap into an unreadable run, and the supplier — the thing the
   row exists to identify — ends up trailing. Give it an explicit two-line
   shape instead: the number and its origin on top, who is offering it and on
   what terms underneath. */
@media (max-width: 620px) {
  .sheet--offers .offer {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 2px var(--s-2);
    align-items: baseline;
    padding-block: var(--s-2) !important;
  }
  .sheet--offers .offer > .num { grid-column: 1; grid-row: 1; min-width: 0 !important; }
  .sheet--offers .offer > .chip { grid-column: 2; grid-row: 1; justify-self: start; }
  /* Supplier name spans the full width on its own line — it is the answer to
     "whose price is this", so it must never be the thing that gets clipped. */
  .sheet--offers .offer__who { grid-column: 1 / -1; grid-row: 2; font-weight: 500; }
  .sheet--offers .offer > .xs:last-child { grid-column: 1 / -1; grid-row: 3; }
}

/* ── Match rows on a phone ─────────────────────────────────────────────────
   The generic label/value flex row works for a number. It does not work for a
   full sentence or a wrapping list of chips — those need the label ABOVE and
   the content on its own full width, or the sentence is squeezed into half a
   column and reads worse than not being there. */
@media (max-width: 620px) {
  .sheet--match tbody tr { padding-block: var(--s-4); }

  .sheet--match td[data-label="سبب المطابقة"],
  .sheet--match td[data-label="العوامل"] {
    display: block;
    margin-block-start: var(--s-2);
  }
  .sheet--match td[data-label="سبب المطابقة"]::before,
  .sheet--match td[data-label="العوامل"]::before {
    content: attr(data-label);
    display: block;
    color: var(--paper-dim);
    font-size: var(--t-xs);
    margin-block-end: 3px;
  }
  /* The reason is the claim this screen makes. Give it room to be read. */
  .sheet--match td[data-label="سبب المطابقة"] { line-height: var(--lh-body); }
  .sheet--match .match__factors { display: block; }

  /* The score stays a label/value row — it is one number. */
  .sheet--match td[data-label="الدرجة"] {
    margin-block-start: var(--s-2);
    padding-block-start: var(--s-2);
    border-block-start: 1px dotted var(--paper-rule);
  }
}

/* ── Quote comparison rows on a phone ──────────────────────────────────────
   The supplier cell carries the name plus the win badges (الأقل سعراً and the
   rest). Those must stay with the name, not float off as an unlabelled row. */
@media (max-width: 620px) {
  .sheet--quotes tbody tr { padding-block: var(--s-4); }
  /* The award button is an action, not a field — it gets its own full-width row. */
  .sheet--quotes td:last-child {
    margin-block-start: var(--s-3);
    display: flex; justify-content: flex-end;
  }
  .sheet--quotes td:last-child::before { content: none; }
  /* Payment terms are a sentence; give them the label above rather than
     squeezing them into half a row. */
  .sheet--quotes td[data-label="شروط الدفع"] {
    display: block; margin-block-start: var(--s-2);
  }
  .sheet--quotes td[data-label="شروط الدفع"]::before {
    content: attr(data-label); display: block;
    color: var(--paper-dim); font-size: var(--t-xs); margin-block-end: 2px;
  }
  /* The gross is the number the reader is actually comparing — let it carry
     the weight it has on desktop. */
  .sheet--quotes td[data-label="شامل الضريبة"] { font-size: var(--t-base); }
}

/* A `.sheet__spec` is a qualifier on the value above it — «تركيا / متوازن»,
   «اعتماد مستندي / DDP». In the stacked label/value row it was being stretched
   by the flex layout and landing at the opposite edge from the value it
   qualifies, reading as an unrelated third column. Keep the pair together. */
@media (max-width: 620px) {
  .sheet--doc td[data-label] { flex-wrap: wrap; }
  .sheet--doc td[data-label] > .sheet__spec {
    flex-basis: 100%;
    text-align: end;
    margin-block-start: 1px;
  }

  /* `.cell-v` wraps a value together with its own qualifier — «12,840» over
     «نطاق 8,000–18,000» — so the flex row treats the pair as ONE item. The rule
     above uses a direct-child selector and deliberately does not reach inside.

     Without the margin below, the pair sits 0px from its value and 2px from the
     NEXT label/value row, and proximity groups it downward: on a phone the unit
     price's band read as the range of the line TOTAL. On a qty-3 row that is a
     total of 14,790 captioned «3,000–7,500», which is not a rounding quibble —
     it is the wrong fact. Gaps decide what belongs to what. */
  .sheet--doc td[data-label] .cell-v { display: block; }
  .sheet--doc td[data-label] .cell-v .sheet__spec { margin-block-end: var(--s-2); }
}

/* On a phone there is no reliably empty region for a 104px seal.
   Anchored to the bottom it covered «تمت الترسية على هذه الحزمة» — 2,377 px² measured, the
   seal celebrating the award obscuring the sentence confirming it. Moved to the top edge it
   covered the package title instead, 4,272 px², because the header wraps at this width so
   the title spans the line the stamp was aiming at.
   So: stop absolutely positioning it. In the flow it reads as a seal at the foot of the
   document — which is where one goes — and it cannot overlap anything by construction.

   That reasoning turned out to hold at every width, so the desktop seal now also lives in
   the footer. Here it simply stays in flow as a flex item; no reserved band is needed
   because the footer wraps to one column at this size. */
@media (max-width: 620px) {
  .stamp {
    --stamp-size: 104px;
    transform: rotate(-9deg);
  }
  @keyframes stamp-land {
    0%   { opacity: 0; transform: rotate(-9deg) scale(2.2); }
    70%  { opacity: .95; transform: rotate(-9deg) scale(.95); }
    100% { opacity: .82; transform: rotate(-9deg) scale(1); }
  }
}

/* ── Route marks ───────────────────────────────────────────────────────────
   Line-work from the industry's own objects, one per supply-side route. They
   carry no colour of their own — `currentColor` means a mark can never
   introduce a hue outside his gold ramp, and it picks up the tile's state for
   free. Sized to the cap height of the title beside it so the pair reads as
   one line rather than as an icon with a caption bolted on. */
.mark-slot {
  flex: none;
  display: block;
  color: var(--gold-deep);
  margin-block-start: 1px;
  transition: color var(--d-fast) var(--ease);
}
.mark-slot .mark { display: block; }
.panel:hover .mark-slot,
[data-arch][aria-pressed='true'] .mark-slot { color: var(--gold-lit); }
