@charset "UTF-8";
/* ===========================================================================
   ROOT — design system
   ---------------------------------------------------------------------------
   Every colour in this file is taken from the logo (/static/img/logo.png):
   a deep bark-green field with a bone-cream wordmark, a campfire flame and a
   root system. Nothing is invented — the greens are tints and shades of the
   logo's own background (#2C2F1C), the creams are tints of its artwork
   (#E3D5BD), and the single warm accent is pulled from the flame inside the
   tent and the red expedition route on the vintage map in the hero.

   The tone the layout aims for is printed travel documentary, not dashboard:
   hairline rules instead of drop shadows, generous whitespace, flat surfaces,
   one accent used sparingly.

   Sections
     01  Fonts
     02  Tokens
     03  Reset & base type
     04  Layout primitives
     05  Buttons & badges
     06  Forms
     07  Header
     08  Footer
     09  Cinematic map hero
     10  Home / marketing sections
     11  Cards & grids
     12  Detail pages
     13  Tables, panels & dashboards
     14  Auth pages
     15  Feedback
     16  Overlays
     17  Documents, gallery, blog
     18  Telegram board
     19  Utilities, motion & print
   =========================================================================== */


/* == 01  Fonts ============================================================= */
/* Vazirmatn is self-hosted (public/fonts) rather than loaded from a CDN: the
   audience is Farsi-first and third-party font hosts are unreliable on
   Iranian networks. One variable file covers weights 100–900 for both the
   Persian and the Latin text, so the whole site costs a single ~110 KB
   request. */
@font-face {
  font-family: 'Vazirmatn';
  src: url('/static/fonts/Vazirmatn-var.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}


/* == 02  Tokens ============================================================ */
:root {
  /* --- bark: the logo's field colour, as a full ramp -------------------- */
  --bark-950: #101208;
  --bark-900: #1a1d10;
  --bark-800: #232717;
  --bark-700: #2c2f1c;   /* the logo background, exactly */
  --bark-600: #383d25;
  --bark-500: #4a5131;
  --bark-400: #626a41;
  --bark-300: #838c5c;
  --bark-200: #a8b083;

  /* --- bone: the logo's wordmark, as a full ramp ------------------------ */
  --bone-50:  #fdfbf6;
  --bone-100: #f7f3ea;
  --bone-200: #efe8d9;
  --bone-300: #e3d5bd;   /* the logo artwork, exactly */
  --bone-400: #cdbb9c;
  --bone-500: #ab9a7c;
  --bone-600: #7d7059;

  /* --- ember: the campfire inside the tent, and the map's route red ----- */
  --ember-600: #a8552b;
  --ember-500: #c06a33;
  --ember-400: #d4884f;
  --route-red: #9e3b2e;

  /* --- semantic --------------------------------------------------------- */
  --brand:      var(--bark-700);
  --brand-deep: var(--bark-900);
  --brand-soft: var(--bark-400);
  --accent:     var(--ember-600);

  --ink:      #22241a;
  --ink-soft: #4d5142;
  --muted:    #767a68;
  --paper:    var(--bone-100);
  --surface:  #ffffff;
  --surface-sunk: var(--bone-200);
  --line:        #e0d8c6;
  --line-strong: #cdc2a9;

  --ok:        #3f6b4a;
  --ok-bg:     #e9f1e9;
  --danger:    #a3352a;
  --danger-bg: #fbeae7;
  --warn:      var(--ember-600);
  --warn-bg:   #fcf1e7;

  /* --- shape & rhythm --------------------------------------------------- */
  --radius:      6px;
  --radius-sm:   4px;
  --radius-lg:   12px;
  --radius-pill: 999px;

  --gap: 1rem;
  --max-width: 1220px;
  --max-text: 68ch;

  --shadow-sm: 0 1px 2px rgba(26, 29, 16, .05);
  --shadow-md: 0 6px 22px -10px rgba(26, 29, 16, .28);
  --shadow-lg: 0 26px 60px -24px rgba(26, 29, 16, .45);

  --header-h: 68px;

  --font: 'Vazirmatn', 'IRANSans', 'Segoe UI', Tahoma, system-ui, -apple-system,
          'Noto Sans Arabic', sans-serif;
  /* Hierarchy comes from weight and tracking rather than a second download. */
  --font-display: var(--font);
  --font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;

  --ease: cubic-bezier(.22, .61, .36, 1);
}


/* == 03  Reset & base type ================================================= */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: 16px;
  color: var(--ink);
  background: var(--paper);
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

body.rtl { direction: rtl; }
body.ltr { direction: ltr; }

/* An interface full of prices and dates reads better with tabular figures. */
.table, .price, .price-lg, .stat-row strong, .support-phone,
.leader-head-stats strong, td, th { font-variant-numeric: tabular-nums; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  line-height: 1.22;
  margin: 0 0 .5em;
  color: var(--brand-deep);
  font-weight: 700;
  letter-spacing: -.005em;
}
h1 { font-size: clamp(1.65rem, 1.2rem + 1.8vw, 2.4rem); }
h2 { font-size: clamp(1.25rem, 1.05rem + .8vw, 1.6rem); margin-top: 2.2rem; }
/* h3 had no top margin at all, so a subheading following a table, a card or an
   empty-state box sat flush against it — "Assign leader" was touching the box
   above it. Less standoff than an h2, since it opens a smaller division. */
h3 { font-size: 1.08rem; margin-top: 1.8rem; }
/* …but a heading that opens its container needs none: the padding is there. */
h2:first-child, h3:first-child { margin-top: 0; }
h4 { font-size: .95rem; }

p { margin: 0 0 1em; }

a { color: var(--bark-600); text-underline-offset: .18em; text-decoration-thickness: 1px; }
a:hover { color: var(--accent); }

hr { border: 0; border-top: 1px solid var(--line); margin: 2rem 0; }

::selection { background: var(--bark-700); color: var(--bone-200); }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm); }

img, video, svg { max-width: 100%; }

code, pre, kbd { font-family: var(--font-mono); font-size: .88em; }
code { background: var(--surface-sunk); padding: .1em .35em; border-radius: var(--radius-sm); }


/* == 04  Layout primitives ================================================= */
.container { width: min(100% - 2.5rem, var(--max-width)); margin-inline: auto; }
.container-narrow { width: min(100% - 2.5rem, 780px); margin-inline: auto; }

.section { padding-block: clamp(2.25rem, 4vw, 4rem); }
.section-tight { padding-block: 1.75rem; }
.narrow { max-width: 480px; }

.muted { color: var(--muted); }
.lead { font-size: 1.09rem; color: var(--ink-soft); max-width: var(--max-text); }

/* A small letterspaced label above a heading — the documentary caption. */
.eyebrow {
  display: block;
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: .24em;
  text-transform: uppercase;
  color: var(--brand-soft);
  margin-bottom: .7rem;
}
/* Persian script must never be letterspaced or uppercased — the letters would
   stop joining. Every tracked style is reset inside the RTL tree. */
body.rtl .eyebrow,
body.rtl .scroll-cue,
body.rtl .btn,
body.rtl .panel-btn-label,
body.rtl .footer-legal,
body.rtl .stat-row span,
body.rtl .fact-grid span,
body.rtl .table th {
  letter-spacing: 0;
  text-transform: none;
}
body.rtl .eyebrow { font-size: .84rem; }

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.4rem;
  padding-bottom: .9rem;
  border-bottom: 1px solid var(--line);
}
.section-head h2 { margin: 0; }
.section-head > a {
  font-size: .88rem; font-weight: 600; text-decoration: none;
  color: var(--brand-soft); white-space: nowrap;
}
.section-head > a:hover { color: var(--accent); }

.stack-sm { display: grid; gap: .5rem; }
.stack-md { display: grid; gap: 1rem; }
.site-main { min-height: 55vh; }

.skip-link {
  position: absolute; inset-inline-start: 1rem; top: -100px; z-index: 200;
  background: var(--bark-800); color: var(--bone-200);
  padding: .6rem 1rem; border-radius: var(--radius); text-decoration: none;
  transition: top .18s var(--ease);
}
.skip-link:focus { top: 1rem; color: var(--bone-100); }


/* == 05  Buttons & badges ================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .45rem;
  padding: .62rem 1.25rem;
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  font: inherit;
  font-size: .9rem;
  font-weight: 600;
  line-height: 1.4;
  background: none;
  white-space: nowrap;
  transition: background-color .18s var(--ease), color .18s var(--ease),
              border-color .18s var(--ease), transform .12s var(--ease);
}
.btn:active { transform: translateY(1px); }
.btn[disabled], .btn:disabled { opacity: .45; cursor: not-allowed; transform: none; }

.btn-primary { background: var(--bark-700); color: var(--bone-200); }
.btn-primary:hover { background: var(--bark-600); color: var(--bone-100); }

.btn-accent { background: var(--ember-600); color: #fff; }
.btn-accent:hover { background: var(--ember-500); color: #fff; }

.btn-ghost { border-color: var(--line-strong); color: var(--ink); background: transparent; }
.btn-ghost:hover { border-color: var(--bark-700); background: var(--surface); color: var(--brand-deep); }

.btn-quiet { color: var(--ink-soft); background: var(--surface-sunk); }
.btn-quiet:hover { background: var(--bone-300); color: var(--brand-deep); }

.btn-link { color: var(--muted); padding-inline: .35rem; font-weight: 500; border-radius: var(--radius-sm); }
.btn-link:hover { color: var(--accent); }

.btn-success { background: var(--ok); color: #fff; }
.btn-success:hover { background: #35573c; color: #fff; }

.btn-danger { background: var(--danger); color: #fff; }
.btn-danger:hover { filter: brightness(1.1); }

.btn-sm { padding: .34rem .8rem;  font-size: .82rem; }
.btn-lg { padding: .82rem 1.7rem; font-size: 1rem; }
.btn-block { display: flex; width: 100%; }

.badge, .tag {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  background: var(--surface-sunk);
  color: var(--ink-soft);
  border-radius: var(--radius-pill);
  padding: .16rem .7rem;
  font-size: .76rem;
  font-weight: 600;
  line-height: 1.6;
  white-space: nowrap;
}
.tag-row { display: flex; gap: .4rem; flex-wrap: wrap; padding: 0; margin: 0; list-style: none; }

/* Settled, half-way, nothing yet. Used by the payments panel and the payout
   and leader tables, which asked for these three and had been getting the
   plain grey tag because they were never defined. */
.tag-ok     { background: #e2ece2; color: #2f5136; }
.tag-warn   { background: #f6ead2; color: #7a5a16; }
.tag-danger { background: #f3dcda; color: #8a3128; }

.badge-difficulty {
  position: absolute;
  inset-inline-start: .7rem;
  inset-block-start: .7rem;
  background: rgba(26, 29, 16, .78);
  color: var(--bone-200);
  backdrop-filter: blur(6px);
}
.badge-warn  { background: var(--warn-bg);      color: var(--ember-600); }
.badge-info  { background: var(--surface-sunk); color: var(--bark-600); }
.badge-paid  { background: var(--ok-bg);        color: var(--ok); }
.badge-live  { background: var(--ok-bg);        color: var(--ok); }
.badge-draft { background: var(--surface-sunk); color: var(--muted); }

/* --- tour status, said loudly -------------------------------------------
   An admin scanning the tours table is looking for one thing: which trips are
   live, which are still drafts, and which fell over. One grey pill for all six
   made that a read rather than a glance, so status now carries weight and a
   colour of its own — filled rather than tinted, because this is the word the
   eye should land on first in the row.

   Colour is a second signal on top of the word, never the only one, so the
   table still works in greyscale and for a colour-blind reader. */
.badge-status {
  font-weight: 700;
  font-size: .78rem;
  letter-spacing: .02em;
  padding: .24rem .85rem;
  border: 1px solid transparent;
}
.badge-status.status-DRAFT     { background: var(--bone-300);  color: var(--bark-800); border-color: var(--line-strong); }
.badge-status.status-PUBLISHED { background: var(--ok);        color: #fff; }
.badge-status.status-FULL      { background: var(--bark-600);  color: var(--bone-100); }
.badge-status.status-ONGOING   { background: var(--ember-600); color: #fff; }
.badge-status.status-COMPLETED { background: var(--bark-200);  color: var(--bark-900); }
.badge-status.status-CANCELLED { background: var(--danger);    color: #fff; }

/* --- approval state ------------------------------------------------------
   Three states, and the one that matters is "still waiting" — an expense
   nobody has looked at counts for nothing, and that has to be visible without
   reading the word. */
.badge-approval { font-weight: 700; }
.badge-approval.approval-PENDING  { background: var(--warn-bg); color: var(--ember-600); }
.badge-approval.approval-APPROVED { background: var(--ok-bg);   color: var(--ok); }
.badge-approval.approval-REJECTED { background: var(--danger-bg); color: var(--danger); }

/* A destructive action in a row of ordinary links, without shouting until it
   is reached for. */
.btn-danger-link { color: var(--danger); background: none; }
.btn-danger-link:hover { color: var(--danger); text-decoration: underline; }

/* A line of guidance that belongs to a pair of fields rather than to one, so
   it cannot live inside either label. */
.field-note {
  margin: -.35rem 0 .2rem;
  font-size: .85rem;
  color: var(--ink-soft);
  background: var(--surface-sunk);
  border-radius: var(--radius);
  padding: .55rem .8rem;
}

/* --- category chips ------------------------------------------------------
   One tint per landscape, mixed from the same bark/bone/ember ramp as the rest
   of the site so the table still reads as one page. The text stays dark on
   every one of them: the background is doing the sorting, not the shouting. */
.chip {
  display: inline-flex;
  align-items: center;
  border-radius: var(--radius-pill);
  padding: .16rem .7rem;
  font-size: .76rem;
  font-weight: 600;
  line-height: 1.6;
  white-space: nowrap;
  background: var(--surface-sunk);
  color: var(--ink-soft);
}
.chip-cat { border: 1px solid rgba(26, 29, 16, .08); color: var(--bark-900); }
.chip-cat.cat-CAMPING  { background: #e6dcc4; }
.chip-cat.cat-HIKING   { background: #d9e2d0; }
.chip-cat.cat-TREKKING { background: #cfd9c2; }
.chip-cat.cat-DESERT   { background: #f0dcc0; }
.chip-cat.cat-ISLAND   { background: #cfe3e2; }
.chip-cat.cat-SEA      { background: #c9dee8; }
.chip-cat.cat-SPRING   { background: #dcecd2; }
.chip-cat.cat-WINTER   { background: #dde4ec; }
.chip-cat.cat-CANYONING { background: #cde6e0; }
/* A tour carries several categories now, so the chips need to sit apart and to
   wrap rather than stretch the row. A custom category matches no `cat-` rule
   above and simply keeps the neutral chip background. */
.chip-cat { margin: .1rem .15rem .1rem 0; display: inline-block; }


/* == 06  Forms ============================================================= */
input, select, textarea, button { font: inherit; color: inherit; }

input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
select, textarea {
  width: 100%;
  padding: .62rem .8rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  transition: border-color .16s var(--ease), box-shadow .16s var(--ease);
}
input::placeholder, textarea::placeholder { color: #a9a795; }

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--bark-500);
  box-shadow: 0 0 0 3px rgba(74, 81, 49, .13);
}
input[aria-invalid="true"] { border-color: var(--danger); }

select {
  appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%237d7059' stroke-width='1.6' d='M1 1.5 6 6.5l5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .8rem center;
  background-size: 11px;
  padding-inline-end: 2.1rem;
}
body.rtl select {
  background-position: left .8rem center;
  padding-inline-end: .8rem;
  padding-inline-start: 2.1rem;
}

textarea { min-height: 6rem; resize: vertical; }

.form { display: grid; gap: 1rem; }
/**
 * A field: its caption on one line, its control on the next.
 *
 * The caption is not always just a name. A price carries its currency ("IRT"),
 * a coupon value carries a note about what the number means, and a stacking
 * layout gave each of those its own line — which pushed that field's input
 * below the inputs beside it and broke the row. Wrapping keeps the caption and
 * its note together on the first line and gives the control a line of its own,
 * so every field in a row is the same height and the inputs line up.
 */
.form label {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: .35rem .45rem; font-size: .88rem; font-weight: 500; color: var(--ink-soft);
}
.form label > input, .form label > select, .form label > textarea { font-weight: 400; color: var(--ink); }
/* The control, and anything reported underneath it, take a full line. */
.form label > input:not([type="checkbox"]):not([type="radio"]),
.form label > select,
.form label > textarea,
.form label > small.error { flex: 0 0 100%; }
.form small.muted { font-weight: 400; font-size: .78rem; }

.form-grid { display: grid; gap: 1rem 1.1rem; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.form-grid .span-2 { grid-column: 1 / -1; }
/**
 * The same field, in a row of them.
 *
 * Every cell is stretched to the height of the tallest one in its row, so what
 * decides whether the controls line up is where the spare height goes. It goes
 * underneath: caption, then control, then anything the field explains about
 * itself. A field carrying a note below its control — "lower numbers appear
 * first" — is taller than its neighbours, and packing to the bottom instead
 * would lift that field's control a line above theirs.
 */
.form-grid label { align-content: flex-start; }

/**
 * The button that ends the form.
 *
 * Set further from the last row of fields than those rows are from each other,
 * so it reads as the end of the form rather than as one more thing to fill in
 * — and so it is not left sitting against the hint under the field above it.
 */
.form-grid > .form-actions,
.form-grid > div:has(> button[type="submit"]) { margin-block-start: 1rem; }

/**
 * What already exists, below the form that adds to it.
 *
 * The gap has to be wider than anything inside the form, or the submit button
 * reads as belonging to the table it is sitting on top of rather than to the
 * fields above it.
 */
form.form + .table,
form.form + .table-scroll,
form.form + .empty-state,
form.form + .filter-bar,
form.form + h2 { margin-block-start: 2.4rem; }

/**
 * A tick box is a line, not a column.
 *
 * `.form label` and `.form-grid label` both outrank `.checkbox` on their own,
 * which turned every checkbox inside a form into a stack — the box on one row
 * and the words it belongs to on the next. Restated here so the box and its
 * label stay side by side, and so the row is not stretched to the height of
 * whatever text field happens to sit next to it.
 */
.form label.checkbox,
.form-grid label.checkbox {
  display: flex; flex-direction: row; flex-wrap: nowrap; align-items: center;
  gap: .5rem; align-self: center; justify-content: flex-start;
}
.form label.checkbox > input,
.form-grid label.checkbox > input { width: auto; flex: 0 0 auto; }

/**
 * A single control given the top of a form to itself.
 *
 * Full width so it reads as a header rather than as the first of a row of
 * fields, but the control keeps a sensible width instead of stretching across
 * the page — a select as wide as the form looks like a mistake.
 */
.form-toprow {
  padding-block-end: .9rem;
  border-block-end: 1px solid var(--line);
  margin-block-end: .3rem;
}
.form-toprow label { max-width: 16rem; }

.form-section {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  padding: 1.4rem 1.5rem;
  margin-block: 1.4rem;
}
.form-section > h2 {
  margin: 0 0 1.1rem;
  padding-bottom: .7rem;
  font-size: 1rem;
  border-bottom: 1px solid var(--line);
}
/**
 * A note inside a section, in either of the two places one turns up.
 *
 * The pull-up belongs only to an intro line sitting under the heading, where
 * it closes the gap the h2's own bottom margin leaves. A note *after* the
 * fields — "your password is stored as a hash" — was inheriting that negative
 * margin too and being dragged up into the last row of inputs, printing itself
 * across their bottom borders.
 */
.form-section > p.muted { font-size: .86rem; margin-block: .9rem 0; }
.form-section > h2 + p.muted { margin-top: -.6rem; }

.form-panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: .9rem 1.2rem;
  margin-block: 1rem;
}
/* :not(.btn) — a summary wearing a button carries its own colour, and this
   rule would otherwise outrank it on specificity alone. */
.form-panel > summary:not(.btn) { cursor: pointer; font-weight: 600; color: var(--brand-deep); }
.form-panel > summary:not(.btn)::marker { color: var(--brand-soft); }
.form-panel > .form { margin-top: 1.1rem; }

/**
 * A create form behind a disclosure, on a page whose sibling pages put the
 * same action behind a "new X" button.
 *
 * Closed, it is that button and nothing else — no panel drawn around it — so
 * /admin/leaders opens looking like /admin/tours. Open, the panel comes back
 * to hold the form it just revealed, with the button sitting on top of it.
 */
.form-panel.panel-action { border: 0; background: none; padding: 0; }
.form-panel.panel-action > summary.btn { list-style: none; }
.form-panel.panel-action > summary.btn::-webkit-details-marker { display: none; }
.form-panel.panel-action[open] {
  border: 1px solid var(--line);
  background: var(--surface);
  padding: .9rem 1.2rem;
}
.form-panel.panel-action[open] > summary.btn { margin-block-end: .9rem; }

.filter-bar {
  display: flex;
  gap: .6rem;
  flex-wrap: wrap;
  align-items: center;
  margin-block: 1.2rem 1.8rem;
  padding: .85rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.filter-bar input, .filter-bar select { width: auto; min-width: 170px; flex: 1 1 170px; }
/* Checkboxes are not text fields: they must not inherit the 170px flex basis,
   or the box drifts away from its own label. */
.filter-bar input[type="checkbox"],
.filter-bar input[type="radio"] { flex: 0 0 auto; min-width: 0; width: 1.05em; }
.filter-bar .checkbox { flex: 0 0 auto; }
.filter-bar .btn { flex: 0 0 auto; }

.checkbox {
  display: flex; align-items: center; gap: .5rem;
  font-size: .88rem; font-weight: 500; color: var(--ink-soft);
}
.checkbox input { width: auto; flex: 0 0 auto; }
input[type="checkbox"], input[type="radio"] { width: 1.05em; height: 1.05em; accent-color: var(--bark-600); }

.consent-list { display: grid; gap: .8rem; margin-block: 1.4rem; }
.consent-list label {
  /* nowrap: the tick box stays beside its sentence however long the sentence
     runs, rather than being pushed onto a line of its own. */
  display: flex; flex-wrap: nowrap; align-items: flex-start; gap: .6rem;
  font-size: .88rem; font-weight: 500; color: var(--ink-soft);
}
.consent-list input { width: auto; flex: 0 0 auto; margin-top: .38rem; }

small.error, .error { color: var(--danger); font-weight: 500; }
small.error { display: block; font-size: .78rem; }

.row-actions { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }
.row-actions form { display: inline; }
/**
 * A form that lives inside a table cell: a few controls and the button that
 * submits them, on one line.
 *
 * `display: inline` left the text fields at their full 100% width, so the
 * button was pushed onto its own line and sat against the bottom edge of the
 * field above it. A wrapping flex row keeps the button beside its input, and
 * only wraps when the cell genuinely runs out of room.
 */
.inline-form {
  display: flex; flex-wrap: wrap; gap: .45rem; align-items: center;
}
.inline-form input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]),
.inline-form select { width: auto; flex: 1 1 8.5rem; min-width: 7rem; }
.inline-form .btn { flex: 0 0 auto; }

.order-form { display: flex; gap: .3rem; align-items: center; }
.order-form input { width: 4.6rem; padding: .28rem .45rem; }
.order-form .btn-link { padding: 0 .25rem; font-size: .8rem; }

.upload-form { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; margin-top: .7rem; }
.upload-form input[type="file"] { width: auto; flex: 1 1 170px; }
.upload-hint { font-size: .78rem; flex: 0 0 auto; }

/* The tour media page reads top to bottom as: how full the tour is → add to
   it → what is already there. The upload control belongs with the allowance
   boxes above it, so its own gap is closed and the breathing room is spent on
   the separation from the grid below. */
/* Compound selectors on purpose: `.stat-row` and `.muted` set their own
   margins further down this file, and a single-class rule up here would lose
   to them on source order alone. */
.stat-row.gallery-usage { margin-block-end: .55rem; }
.upload-form.gallery-upload { margin-top: 0; margin-block-end: 1.5rem; }
p.gallery-upload { margin-block: 0 1.5rem; }
form.story-delete { margin-block-end: .8rem; }
input[type="file"] { font-size: .86rem; }


/* == 07  Header ============================================================ */
/* A bark-green bar carrying the cream logo: the logo's own field colour, so
   the wordmark sits on the background it was drawn for. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--bark-800);
  color: var(--bone-200);
  border-bottom: 1px solid rgba(227, 213, 189, .12);
}
.header-inner {
  display: flex;
  align-items: center;
  gap: 1.4rem;
  min-height: var(--header-h);
  padding-block: .6rem;
}

.brand { display: inline-flex; align-items: center; gap: .55rem; text-decoration: none; color: var(--bone-200); flex: 0 0 auto; }
.brand-logo { display: block; height: 30px; width: auto; }
.brand-name { font-weight: 700; font-size: 1.05rem; }
/* The wordmark image already says ROOT, so the text label is for assistive
   technology only when the image is present. */
.brand-logo + .brand-name {
  position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

.main-nav { display: flex; gap: .3rem; flex: 1; flex-wrap: wrap; }
.main-nav a {
  position: relative;
  text-decoration: none;
  color: rgba(227, 213, 189, .74);
  font-size: .9rem;
  font-weight: 500;
  padding: .45rem .7rem;
  border-radius: var(--radius);
  transition: color .16s var(--ease), background-color .16s var(--ease);
}
.main-nav a:hover { color: var(--bone-100); background: rgba(227, 213, 189, .08); }
.main-nav a.active { color: var(--bone-100); font-weight: 600; }
.main-nav a.active::after {
  content: '';
  position: absolute;
  inset-inline: .7rem;
  bottom: .12rem;
  height: 2px;
  border-radius: 2px;
  background: var(--ember-500);
}

.header-actions { display: flex; align-items: center; gap: .55rem; flex: 0 0 auto; }
.header-actions .btn-ghost { border-color: rgba(227, 213, 189, .32); color: var(--bone-200); }
.header-actions .btn-ghost:hover { border-color: var(--bone-300); background: rgba(227, 213, 189, .1); color: var(--bone-100); }
.header-actions .btn-primary { background: var(--bone-300); color: var(--bark-800); }
.header-actions .btn-primary:hover { background: var(--bone-200); color: var(--bark-900); }
.header-actions .btn-link { color: rgba(227, 213, 189, .66); }
.header-actions .btn-link:hover { color: var(--bone-100); }

.lang-switch {
  display: inline-flex; align-items: center; gap: .1rem; padding: .12rem;
  border: 1px solid rgba(227, 213, 189, .22);
  border-radius: var(--radius-pill);
}
/**
 * The two halves of the switch, each centred in its own pill.
 *
 * `min-width` because FA and EN are not the same width, and without it the two
 * pills were different sizes — the active one visibly wider or narrower than
 * its neighbour depending on which language you were reading in.
 *
 * The tracking is gone rather than reduced: letter-spacing appends its gap
 * *after* the final letter, so a centred two-letter label sits half a space
 * left of true centre. Compensating for that with an indent would have to flip
 * with the page direction; at this size the tracking bought nothing worth that.
 */
.lang-switch a {
  text-decoration: none; font-size: .72rem; font-weight: 600;
  color: rgba(227, 213, 189, .62);
  padding: .18rem .45rem; border-radius: var(--radius-pill); line-height: 1.5;
  display: inline-block; min-width: 2.4rem; text-align: center;
}
.lang-switch a:hover { color: var(--bone-100); }
.lang-switch a.active { background: var(--bone-300); color: var(--bark-800); }

.nav-toggle {
  display: none;
  width: 42px; height: 42px;
  align-items: center; justify-content: center;
  background: none;
  border: 1px solid rgba(227, 213, 189, .28);
  border-radius: var(--radius);
  color: var(--bone-200);
  cursor: pointer;
  flex: 0 0 auto;
}
.nav-toggle svg { width: 20px; height: 20px; }
.nav-toggle .icon-close { display: none; }
.site-header.nav-open .nav-toggle .icon-close { display: block; }
.site-header.nav-open .nav-toggle .icon-open  { display: none; }

/* Role-coloured panel button: admin, leader and customer each get their own
   surface so nobody has to guess which panel they are about to open. */
.panel-btn {
  display: inline-flex; align-items: center; gap: .55rem;
  padding: .42rem .9rem;
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  text-decoration: none; line-height: 1.25;
  transition: filter .14s var(--ease), transform .12s var(--ease);
}
.panel-btn:hover { filter: brightness(1.08); transform: translateY(-1px); }
.panel-btn-icon { font-size: 1.15rem; line-height: 1; }
.panel-btn-text { display: grid; }
.panel-btn-label { font-size: .85rem; font-weight: 700; }
.panel-btn-name { font-size: .72rem; opacity: .8; }

.panel-btn-admin    { background: var(--bone-300); color: var(--bark-800); }
.panel-btn-leader   { background: var(--ember-600); color: #fff; }
.panel-btn-customer { background: rgba(227, 213, 189, .14); color: var(--bone-200); border-color: rgba(227, 213, 189, .3); }

@media (max-width: 1000px) {
  .nav-toggle { display: inline-flex; }
  .header-inner { flex-wrap: wrap; gap: .7rem; }
  .main-nav {
    order: 10;
    flex-basis: 100%;
    flex-direction: column;
    gap: 0;
    display: none;
    border-top: 1px solid rgba(227, 213, 189, .14);
    padding-block: .5rem;
  }
  .site-header.nav-open .main-nav { display: flex; }
  .main-nav a { padding: .6rem .3rem; }
  .main-nav a.active::after { inset-inline: .3rem auto; width: 18px; }
  .header-actions { margin-inline-start: auto; }
}
@media (max-width: 620px) {
  .panel-btn-name { display: none; }
  .header-actions { gap: .35rem; }
  .header-actions .btn { padding: .5rem .85rem; font-size: .84rem; }
  .brand-logo { height: 26px; }
}


/* == 08  Footer ============================================================ */
.site-footer { margin-top: 4rem; background: var(--bark-800); color: rgba(227, 213, 189, .74); }
/**
 * …except under the split auth pages.
 *
 * Those are full-bleed coloured panels that run to the bottom of the page, so
 * the footer's usual standoff read as a band of stray paper between the green
 * column and the green footer rather than as breathing room. The two are the
 * same colour; they should meet.
 */
.site-main:has(.auth) + .site-footer { margin-top: 0; }
.site-footer a { color: rgba(227, 213, 189, .74); text-decoration: none; }
.site-footer a:hover { color: var(--bone-100); }
.site-footer strong { color: var(--bone-200); font-weight: 600; }
.site-footer .muted { color: rgba(227, 213, 189, .55); }

.footer-inner {
  display: grid;
  gap: 2.2rem;
  grid-template-columns: minmax(240px, 1.4fr) repeat(auto-fit, minmax(190px, 1fr));
  align-items: start;
  padding-block: 3rem 2.2rem;
}
.footer-brand { max-width: 340px; }
.footer-logo { display: block; height: 34px; width: auto; margin-bottom: .9rem; }
.footer-brand p { font-size: .89rem; margin: 0; line-height: 1.75; }

.footer-col-title {
  font-size: .72rem; font-weight: 700; letter-spacing: .2em; text-transform: uppercase;
  color: rgba(227, 213, 189, .5); margin-bottom: .9rem;
}
body.rtl .footer-col-title { letter-spacing: 0; text-transform: none; font-size: .82rem; }

.footer-nav { display: grid; gap: .55rem; }
.footer-nav a, .footer-link-btn { font-size: .89rem; }

.support-list { list-style: none; padding: 0; margin: 0; display: grid; gap: .9rem; }
.support-list li { display: grid; gap: .1rem; font-size: .86rem; }
.support-service { font-weight: 600; color: var(--bone-200); }
.support-phone { letter-spacing: .03em; direction: ltr; text-align: start; font-weight: 600; }

.footer-bottom {
  display: flex; flex-wrap: wrap; gap: .5rem 1.4rem;
  justify-content: space-between; align-items: center;
  border-top: 1px solid rgba(227, 213, 189, .13);
  padding-block: 1.2rem;
}
.footer-bottom p { margin: 0; font-size: .82rem; }

.footer-link-btn {
  background: none; border: 0; padding: 0; cursor: pointer;
  color: rgba(227, 213, 189, .74); font: inherit; font-size: .89rem; text-align: start;
}
.footer-link-btn:hover { color: var(--bone-100); }

@media (max-width: 720px) {
  .footer-inner { grid-template-columns: 1fr; gap: 1.9rem; padding-block: 2.2rem 1.6rem; }
}


/* == 09  Cinematic map hero ================================================
   Layout contract — the JS in /static/js/map-hero.js owns the timeline:

     .map-hero        the tall scroll track. Its height comes from the
                      --map-hero-scroll custom property, so the length of the
                      sequence is tunable without touching the JS.
     .map-hero-stage  a position:sticky viewport pinned for the whole track.
                      This is what makes the page feel locked to the scene.
     .map-hero-video  the single <video>. Never played, never recreated.
     .map-hero-poster the FIRST FRAME, painted underneath so there is no white
                      or black flash before the video metadata arrives.

   Nothing here animates on its own. Every visual change is driven by the
   scroll position through the --progress / --intro / --outro / --cue custom
   properties the JS writes.
   ------------------------------------------------------------------------ */
.map-hero {
  position: relative;
  height: var(--map-hero-scroll, 500vh);
  background: var(--bark-900);
  /* pull the sticky stage up under the header so the map is full-bleed */
  margin-top: calc(var(--header-h) * -1);
}

.map-hero-stage {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  background: #0d1006;
}

/* Poster and video share the same box, so revealing the video is a pure
   opacity change with no reflow. object-fit:cover crops the frame — it never
   stretches it, so the map keeps its aspect ratio. */
.map-hero-poster,
.map-hero-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 45%;
  display: block;
}
.map-hero-poster { z-index: 1; transition: opacity .5s var(--ease); }
.map-hero-video  { z-index: 2; opacity: 0; transition: opacity .5s var(--ease); pointer-events: none; }

/* Set by the JS once the video can actually paint a frame. */
.map-hero.is-ready .map-hero-video  { opacity: 1; }
.map-hero.is-ready .map-hero-poster { opacity: 0; }

/* Vignette so the antique map reads as printed rather than as a screenshot,
   and so the overlay type stays legible over any frame. */
.map-hero-veil {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background:
    /* a soft pool behind the title block, so the type never fights the map */
    radial-gradient(46% 34% at 50% 46%, rgba(16, 18, 8, .58), transparent 72%),
    radial-gradient(120% 85% at 50% 42%, transparent 40%, rgba(16, 18, 8, .55) 100%),
    linear-gradient(to bottom, rgba(16, 18, 8, .55) 0%, transparent 24%,
                                transparent 60%, rgba(16, 18, 8, .78) 100%);
}

.map-hero-overlay {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: grid;
  align-content: center;
  justify-items: center;
  text-align: center;
  padding: 0 1.5rem;
  color: var(--bone-100);
  pointer-events: none;
}
.map-hero-overlay > * { pointer-events: auto; }

/* The title block fades out as the camera starts moving. */
.map-hero-intro {
  opacity: var(--intro, 1);
  transform: translateY(calc((1 - var(--intro, 1)) * -18px));
  transition: opacity .12s linear;
  display: grid;
  justify-items: center;
  gap: .4rem;
  max-width: min(92vw, 46rem);
}
.map-hero-eyebrow {
  font-size: .72rem; font-weight: 600; letter-spacing: .38em; text-transform: uppercase;
  color: var(--bone-300); text-shadow: 0 1px 12px rgba(16, 18, 8, .8);
}
body.rtl .map-hero-eyebrow { letter-spacing: .06em; text-transform: none; font-size: .84rem; }

.map-hero-title {
  font-family: var(--font-display);
  font-size: clamp(2rem, 1rem + 4.4vw, 4.1rem);
  font-weight: 300;
  line-height: 1.12;
  margin: 0;
  color: var(--bone-100);
  text-shadow: 0 2px 30px rgba(16, 18, 8, .75);
  text-wrap: balance;
}
.map-hero-title strong { font-weight: 700; }
/* Persian sets wider than Latin at the same size, so the display size is
   trimmed under dir="rtl" to keep the headline on two lines. */
body.rtl .map-hero-title { font-size: clamp(1.75rem, .9rem + 3.6vw, 3.4rem); }

.map-hero-sub {
  margin: .3rem 0 0;
  font-size: clamp(.9rem, .82rem + .3vw, 1.02rem);
  color: rgba(247, 243, 234, .8);
  text-shadow: 0 1px 16px rgba(16, 18, 8, .8);
  max-width: 46ch;
}

/* The route caption arrives near the end of the sequence. */
.map-hero-outro {
  position: absolute;
  inset-block-end: clamp(4.5rem, 11vh, 7.5rem);
  opacity: var(--outro, 0);
  transform: translateY(calc((1 - var(--outro, 0)) * 14px));
  transition: opacity .12s linear;
  display: grid;
  justify-items: center;
  gap: .45rem;
}
.map-hero-route {
  display: inline-flex; align-items: center; gap: .55rem;
  flex-wrap: wrap; justify-content: center;
  font-size: .78rem; font-weight: 600; letter-spacing: .16em; text-transform: uppercase;
  color: var(--bone-200);
  background: rgba(16, 18, 8, .42);
  border: 1px solid rgba(227, 213, 189, .22);
  backdrop-filter: blur(8px);
  border-radius: var(--radius-pill);
  padding: .5rem 1.2rem;
}
body.rtl .map-hero-route { letter-spacing: 0; text-transform: none; font-size: .86rem; }
.map-hero-route i { width: 22px; height: 1px; background: var(--route-red); font-style: normal; flex: 0 0 auto; }

/* Progress hairline along the bottom of the stage — the only chrome. */
.map-hero-progress {
  position: absolute; inset-inline: 0; bottom: 0; z-index: 5;
  height: 2px; background: rgba(227, 213, 189, .14);
}
.map-hero-progress span {
  display: block; height: 100%; width: 100%;
  background: var(--route-red);
  transform: scaleX(var(--progress, 0));
  transform-origin: left center;
}
body.rtl .map-hero-progress span { transform-origin: right center; }

.scroll-cue {
  position: absolute;
  inset-block-end: 1.6rem;
  inset-inline: 0;
  z-index: 4;
  display: grid;
  justify-items: center;
  gap: .55rem;
  font-size: .66rem; font-weight: 600; letter-spacing: .3em; text-transform: uppercase;
  color: rgba(227, 213, 189, .72);
  opacity: var(--cue, 1);
  transition: opacity .35s var(--ease);
  pointer-events: none;
}
.scroll-cue i {
  display: block; width: 1px; height: 42px; position: relative; overflow: hidden;
  background: linear-gradient(to bottom, rgba(227, 213, 189, .05), rgba(227, 213, 189, .8));
}
.scroll-cue i::after {
  content: '';
  position: absolute; inset-inline: 0; top: -42px; height: 42px;
  background: linear-gradient(to bottom, transparent, var(--ember-400));
  animation: cue-trace 2.1s var(--ease) infinite;
}
@keyframes cue-trace { to { transform: translateY(84px); } }

/* The next section rises over the held final frame rather than cutting: a
   short overlap plus a parchment fade. */
.map-hero-next {
  position: relative;
  z-index: 6;
  margin-top: -16vh;
  /* The overlap is a veil, not a card edge: the first real element has to
     clear the fold, or the search panel pokes into the final frame. */
  padding-top: calc(16vh + 3.5rem);
  background: linear-gradient(to bottom, transparent 0%, var(--paper) 60%, var(--paper) 100%);
}
.map-hero-next .home-search { margin-top: 0; }

/* Static fallback — set as a class by the JS when scrubbing is unavailable
   (reduced motion, no video support, decode failure). The tall track
   collapses to one screen and the final frame stands as a still. */
.map-hero.is-static { height: auto; margin-top: 0; }
.map-hero.is-static .map-hero-stage { position: relative; height: min(78vh, 620px); }
.map-hero.is-static .map-hero-video { display: none; }
.map-hero.is-static .scroll-cue,
.map-hero.is-static .map-hero-progress { display: none; }
.map-hero.is-static .map-hero-intro,
.map-hero.is-static .map-hero-outro { opacity: 1; transform: none; }
/* Without the timeline there is nothing to stagger, so the route caption
   drops out of its absolute slot and stacks under the title instead of
   landing on top of it. */
.map-hero.is-static .map-hero-outro { position: static; margin-top: 1.5rem; }
.map-hero.is-static + .map-hero-next,
.map-hero.is-static ~ .map-hero-next { margin-top: 0; padding-top: 0; background: var(--paper); }

/* The same fallback expressed in CSS alone, so it also holds before the JS
   has run and when JS is disabled entirely. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .map-hero { height: auto; margin-top: 0; }
  .map-hero-stage { position: relative; height: min(78vh, 620px); }
  .map-hero-video { display: none; }
  .scroll-cue, .map-hero-progress { display: none; }
  .map-hero-intro, .map-hero-outro { opacity: 1; transform: none; }
  .map-hero-outro { position: static; margin-top: 1.5rem; }
  .map-hero-next { margin-top: 0; padding-top: 0; background: var(--paper); }
  .scroll-cue i::after { animation: none; }
}

/* A shorter track on phones: the same sequence over less scrolling, so it
   never feels like a chore on a small screen. */
@media (max-width: 780px) {
  .map-hero { --map-hero-scroll: 320vh; }
  .map-hero-poster, .map-hero-video { object-position: center 50%; }
  .map-hero-outro { inset-block-end: 3.5rem; }
}


/* == 10  Home / marketing sections ========================================= */
/* Flat hero for pages that are not the cinematic one. */
.hero {
  position: relative;
  background: var(--bark-800);
  color: var(--bone-200);
  padding-block: clamp(3rem, 7vw, 5.5rem);
  overflow: hidden;
}
.hero::after {
  content: '';
  position: absolute; inset: 0;
  background: radial-gradient(90% 120% at 15% 0%, rgba(98, 106, 65, .35), transparent 60%);
  pointer-events: none;
}
.hero .container { position: relative; z-index: 1; }
.hero h1 { color: var(--bone-100); max-width: 18ch; }
.hero .lead { color: rgba(227, 213, 189, .82); }

.search-bar {
  display: flex; gap: .6rem; flex-wrap: wrap;
  margin-top: 1.8rem; padding: .7rem;
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}
.search-bar input, .search-bar select { flex: 1 1 200px; border-color: transparent; background: var(--bone-100); }
.search-bar .btn { flex: 0 0 auto; }

/* The search card straddles the seam between the hero and the content. */
.home-search { position: relative; z-index: 7; margin-top: -2.6rem; margin-bottom: 1rem; }
.home-search .search-bar { margin-top: 0; box-shadow: var(--shadow-lg); }
.home-search-label {
  display: block; font-size: .74rem; font-weight: 600;
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 .7rem .2rem;
}
body.rtl .home-search-label { letter-spacing: 0; text-transform: none; font-size: .84rem; }

.band { background: var(--bark-800); color: rgba(227, 213, 189, .82); padding-block: clamp(2.5rem, 5vw, 4rem); }
.band h2 { color: var(--bone-100); }
.band-grid { display: grid; gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.band-item { display: grid; gap: .4rem; align-content: start; }
.band-num { font-size: .72rem; font-weight: 700; letter-spacing: .2em; color: var(--ember-400); }
.band-item h3 { color: var(--bone-100); margin: 0; font-size: 1.02rem; }
.band-item p { margin: 0; font-size: .89rem; line-height: 1.75; }


/* == 11  Cards & grids ===================================================== */
.grid { display: grid; gap: 1.35rem; }
.grid-2 { grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fill, minmax(285px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fill, minmax(215px, 1fr)); }

/**
 * The compact card rows: blog posts and leaders.
 *
 * These are a picture, a name and a line — they do not need the width a tour
 * card does, and at the old size a phone showed one enormous post per screen.
 * Small enough that a phone fits two side by side, which is stated outright at
 * the narrow end rather than left to a minmax to work out.
 */
.grid-compact { grid-template-columns: repeat(auto-fill, minmax(215px, 1fr)); gap: 1.1rem; }
.two-col { display: grid; gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
.card:hover { border-color: var(--line-strong); box-shadow: var(--shadow-md); }
.card-body { padding: 1.1rem 1.2rem 1.2rem; display: flex; flex-direction: column; flex: 1; }
.card-body h3 { font-size: 1.05rem; margin-bottom: .35rem; }
.card-body h3 a { color: inherit; text-decoration: none; }
.card-body h3 a:hover { color: var(--accent); }

.card-media {
  display: block; position: relative;
  aspect-ratio: 3 / 2;
  background: var(--surface-sunk);
  overflow: hidden;
}
.card-media img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .6s var(--ease); }
.card:hover .card-media img { transform: scale(1.045); }
.card-placeholder {
  display: grid; place-items: center; height: 100%;
  font-size: 2.2rem; color: var(--bone-500); background: var(--surface-sunk);
}

.card-meta {
  color: var(--brand-soft); font-size: .75rem; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase; margin: 0 0 .45rem;
}
body.rtl .card-meta { letter-spacing: 0; text-transform: none; font-size: .82rem; }

.card-facts {
  list-style: none; padding: 0; margin: .7rem 0 0;
  font-size: .84rem; color: var(--ink-soft);
  display: grid; gap: .3rem;
}
.card-facts li { display: flex; justify-content: space-between; gap: .6rem; }

.card-footer {
  display: flex; justify-content: space-between; align-items: center;
  gap: .7rem; margin-top: auto; padding-top: 1rem;
  border-top: 1px solid var(--line);
}
.price    { font-weight: 700; color: var(--brand-deep); }
.price-lg { font-weight: 700; font-size: 1.4rem; color: var(--brand-deep); }

/* Everything in a compact card is a size down, so the card is mostly the
   picture and the title rather than padding. */
.card-compact .card-media { aspect-ratio: 4 / 3; }
.card-compact .card-body { padding: .75rem .85rem .9rem; }
.card-compact .card-body h3 { font-size: .95rem; margin-bottom: .25rem; }
.card-compact .card-body p { font-size: .78rem; margin: 0; }
.card-compact .card-body p + p { margin-top: .35rem; }
/* An excerpt runs to whatever length somebody typed; three lines keeps the
   cards in a row the same height without cutting a title off. */
.card-excerpt {
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}

.leader-card { padding: 1rem .8rem 1.1rem; text-align: center; align-items: center; }
.leader-card a { text-decoration: none; color: inherit; }
.leader-card h3 { font-size: .95rem; margin: 0 0 .2rem; }
.leader-card .card-facts { text-align: start; width: 100%; font-size: .78rem; }
/* The face is the whole point of a leader card, so it grows as the card
   shrinks — a smaller card with a bigger portrait in it. */
.leader-card .avatar { width: 104px; height: 104px; font-size: 2.2rem; margin-bottom: .7rem; }

.avatar {
  width: 76px; height: 76px; border-radius: 50%; margin: 0 auto .8rem;
  background: var(--surface-sunk); border: 1px solid var(--line);
  display: grid; place-items: center; overflow: hidden; font-size: 1.7rem;
}
.avatar-lg { width: 104px; height: 104px; font-size: 2.2rem; }
.avatar img { width: 100%; height: 100%; object-fit: cover; }

/* Horizontal scrollers. Under dir="rtl" the first card lands on the right and
   the row scrolls right-to-left with no extra rules. */
.scroller-block { margin-block: 1.8rem; }

/* An arrow sitting over each end of anything that scrolls sideways — the tour
   rows and the panel tab bar both use these. The buttons overlap the content
   rather than taking a column of their own, so a narrow screen spends all of
   its width on the thing being scrolled. */
.rail-shell { position: relative; }
.scroller {
  display: flex; gap: 1rem; overflow-x: auto; padding-bottom: .8rem;
  scroll-snap-type: x proximity; scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}
.rail-nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  z-index: 2;
  /* The chevrons are bidi-mirrored characters: inside a Farsi page they flip,
     so the button that scrolls right drew a left-pointing arrow. Direction
     alone does not stop that — the glyph still belongs to the surrounding
     paragraph — so the button isolates its own text and pins it left-to-right,
     and the arrow then means the same thing on both sides of the site. */
  direction: ltr; unicode-bidi: isolate;
  display: grid; place-items: center;
  width: 2.2rem; height: 2.2rem; padding: 0;
  font-size: 1.5rem; line-height: 1;
  border-radius: 50%; cursor: pointer;
  color: var(--brand-deep);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  box-shadow: var(--shadow-md);
  transition: background-color .16s var(--ease), color .16s var(--ease);
}
.rail-nav:hover { background: var(--bark-700); color: var(--bone-200); border-color: var(--bark-700); }
.rail-nav:focus-visible { outline: 2px solid var(--bark-500); outline-offset: 2px; }
.rail-nav[hidden] { display: none; }
/* Physical sides: these do not swap under dir="rtl", and neither do the
   deltas the script scrolls by. */
.rail-nav-left { left: -.5rem; }
.rail-nav-right { right: -.5rem; }

/* A tour row keeps a strip of space under it for its scrollbar, so centring on
   the row alone would sit the arrows slightly low. */
.scroller-shell .rail-nav { top: calc(50% - .4rem); }

@media (max-width: 780px) {
  .rail-nav-left { left: 0; }
  .rail-nav-right { right: 0; }
}
.scroller-card {
  flex: 0 0 245px; scroll-snap-align: start; display: grid; gap: .25rem;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: .8rem;
  text-decoration: none; color: inherit; font-size: .85rem;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
.scroller-card:hover { border-color: var(--line-strong); box-shadow: var(--shadow-md); }
.scroller-media {
  aspect-ratio: 16 / 10; background: var(--surface-sunk); border-radius: var(--radius);
  overflow: hidden; display: grid; place-items: center; margin-bottom: .45rem;
}
.scroller-media img { width: 100%; height: 100%; object-fit: cover; }


/* == 12  Detail pages ====================================================== */
.detail-head {
  display: flex; justify-content: space-between; gap: 1.5rem;
  flex-wrap: wrap; align-items: flex-start;
  padding-bottom: 1.4rem; margin-bottom: 1.6rem;
  border-bottom: 1px solid var(--line);
}
.detail-head h1 { margin-bottom: .3rem; }
.detail-cta { display: grid; gap: .6rem; justify-items: start; }

.hero-image {
  width: 100%; max-height: 460px; object-fit: cover;
  border-radius: var(--radius-lg); margin-block: 1.4rem;
  background: var(--surface-sunk);
}

.fact-grid { list-style: none; padding: 0; display: grid; gap: .7rem; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
.fact-grid li {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: .75rem .9rem;
  display: grid; gap: .1rem; font-weight: 600; color: var(--brand-deep);
  /* A grid item defaults to min-width:auto, so a value with nowhere to break —
     an email address is one long word as far as the line breaker is concerned —
     ran straight through the card's edge instead of being contained by it. */
  min-width: 0;
}
/* Wrap anywhere, because the values that overflow here (emails, Telegram
   handles, national IDs) have no spaces to break at. */
.fact-grid li strong { overflow-wrap: anywhere; min-width: 0; }
.fact-grid span { font-size: .74rem; font-weight: 500; color: var(--muted); letter-spacing: .06em; }

.stat-row {
  list-style: none; padding: 0; display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  margin-block: 1.6rem;
}
.stat-row li {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 1.1rem 1.2rem;
  display: grid; gap: .15rem; align-content: start;
  /* A grid item defaults to min-width:auto, so an unbreakable value — an email
     address is one long word to the line breaker — pushed straight through the
     card's right edge instead of being contained by it. */
  min-width: 0;
}
/* A money figure carries a currency and thousands separators, so it is a long
   string before it is a big one — at the old size the tiles were breaking it
   across two and three lines, which is harder to read than a smaller number. */
.stat-row strong {
  font-size: 1.3rem; font-weight: 700; color: var(--brand-deep); line-height: 1.25;
  /* Wrap anywhere, since the strings that overflow here (emails, Telegram
     handles, national IDs) have no spaces to break at. */
  overflow-wrap: anywhere;
}
.stat-row span { font-size: .76rem; color: var(--muted); font-weight: 500; letter-spacing: .06em; }
.stat-row-lg li { min-width: 170px; }
.stat-row-lg .btn-link { padding: 0; text-align: start; font-size: .78rem; }
.stat-row-3 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.stat-row-3 strong { font-size: 1.45rem; }
.stat-row-3 small { font-size: .72rem; margin-top: .25rem; display: block; color: var(--muted); }

.timeline { padding-inline-start: 0; list-style: none; margin: 1rem 0; }
.timeline li {
  position: relative;
  padding-inline-start: 1.5rem;
  padding-bottom: 1.1rem;
  border-inline-start: 1px solid var(--line-strong);
  margin-inline-start: .35rem;
}
.timeline li:last-child { border-inline-start-color: transparent; padding-bottom: 0; }
.timeline li::before {
  content: '';
  position: absolute; inset-inline-start: -4.5px; top: .62em;
  width: 8px; height: 8px; border-radius: 50%; background: var(--ember-600);
}

.link-list { padding-inline-start: 1.3rem; margin: .8rem 0; }
.tick-list, .cross-list { list-style: none; padding-inline-start: 0; margin: .8rem 0; }
.link-list li, .tick-list li, .cross-list li { margin-bottom: .4rem; }
.tick-list li, .cross-list li { padding-inline-start: 1.5rem; position: relative; }
.tick-list li::before  { content: '✓'; position: absolute; inset-inline-start: 0; color: var(--ok); font-weight: 700; }
.cross-list li::before { content: '✕'; position: absolute; inset-inline-start: 0; color: var(--danger); font-weight: 700; }

.review-list { list-style: none; padding: 0; margin: 0; }
.review-list li { border-bottom: 1px solid var(--line); padding-block: 1.1rem; }
.review-list li:last-child { border-bottom: 0; }
.rating-breakdown {
  list-style: none; padding: 0; display: flex; gap: 1.2rem; flex-wrap: wrap;
  color: var(--muted); font-size: .88rem;
}
.rating-card .rating-average { display: flex; align-items: baseline; gap: .6rem; }
.rating-card .rating-average strong { font-size: 1.9rem; color: var(--brand-deep); }
.stars { color: var(--ember-500); letter-spacing: .08em; }

.bar-list { list-style: none; padding: 0; margin: 0; }
.bar-list li {
  display: flex; justify-content: space-between; gap: 1rem;
  border-bottom: 1px solid var(--line); padding-block: .55rem; font-size: .9rem;
}
.bar-list li:last-child { border-bottom: 0; }

.partner-row { list-style: none; padding: 0; display: flex; gap: 1rem; flex-wrap: wrap; }
.partner-row a {
  display: inline-block; padding: .55rem 1.1rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); text-decoration: none; font-size: .9rem;
}
.partner-row a:hover { border-color: var(--line-strong); }

.leader-head {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: 1.4rem;
  padding-bottom: 1.4rem; border-bottom: 1px solid var(--line);
}
.leader-head-identity { display: flex; align-items: center; gap: 1.2rem; }
.leader-head-identity h1 { margin: 0; }
.leader-head-stats { list-style: none; display: flex; gap: .8rem; padding: 0; margin: 0; margin-inline-start: auto; }
.leader-head-stats li {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: .85rem 1.3rem; display: grid; min-width: 130px; text-align: center;
}
.leader-head-stats strong { font-size: 1.55rem; line-height: 1.15; color: var(--brand-deep); }
.leader-head-stats span { font-size: .74rem; color: var(--muted); }

.detail-panel { margin-block: 1.4rem; }
.payout-card { margin-block: 1.2rem; }
.cover-preview { max-width: 320px; border-radius: var(--radius); display: block; margin-bottom: .6rem; }
/* The cover block sits inside the tour form's grid, so it keeps its own
   stacking rather than inheriting the two-column rhythm around it. */
.cover-field { display: grid; gap: .5rem; justify-items: start; }
.cover-field input[type="file"] { width: auto; }
.leader-row {
  display: grid; gap: .7rem; grid-template-columns: repeat(auto-fit, minmax(155px, 1fr));
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: .85rem;
}


/* == 13  Tables, panels & dashboards ======================================= */
.table {
  width: 100%; border-collapse: collapse; font-size: .89rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.table th, .table td {
  text-align: start; padding: .72rem .85rem;
  border-bottom: 1px solid var(--line); vertical-align: middle;
}
.table th {
  background: var(--surface-sunk);
  font-weight: 600; font-size: .78rem; letter-spacing: .06em;
  color: var(--ink-soft); white-space: nowrap;
}
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: var(--bone-50); }
.table a { font-weight: 600; text-decoration: none; }
.table a:hover { text-decoration: underline; }

.table-scroll { overflow-x: auto; border-radius: var(--radius-lg); scrollbar-width: thin; }
.roster-table { font-size: .82rem; min-width: 900px; }
.roster-table td { vertical-align: top; }
.row-muted { opacity: .55; }
.row-warn { background: var(--warn-bg); }

/* Panel navigation: a scrollable pill rail rather than a wrapped link soup. */
/* The bar's own margins move to the shell, so the arrows sitting over its
   ends line up with the pill rather than with the space around it. */
.panel-nav-shell { margin-block: 1.4rem 2rem; }
.panel-nav-shell > .panel-nav { margin-block: 0; }

.panel-nav {
  display: flex; gap: .35rem; margin-block: 1.4rem 2rem;
  padding: .35rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  overflow-x: auto;
  scrollbar-width: none;
}
.panel-nav::-webkit-scrollbar { display: none; }
.panel-nav a {
  text-decoration: none; color: var(--ink-soft); font-size: .87rem; font-weight: 500;
  padding: .45rem .95rem; border-radius: var(--radius-pill);
  white-space: nowrap; flex: 0 0 auto;
  transition: background-color .16s var(--ease), color .16s var(--ease);
}
.panel-nav a:hover { background: var(--surface-sunk); color: var(--brand-deep); }
.panel-nav a.active { background: var(--bark-700); color: var(--bone-200); font-weight: 600; }

/* A dashboard's opening line: who you are, and the one action that matters. */
.panel-head {
  display: flex; flex-wrap: wrap; align-items: flex-end;
  justify-content: space-between; gap: 1rem; margin-bottom: .4rem;
}
.panel-head h1 { margin: 0; }
.panel-head .eyebrow { margin-bottom: .35rem; }

.pagination { display: flex; gap: 1rem; align-items: center; justify-content: center; margin-block: 2.2rem; }
.page-info { color: var(--muted); font-size: .88rem; }

.contact-cards { list-style: none; padding: 0; display: grid; gap: .8rem; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); }
.contact-cards li {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: .9rem 1rem; display: grid; gap: .18rem;
}


/* == 14  Auth pages ========================================================
   Login and register share a split shell: a bark-green panel carrying the
   logo and a line of copy, and a parchment panel carrying the form. Below
   900px the green panel collapses to a slim header band.
   ------------------------------------------------------------------------ */
.auth {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  min-height: calc(100vh - var(--header-h));
  align-items: stretch;
}
.auth-aside {
  /* The registration form is far taller than this panel, so it sticks rather
     than stretching — otherwise its content sits centred somewhere off
     screen and the visitor only ever sees an empty green column. */
  position: sticky;
  top: var(--header-h);
  align-self: start;
  min-height: calc(100vh - var(--header-h));
  background: var(--bark-800);
  color: rgba(227, 213, 189, .8);
  padding: clamp(2.5rem, 5vw, 4.5rem);
  display: grid; align-content: center; gap: 1.1rem;
  overflow: hidden;
}
.auth-aside::after {
  content: '';
  position: absolute; inset: 0;
  background:
    radial-gradient(80% 90% at 20% 15%, rgba(98, 106, 65, .4), transparent 62%),
    url('/static/map/map-last.jpg') center / cover no-repeat;
  opacity: .16;
  pointer-events: none;
}
.auth-aside > * { position: relative; z-index: 1; }
.auth-logo { height: 44px; width: auto; margin-bottom: .6rem; }
.auth-aside h2 {
  color: var(--bone-100); margin: 0; font-weight: 300;
  font-size: clamp(1.4rem, 1rem + 1.4vw, 2rem);
}
.auth-aside h2 strong { font-weight: 700; }
.auth-aside p { margin: 0; font-size: .93rem; line-height: 1.8; max-width: 40ch; }
.auth-points { list-style: none; padding: 0; margin: .6rem 0 0; display: grid; gap: .6rem; font-size: .88rem; }
.auth-points li { display: flex; gap: .6rem; align-items: flex-start; }
.auth-points li::before { content: '—'; color: var(--ember-400); flex: 0 0 auto; }

.auth-main {
  padding: clamp(2rem, 4vw, 3.5rem) clamp(1.25rem, 4vw, 3.5rem);
  display: grid; align-content: start; background: var(--paper);
}
/* The sticky aside stops short of the form column on a long page, so whatever
   the form leaves below itself shows up as paper beside the green. Trimmed,
   since the footer now sits directly underneath. */
.auth-main { padding-bottom: clamp(1.5rem, 3vw, 2.5rem); }
.auth-main-inner { width: 100%; max-width: 460px; margin-inline: auto; }
.auth-wide .auth-main-inner { max-width: 780px; }
.auth-main h1 { margin-bottom: .3rem; }
.auth-sub { color: var(--muted); font-size: .93rem; margin-bottom: 1.8rem; }

.auth-alt {
  margin-top: 1.8rem; padding-top: 1.4rem;
  border-top: 1px solid var(--line);
  font-size: .9rem; color: var(--muted);
}
.auth-alt a { font-weight: 600; text-decoration: none; }
.auth-alt a:hover { text-decoration: underline; }

@media (max-width: 900px) {
  .auth { grid-template-columns: 1fr; min-height: 0; }
  .auth-aside {
    position: static;
    min-height: 0;
    padding: 1.8rem 1.5rem;
    gap: .7rem;
  }
  .auth-aside p, .auth-aside .auth-points { display: none; }
  .auth-logo { height: 32px; margin-bottom: 0; }
  .auth-main { padding-inline: 1.25rem; }
}


/* == 15  Feedback ========================================================== */
.alert {
  padding: .85rem 1.1rem; border-radius: var(--radius);
  margin-block: 1.2rem; font-size: .9rem;
  border-inline-start: 3px solid transparent;
}
.alert-success { background: var(--ok-bg);     color: var(--ok);     border-inline-start-color: var(--ok); }
.alert-error   { background: var(--danger-bg); color: var(--danger); border-inline-start-color: var(--danger); }

.notice {
  border: 1px solid var(--line);
  border-inline-start: 3px solid var(--ember-600);
  background: var(--surface);
  border-radius: var(--radius);
  padding: 1.1rem 1.3rem;
  margin-block: 1.2rem;
  font-size: .9rem;
}
.notice-info { border-inline-start-color: var(--bark-500); }
.notice h3 { margin: 0 0 .45rem; font-size: .97rem; }
.notice-pay .contact-cards { margin-block-start: .9rem; }
.notice p { margin: 0 0 .5rem; color: var(--ink-soft); }
.notice p:last-child { margin-bottom: 0; }
.notice ul, .notice ol { margin: .4rem 0 0; padding-inline-start: 1.3rem; color: var(--ink-soft); }
.notice li { margin-bottom: .3rem; }
.notice code { word-break: break-all; }

.empty-state {
  color: var(--muted);
  padding: 3.5rem 1rem;
  text-align: center;
  background: var(--surface);
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-lg);
  margin: 0;
}

.error-page { text-align: center; padding-block: clamp(3rem, 9vw, 6rem); }
.error-page h1 { font-size: clamp(3.5rem, 12vw, 7rem); color: var(--bone-400); margin-bottom: .1em; }
.error-page p { color: var(--ink-soft); font-size: 1.05rem; }
.stack {
  text-align: start; white-space: pre-wrap;
  background: var(--bark-900); color: var(--bone-300);
  padding: 1.1rem; border-radius: var(--radius); font-size: .78rem;
  overflow: auto; direction: ltr; margin-block: 1.4rem;
}
.code-block {
  background: var(--bark-900); color: var(--bone-300);
  border-radius: var(--radius); padding: .9rem 1.1rem; overflow-x: auto;
  direction: ltr; text-align: start; font-size: .84rem; line-height: 1.65; margin: .7rem 0;
}


/* == 16  Overlays ========================================================== */
body.lightbox-open, body.modal-open { overflow: hidden; }

.modal {
  position: fixed; inset: 0; z-index: 70;
  background: rgba(16, 18, 8, .76);
  backdrop-filter: blur(3px);
  display: grid; place-items: center; padding: 1rem;
}
.modal[hidden] { display: none; }
.modal-panel {
  background: var(--paper); border-radius: var(--radius-lg);
  width: min(980px, 96vw); max-height: 88vh; overflow: hidden;
  display: flex; flex-direction: column; box-shadow: var(--shadow-lg);
}
.modal-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 1.1rem 1.3rem; border-bottom: 1px solid var(--line);
}
.modal-head h2 { margin: 0; font-size: 1.05rem; }
.modal-close {
  background: none; border: 0; font-size: 1.7rem; line-height: 1;
  cursor: pointer; color: var(--muted); padding: 0 .3rem;
}
.modal-close:hover { color: var(--ink); }

.lightbox {
  position: fixed; inset: 0; z-index: 80; background: rgba(16, 18, 8, .95);
  display: flex; align-items: center; justify-content: center; gap: .5rem; padding: 1rem;
}
.lightbox[hidden] { display: none; }
.lightbox-figure { margin: 0; max-width: min(1100px, 92vw); max-height: 88vh; text-align: center; }
.lightbox-figure img {
  max-width: 100%; max-height: 82vh; object-fit: contain;
  border-radius: var(--radius); display: block; margin-inline: auto;
}
.lightbox-caption { color: var(--bone-300); font-size: .86rem; margin-top: .8rem; }
.lightbox-caption[hidden] { display: none; }
.lightbox-arrow, .lightbox-close {
  background: rgba(227, 213, 189, .12); color: var(--bone-100);
  border: 0; cursor: pointer; border-radius: 50%; display: grid; place-items: center;
}
.lightbox-arrow { width: 52px; height: 52px; font-size: 2rem; flex: 0 0 auto; }
.lightbox-close { position: absolute; inset-block-start: 1rem; inset-inline-end: 1rem; width: 42px; height: 42px; font-size: 1.6rem; }
.lightbox-arrow:hover, .lightbox-close:hover { background: rgba(227, 213, 189, .26); }

.photo-carousel { position: relative; margin-block: 1.2rem 1.8rem; }
.carousel-track {
  display: flex; gap: .85rem; overflow-x: auto; scroll-snap-type: x mandatory;
  scroll-behavior: smooth; padding-bottom: .6rem; scrollbar-width: thin;
}
.carousel-slide { margin: 0; flex: 0 0 auto; width: min(330px, 72vw); scroll-snap-align: start; }
.carousel-slide img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  border-radius: var(--radius-lg); display: block; cursor: zoom-in;
  border: 1px solid var(--line); background: var(--surface);
}
.carousel-slide figcaption { font-size: .8rem; margin-top: .4rem; color: var(--muted); }
.carousel-arrow {
  position: absolute; inset-block-start: 38%; z-index: 2;
  width: 42px; height: 42px; border-radius: 50%; cursor: pointer;
  border: 1px solid var(--line); background: rgba(255, 255, 255, .94); color: var(--ink);
  font-size: 1.6rem; line-height: 1; display: grid; place-items: center;
  box-shadow: var(--shadow-md);
}
.carousel-arrow:hover { background: #fff; }
.carousel-prev { inset-inline-start: -6px; }
.carousel-next { inset-inline-end: -6px; }
.carousel-static .carousel-arrow { display: none; }

.dev-scroller { display: flex; gap: 1.1rem; overflow-x: auto; padding: 1.3rem; scroll-snap-type: x mandatory; scrollbar-width: thin; }
.dev-card {
  flex: 0 0 auto; width: min(285px, 78vw); scroll-snap-align: start;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 1.3rem 1.1rem; text-align: center;
}
.dev-photo {
  width: 92px; height: 92px; margin: 0 auto .8rem; border-radius: 50%;
  overflow: hidden; background: var(--surface-sunk); border: 1px solid var(--line);
  display: grid; place-items: center; font-size: 2.2rem;
}
.dev-photo img { width: 100%; height: 100%; object-fit: cover; }
.dev-name { margin: 0 0 .2rem; font-size: 1.02rem; }
.dev-role { margin: 0 0 .7rem; font-size: .8rem; color: var(--brand-soft); font-weight: 600; }
.dev-bio { font-size: .86rem; margin: 0 0 .9rem; color: var(--ink-soft); }
.dev-links { list-style: none; padding: 0; margin: 0; display: grid; gap: .35rem; font-size: .85rem; }
.dev-links a { text-decoration: none; }
.dev-links a:hover { text-decoration: underline; }

.dev-admin-row {
  display: grid; gap: 1.2rem; padding: 1.2rem; margin-block: 1.2rem;
  grid-template-columns: 160px 1fr auto; align-items: start;
}
.dev-admin-photo { text-align: center; }
.dev-admin-photo img { width: 92px; height: 92px; border-radius: 50%; object-fit: cover; }
.dev-admin-photo span { font-size: 2.5rem; }
.dev-admin-form { margin: 0; }


/* == 17  Documents, gallery, blog =========================================== */
.document-manager { display: grid; gap: 1.2rem; grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); }
.document-slot { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 1.1rem; }
.document-slot h3 { font-size: .98rem; margin-bottom: .5rem; }
.document-body { margin-block: 1.2rem; }
.document-meta { font-size: .8rem; color: var(--muted); }
.pdf-embed object { width: 100%; height: 75vh; border: 1px solid var(--line); border-radius: var(--radius-lg); }

.pdf-viewer {
  max-height: 88vh; overflow-y: auto; background: var(--surface-sunk);
  border: 1px solid var(--line); border-radius: var(--radius-lg); padding: .85rem;
  scrollbar-width: thin;
  /* A page of prose is read at a column width, not a screen width. Stretched
     across a desktop container the type came out tiny and the lines far too
     long — the viewer renders each page to the width it is given, so the fix
     is to give it less. Below 760px this resolves to 100%, which is why the
     phone layout was already right and is untouched. */
  width: min(100%, 760px);
  margin-inline: auto;
}
.pdf-pages { display: grid; gap: .85rem; }
.pdf-page { display: block; width: 100%; height: auto; background: #fff; box-shadow: var(--shadow-md); border-radius: var(--radius-sm); }
.pdf-status { text-align: center; padding: 2.5rem 0; margin: 0; color: var(--muted); }

.gallery-tile { padding: .45rem; text-align: center; }
.gallery-tile img { width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: var(--radius); }

.masonry { columns: 3 250px; column-gap: 1.2rem; }
.masonry figure { margin: 0 0 1.2rem; break-inside: avoid; }
.masonry img, .masonry video { width: 100%; border-radius: var(--radius-lg); display: block; background: var(--surface-sunk); }
.masonry figcaption { font-size: .82rem; margin-top: .45rem; color: var(--muted); }

.prose { max-width: var(--max-text); }
.prose img { max-width: 100%; border-radius: var(--radius-lg); }
.prose h2 { margin-top: 2.4rem; }
.prose blockquote {
  margin: 1.6rem 0; padding-inline-start: 1.3rem;
  border-inline-start: 3px solid var(--ember-500);
  color: var(--ink-soft); font-size: 1.05rem;
}
.article-body { max-width: 760px; }

.post-cover {
  display: block; max-height: 260px; width: auto; max-width: 100%;
  object-fit: cover; border-radius: var(--radius-lg); margin-block: 1.2rem;
}
.document-stage {
  width: min(1180px, 96vw);
  margin-inline: calc(50% - min(590px, 48vw));
  margin-block: 1.5rem;
}
/* The admin document stage is a deliberately wide reading surface, so it
   opts back out of the column cap above. */
.document-stage .pdf-viewer { max-height: 92vh; width: 100%; }
.document-stage .prose { max-width: 72ch; margin-inline: auto; }


/* == 18a  Telegram — the customer's connect page ============================
   Two cards, side by side on a wide screen and stacked on a narrow one. Each
   carries its own explanation and its own button, because the first version
   printed the step titles twice — once in a list, once as a heading below it —
   and readers could not tell which half was the thing to act on.
   ========================================================================= */
.tg-why ul { columns: 2; column-gap: 1.6rem; }
@media (max-width: 720px) { .tg-why ul { columns: 1; } }

.tg-steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 1rem;
  margin: 1.2rem 0;
  align-items: start;
}

.tg-step {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 1.15rem 1.25rem 1.3rem;
}
/* The step you can act on is the one with weight; the other recedes without
   disappearing, so the whole journey stays visible. */
.tg-step-active { border-color: var(--bark-400); box-shadow: var(--shadow-md); }
.tg-step-done   { background: var(--bone-50); border-color: var(--line); }

.tg-step-head {
  display: flex;
  align-items: center;
  gap: .6rem;
  margin-bottom: .7rem;
  padding-bottom: .7rem;
  border-bottom: 1px solid var(--line);
}
.tg-step-head h2 { margin: 0; font-size: 1.02rem; line-height: 1.35; }

.tg-step-num {
  flex: none;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: var(--radius-pill);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: .9rem;
  background: var(--surface-sunk);
  color: var(--ink-soft);
}
.tg-step-active .tg-step-num { background: var(--bark-700); color: var(--bone-200); }
.tg-step-done   .tg-step-num { background: var(--ok-bg);    color: var(--ok); }

.tg-step p { color: var(--ink-soft); margin: 0 0 .7rem; }
.tg-step .btn-block { margin-bottom: .15rem; }
.tg-step-state { color: var(--ink); }

/* The bot's own name, so people know what they are looking for in Telegram's
   search box when the deep link does not open. */
.tg-bot {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  flex-wrap: wrap;
  background: var(--surface-sunk);
  border-radius: var(--radius);
  padding: .5rem .75rem;
}
.tg-bot-handle { font-size: 1.02rem; letter-spacing: .01em; }

.tg-manual { margin-top: .9rem; }
.tg-manual summary { cursor: pointer; color: var(--ink-soft); font-size: .88rem; }
.tg-manual .step-list li { color: var(--ink-soft); }


/* == 18  Telegram board ==================================================== */
.telegram-prompt { display: grid; gap: .7rem; justify-items: start; }
.telegram-prompt .btn { margin-inline-end: .4rem; }

.step-list { padding-inline-start: 1.3rem; margin: .7rem 0; }
.step-list li { margin-bottom: .35rem; color: var(--muted); }
.step-list .step-current { color: var(--ink); font-weight: 600; }
.step-list .step-done { text-decoration: line-through; opacity: .6; }

.message-log {
  list-style: none; padding: 0; margin: 1rem 0 0; display: grid; gap: .7rem;
  max-height: 440px; overflow-y: auto; scrollbar-width: thin;
}
.message-log li {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: .7rem .9rem; background: var(--surface); font-size: .88rem;
}
.message-log .message-in { background: var(--surface-sunk); margin-inline-start: 2rem; }
.message-log .message-out { margin-inline-end: 2rem; }
.message-log .message-failed { border-color: var(--danger); background: var(--danger-bg); }
.message-meta { font-size: .73rem; margin-bottom: .28rem; color: var(--muted); }
.message-text { white-space: pre-wrap; word-break: break-word; }

.bot-switch {
  display: flex; flex-wrap: wrap; gap: 1rem;
  align-items: center; justify-content: space-between;
  border: 1px solid var(--line); border-inline-start-width: 4px;
  border-radius: var(--radius-lg); background: var(--surface);
  padding: 1.1rem 1.3rem; margin-block: 1.2rem;
}
.bot-switch p { margin: .25rem 0 0; font-size: .86rem; max-width: 62ch; color: var(--ink-soft); }
.bot-switch-on { border-inline-start-color: var(--ok); }
.bot-switch-off { border-inline-start-color: var(--ember-600); background: var(--warn-bg); }
.bot-switch-action { display: flex; align-items: center; gap: .8rem; }
.form-section .row-actions { display: flex; flex-wrap: wrap; gap: .55rem; align-items: center; margin-block: .9rem; }


/* == 17b  About =============================================================
   The About intro is a reading column and a picture column.

   Body copy is capped at --max-text so it stays readable, which left the far
   half of the page empty — the left in Farsi, the right in English, since that
   is the side reading runs away from. The grid follows the writing direction
   by itself, so one rule fills the gap in both languages; there is deliberately
   no [dir] selector here.

   The card's own background is a warm cream, a shade off the page's parchment.
   Rather than match the two exactly — which breaks the moment either is
   retuned — the edges are dissolved with a mask, so the picture ends in the
   page rather than on a seam.
   ======================================================================== */
.about-intro {
  display: grid;
  gap: 1.5rem 2.5rem;
  grid-template-columns: minmax(0, 1fr) minmax(0, .8fr);
  align-items: center;
  margin-block-end: 1rem;
}
.about-intro-copy > .panel-head { margin-block-start: 0; }

.about-mark { margin: 0; }
.about-mark img {
  display: block;
  width: 100%;
  height: auto;
  /* The source is square with a lot of quiet above and below the words.
     Cropping to a gentler ratio brings the definition itself up to the size of
     the text beside it. */
  aspect-ratio: 1 / .82;
  object-fit: cover;
  -webkit-mask-image: radial-gradient(ellipse at center, #000 42%, rgba(0, 0, 0, .55) 66%, transparent 82%);
          mask-image: radial-gradient(ellipse at center, #000 42%, rgba(0, 0, 0, .55) 66%, transparent 82%);
}

/* One column below the breakpoint, picture second: on a phone the words should
   arrive before the decoration. */
@media (max-width: 860px) {
  .about-intro { grid-template-columns: 1fr; }
  .about-mark img { aspect-ratio: 1 / .6; max-height: 42vh; }
}


/* == 18b  Ratings =========================================================
   The star control is five radio buttons and nothing else. It is drawn
   backwards — 5 first in the markup — so that `:checked ~ label` can light up
   every star from the chosen one down without a line of JavaScript. That is
   also why it keeps working with scripts blocked, which matters for a form
   that appears exactly once per trip and cannot be reopened.
   ======================================================================== */
.rating-promise {
  background: var(--ok-bg);
  border-inline-start: 3px solid var(--ok);
  border-radius: var(--radius);
  padding: .85rem 1.1rem;
  margin-block: 1.2rem;
  color: var(--bark-800);
  font-size: .92rem;
}

.rating-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 1.4rem 1.5rem;
  margin-block: 1.4rem;
}
.rating-card-head h2 { margin: 0 0 .2rem; font-size: 1.25rem; }
.rating-card-head p { margin: 0 0 1rem; font-size: .85rem; }

.rating-field {
  border: 0;
  border-top: 1px solid var(--line);
  padding: 1rem 0 .3rem;
  margin: 0;
}
.rating-field legend { font-weight: 700; color: var(--brand-deep); padding-inline-end: .6rem; }

.stars {
  display: inline-flex;
  flex-direction: row-reverse;   /* markup runs 5→1; reading order runs 1→5 */
  justify-content: flex-end;
  gap: .15rem;
  margin-block: .35rem .6rem;
}
.stars input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.stars label {
  cursor: pointer;
  font-size: 1.85rem;
  line-height: 1;
  color: var(--line-strong);
  transition: color .12s var(--ease), transform .12s var(--ease);
}
/* The chosen star and everything before it. `~` only reaches forward, which is
   exactly why the markup is reversed. */
.stars input:checked ~ label,
.stars label:hover,
.stars label:hover ~ label { color: var(--ember-500); }
.stars label:active { transform: scale(.92); }
.stars input:focus-visible + label { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--radius-sm); }

.rating-note { display: block; margin-block: .5rem; }
.rating-note span { display: block; margin-block-end: .3rem; font-size: .82rem; }
.rating-form .btn { margin-block-start: 1rem; }
.rating-form .small { font-size: .8rem; }

/* --- the admin side ---------------------------------------------------- */
/* The category picker: a label that reads like the form's other labels, over
   the same wrapping checkbox row the rating categories use. */
.category-config .field-legend {
  display: block; font-size: .84rem; font-weight: 600;
  color: var(--brand-deep); margin-block-end: .1rem;
}
.category-config .rating-defaults { margin-block: .5rem .7rem; }

.rating-config { border-top: 1px solid var(--line); padding-block-start: 1.2rem; }
.rating-config h2 { margin-block-end: .25rem; }
.rating-defaults {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1.4rem;
  margin-block: .9rem 1.4rem;
}
.rating-defaults .checkbox { gap: .45rem; }
.rating-defaults .checkbox small { margin-inline-start: .3rem; }
.rating-row {
  display: flex;
  flex-wrap: wrap;
  gap: .8rem;
  align-items: end;
  margin-block-end: .7rem;
}
.rating-row label { flex: 1 1 220px; }

.star-bars { list-style: none; margin: 0; padding: 0; display: grid; gap: .2rem; min-width: 190px; }
.star-bars li { display: flex; align-items: center; gap: .45rem; font-size: .76rem; }
.star-bar-label { width: 2.2em; color: var(--muted); }
.star-bar {
  flex: 1 1 auto;
  height: .5rem;
  border-radius: var(--radius-pill);
  background: var(--surface-sunk);
  overflow: hidden;
}
.star-bar > span { display: block; height: 100%; background: var(--ember-400); }

.note-list { list-style: none; margin: 0 0 1.4rem; padding: 0; display: grid; gap: .6rem; }
.note-list li {
  display: flex;
  gap: .7rem;
  align-items: start;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .7rem .9rem;
}
.note-list p { margin: 0; }

.feedback-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 1rem 1.2rem;
  margin-block: .9rem;
}
.feedback-card header { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin-block-end: .6rem; }
.feedback-card .tag-row { margin-block: .5rem; }


/* == 18c  Footer: bot link & developer credits ==============================
   Two small things that live in the footer's bottom bar.

   The credits are a native <dialog>, so the backdrop, Escape, focus trapping
   and returning focus to the button afterwards are the browser's job rather
   than ours — those are the parts of a modal that are quietly easy to get
   wrong by hand.
   ======================================================================== */
.footer-bot {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .4rem .9rem;
  border: 1px solid rgba(227, 213, 189, .28);
  border-radius: var(--radius-pill);
  color: var(--bone-200);
  text-decoration: none;
  font-size: .85rem;
  transition: background-color .18s var(--ease), border-color .18s var(--ease);
}
.footer-bot:hover { background: rgba(227, 213, 189, .1); border-color: var(--bone-400); }
.footer-bot-handle { font-weight: 600; color: var(--bone-300); }
.footer-bot-note { font-size: .76rem; margin: .35rem 0 0; }

/* Credits are a footnote, not a call to action: quiet until reached for. */
.footer-credits-btn {
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font: inherit;
  font-size: .82rem;
  color: var(--bone-400);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.footer-credits-btn:hover { color: var(--bone-200); }

.dev-credits {
  border: 0;
  border-radius: var(--radius-lg);
  padding: 2rem 1.8rem 2.2rem;
  max-width: min(940px, 94vw);
  width: 100%;
  background: var(--paper);
  color: var(--ink);
  box-shadow: var(--shadow-lg);
}
.dev-credits::backdrop { background: rgba(16, 18, 8, .62); backdrop-filter: blur(3px); }
.dev-credits-title { margin: 0 0 .2rem; text-align: center; }
.dev-credits-sub { margin: 0 0 1.8rem; text-align: center; font-size: .88rem; }

.dev-credits-dismiss { display: flex; justify-content: flex-end; margin: -.8rem -.6rem 0; }
.dev-credits-close {
  border: 0; background: none; cursor: pointer;
  font-size: 1.6rem; line-height: 1; color: var(--muted); padding: .2rem .5rem;
  border-radius: var(--radius-sm);
}
.dev-credits-close:hover { color: var(--ink); background: var(--surface-sunk); }
/* showModal() focuses the first control in the dialog, which is this one, so
   its focus ring is the first thing anybody sees — it should look like the
   rest of the site rather than like the browser default. */
.dev-credits-close:focus-visible { outline: 2px solid var(--bark-500); outline-offset: 2px; color: var(--ink); }

.dev-credits-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.1rem;
  align-items: stretch;
}

.dev-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .5rem;
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 1.5rem 1.1rem;
}

/* The main developer reads as the centre of the row rather than as one more
   card in it: a warmer surface, a stronger edge, a larger portrait. */
.dev-card.is-lead {
  border-color: var(--bark-300);
  background: linear-gradient(180deg, var(--bone-100), var(--surface));
  box-shadow: var(--shadow-md);
}
.dev-card.is-lead .dev-photo { width: 128px; height: 128px; border-color: var(--bark-400); }

.dev-photo {
  width: 104px;
  height: 104px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--surface-sunk);
  display: grid;
  place-items: center;
  border: 2px solid var(--line-strong);
}
.dev-photo img { width: 100%; height: 100%; object-fit: cover; }
.dev-photo-fallback { font-size: 1.9rem; font-weight: 700; color: var(--bark-300); }

.dev-name { font-size: 1.02rem; }
.dev-role { font-size: .82rem; }
.badge-lead { background: var(--bark-700); color: var(--bone-200); }

.dev-links {
  list-style: none;
  margin: .3rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .3rem .55rem;
}
.dev-links a {
  font-size: .8rem;
  color: var(--bark-600);
  text-decoration: none;
  border-bottom: 1px solid var(--line-strong);
  padding-bottom: 1px;
}
.dev-links a:hover { color: var(--accent); border-color: var(--accent); }

/* One column on a phone. The lead keeps its emphasis but stops being taller —
   "middle" means nothing once the row is a stack. */
@media (max-width: 760px) {
  .dev-credits { padding: 1.4rem 1.1rem 1.6rem; }
  .dev-credits-grid { grid-template-columns: 1fr; }
  .dev-card.is-lead .dev-photo { width: 104px; height: 104px; }
}


/* == 18d  Payments board ====================================================
   The panel a tour card opens: who is on that tour and what they have paid.
   It is fetched into the payments page and also served as a page of its own,
   so it carries its own frame rather than relying on either host. */
.payment-panel {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  padding: 1.2rem 1.3rem;
  margin-block: 1.4rem;
}
.payment-panel-head {
  display: flex; flex-wrap: wrap; gap: .8rem;
  align-items: flex-start; justify-content: space-between;
  margin-block-end: 1rem;
}
.payment-panel-head h2 { margin: 0 0 .3rem; font-size: 1.05rem; }
.payment-panel-head p { margin: 0; font-size: .84rem; }
.payment-panel .stat-row { margin-block-end: 1.1rem; }

/* Fetching, or failed to fetch. */
.panel-status { margin-block: 1.2rem; font-size: .9rem; }
.panel-status.error { color: var(--danger); font-weight: 500; }

/* A dated filter keeps its caption beside the field: the bar is a row of
   controls, and a stacked label would make one of them twice as tall. */
.filter-date {
  display: flex; align-items: center; gap: .4rem; flex: 1 1 215px;
  font-size: .82rem; font-weight: 500; color: var(--ink-soft);
}
.filter-date input { flex: 1 1 auto; min-width: 0; }


/* == 19  Utilities, motion & print ========================================= */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}
.text-center { text-align: center; }
.mt-0 { margin-top: 0; }
.mb-0 { margin-bottom: 0; }

/* Sections fade up the first time they enter the viewport. Purely additive:
   without JS the elements are simply visible (see the .no-js guard in
   /static/js/app.js, which adds .is-visible immediately if IO is missing). */
[data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity .7s var(--ease), transform .7s var(--ease);
}
[data-reveal].is-visible { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  [data-reveal] { opacity: 1; transform: none; }
  .card:hover .card-media img { transform: none; }
}

@media (max-width: 640px) {
  /* Two per row, stated rather than inferred: the whole point of the compact
     card is that a phone shows a pair of them. */
  .grid-compact { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .75rem; }
  .card-compact .card-body { padding: .6rem .65rem .7rem; }
  .card-compact .card-body h3 { font-size: .86rem; }
  .card-compact .card-body p { font-size: .72rem; }
  .card-excerpt { -webkit-line-clamp: 2; }
  .leader-card { padding: .85rem .5rem .9rem; }
  .leader-card .avatar { width: 92px; height: 92px; }
  .leader-card .card-facts { font-size: .72rem; }
}

@media (max-width: 780px) {
  .masonry { columns: 2 160px; }
  .table { display: block; overflow-x: auto; }
  .stat-row { grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: .7rem; }
  .stat-row li { padding: .85rem .9rem; }
  .stat-row strong { font-size: 1.15rem; }
  .leader-head-stats { margin-inline-start: 0; width: 100%; }
  .leader-head-stats li { flex: 1; min-width: 0; padding: .7rem .8rem; }
  .dev-admin-row { grid-template-columns: 1fr; }
  .document-stage { width: 100%; margin-inline: 0; }
  .form-section { padding: 1.1rem; }
}

@media print {
  .site-header, .site-footer, .panel-nav, .row-actions, .filter-bar,
  .map-hero, .scroll-cue { display: none !important; }
  body { background: #fff; }
  .card, .table, .form-section { border-color: #ccc; box-shadow: none; }
  .roster-table { min-width: 0; font-size: .7rem; }
}


/* == 20  Tour stories ======================================================
   A rail of rings above the tour grid, one per tour that leaders or admins
   have posted media to, plus the full-screen viewer those rings open.
   The rail is a plain list of links until /static/js/stories.js loads.
   ------------------------------------------------------------------------ */
.stories {
  padding-block: clamp(1.75rem, 3vw, 2.75rem);
  border-bottom: 1px solid var(--line);
}

.stories-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.3rem;
}
.stories-head h2 { margin: 0; font-size: clamp(1.15rem, 1rem + .5vw, 1.4rem); }
.stories-head > a {
  font-size: .88rem; font-weight: 600; text-decoration: none;
  color: var(--brand-soft); white-space: nowrap;
}
.stories-head > a:hover { color: var(--accent); }

/* Under dir="rtl" the first ring lands on the right and the rail scrolls
   right-to-left with no extra rules. */
.stories-rail {
  display: flex;
  gap: 1.1rem;
  list-style: none;
  margin: 0;
  padding: .25rem .1rem 1rem;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}
.stories-item {
  flex: 0 0 auto;
  width: 92px;
  scroll-snap-align: start;
  display: grid;
  justify-items: center;
  gap: .5rem;
}

/* The ring itself is a gradient border drawn with a padded background, so it
   stays a perfect circle at any size without an extra element. */
.story-ring {
  position: relative;
  display: block;
  width: 84px;
  height: 84px;
  border-radius: 50%;
  padding: 3px;
  background: var(--line-strong);
  text-decoration: none;
  transition: transform .18s var(--ease), background .18s var(--ease);
}
.story-ring:hover { transform: translateY(-2px); }
.story-ring:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* Anything posted in the last week gets the ember ring; older tours keep the
   quiet one, so "new" actually reads as new. */
.story-ring.is-fresh {
  background: conic-gradient(from 200deg, var(--ember-500), var(--route-red), var(--bark-500), var(--ember-500));
}

.story-ring-inner {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  overflow: hidden;
  background: var(--surface-sunk);
  border: 2px solid var(--paper);
  display: grid;
  place-items: center;
}
.story-ring-inner img { width: 100%; height: 100%; object-fit: cover; display: block; }
.story-ring-placeholder { font-size: 1.7rem; }

.story-badge {
  position: absolute;
  inset-block-end: 0;
  inset-inline-end: 0;
  width: 24px; height: 24px;
  border-radius: 50%;
  background: var(--bark-800);
  color: var(--bone-200);
  border: 2px solid var(--paper);
  display: grid; place-items: center;
  font-size: .6rem;
  line-height: 1;
  padding-inline-start: 2px;   /* optically centre the triangle */
}

.story-name {
  font-size: .76rem;
  line-height: 1.4;
  text-align: center;
  color: var(--ink-soft);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* --- the viewer ---------------------------------------------------------- */
.story-viewer {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: rgba(16, 18, 8, .96);
  display: grid;
  place-items: center;
  padding: clamp(0px, 2vw, 1.5rem);
}
.story-viewer[hidden] { display: none; }

/* The media fills the stage and the chrome floats over it. A grid with
   auto/1fr/auto rows pushes the header and footer out of the media's way
   instead of on top of it, which is not what a story looks like. */
.story-viewer-stage {
  position: relative;
  width: min(440px, 100%);
  height: min(880px, 100%);
  background: var(--bark-950);
  border-radius: clamp(0px, 1.4vw, var(--radius-lg));
  overflow: hidden;
}

.story-progress {
  position: absolute;
  inset-inline: .6rem;
  top: .6rem;
  z-index: 4;
  display: flex;
  gap: 4px;
}
.story-progress-seg {
  flex: 1;
  height: 2px;
  border-radius: 2px;
  background: rgba(227, 213, 189, .28);
  overflow: hidden;
}
.story-progress-seg i {
  display: block;
  height: 100%;
  width: 100%;
  background: var(--bone-200);
  transform: scaleX(0);
  transform-origin: left center;
}
body.rtl .story-progress-seg i { transform-origin: right center; }

.story-viewer-head {
  position: absolute;
  inset-inline: 0;
  top: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .8rem;
  padding: 1.5rem 1rem .8rem;
  background: linear-gradient(to bottom, rgba(16, 18, 8, .75), transparent);
}
.story-viewer-title {
  font-weight: 600;
  font-size: .92rem;
  color: var(--bone-100);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.story-viewer-close {
  flex: 0 0 auto;
  width: 34px; height: 34px;
  border: 0; border-radius: 50%;
  background: rgba(227, 213, 189, .14);
  color: var(--bone-100);
  font-size: 1.4rem; line-height: 1;
  cursor: pointer;
  display: grid; place-items: center;
}
.story-viewer-close:hover { background: rgba(227, 213, 189, .26); }

.story-viewer-media {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: grid;
  place-items: center;
  overflow: hidden;
}
.story-media {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: contain;   /* never crop someone's photo */
  display: block;
}

/* A portrait photo in a taller frame leaves bands top and bottom. Filling
   them with a blurred, darkened copy of the same shot makes the letterbox
   read as deliberate rather than as a broken image. */
.story-media-backdrop {
  position: absolute;
  inset: -8%;
  z-index: 0;
  background-position: center;
  background-size: cover;
  filter: blur(32px) brightness(.42) saturate(1.1);
  transform: scale(1.08);
  pointer-events: none;
}

.story-viewer-foot {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.4rem 1rem 1.1rem;
  background: linear-gradient(to top, rgba(16, 18, 8, .82), transparent);
}
.story-viewer-caption {
  margin: 0;
  font-size: .8rem;
  color: rgba(227, 213, 189, .8);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.story-viewer-foot .btn { flex: 0 0 auto; }

/* Half-screen tap targets rather than small arrows: the gesture people
   already know from every other story UI. Transparent, and below the head
   and foot so the close button and the link stay clickable. */
.story-nav {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 40%;
  z-index: 3;
  border: 0;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.story-nav-prev { inset-inline-start: 0; }
.story-nav-next { inset-inline-end: 0; }
.story-nav:focus-visible { outline: 2px solid var(--accent); outline-offset: -4px; }

@media (max-width: 560px) {
  .stories-item { width: 76px; }
  .story-ring { width: 70px; height: 70px; }
  .story-viewer { padding: 0; }
  .story-viewer-stage { width: 100%; height: 100%; border-radius: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .story-ring:hover { transform: none; }
}
