/* fonts.css */
/*
 * The four faces, self-hosted and subset.
 *
 * Declared, not forced. A browser downloads a face only when rendered text
 * actually uses it — so the English page never fetches Peshang or Bahij, and
 * the Kurdish page never fetches Lato for its body copy. This is the part
 * CanvasKit could not do: it read FontManifest.json and fetched every face
 * before it painted a single frame, which is most of what the old first load
 * was spent on.
 *
 * Served from this origin rather than a font CDN: a third-party font request
 * costs a DNS lookup, a TLS handshake and a round trip before a glyph can be
 * drawn, and browsers no longer share a cross-site font cache, so the
 * "someone else already downloaded it" argument no longer holds.
 *
 * `font-display: swap` means text paints immediately in the fallback and
 * re-renders once the real face lands — never a blank page waiting on a font.
 */

/* Kurdish and Arabic headings — the brand's own voice. One weight, so the
   hierarchy comes from size rather than synthetic bolding. */
@font-face {
  font-family: 'Peshang';
  src: url('/fonts/peshang.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}

/* Kurdish and Arabic body copy, labels and numbers. Full Kurdish, Arabic,
   Latin and Arabic-Indic digit coverage, in the two weights the design uses. */
@font-face {
  font-family: 'Bahij';
  src: url('/fonts/bahij-400.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Bahij';
  src: url('/fonts/bahij-700.woff2') format('woff2');
  font-weight: 700;
  font-display: swap;
}

/* English throughout, and every Latin numeral, handle and phone number in
   all three languages. */
@font-face {
  font-family: 'Lato';
  src: url('/fonts/lato.woff2') format('woff2');
  font-weight: 400 900;
  font-display: swap;
}

/* tokens.css */
/*
 * Every colour, size, radius and rhythm the site uses.
 *
 * Ported from lib/core/theme/app_colors.dart and app_theme.dart. Nothing
 * outside this file names a raw colour, so retuning the palette is a change
 * in one place.
 *
 * There is one theme. The brand is a deep teal ground lit by cyan, and it
 * only reads that way in the dark — a light variant washes the palette out,
 * so the site ships in one theme rather than a weaker second one.
 */

:root {
  /* ── Core brand — sampled from the campaign artwork ──────────────────── */
  --abyss: #021416;        /* deepest ground: the page itself */
  --deep-teal: #062e30;    /* primary brand background */
  --surface: #0a3a3d;      /* cards and sheets */
  --surface-high: #104a4e; /* hover, higher elevation */
  --teal: #0e5c60;         /* mid tone, for gradients and borders */

  /* ── Accents ────────────────────────────────────────────────────────── */
  --accent: #00d2df;       /* the signature luminous cyan */
  --accent-soft: #5fe3ec;
  --accent-deep: #0f96a0;

  /* Warm gold, from the "digital marketing" key visual. Numbers, awards and
     premium badges only — it stops reading as premium the moment it is
     everywhere. */
  --gold: #d9a94a;
  --gold-soft: #f2d28a;

  /* ── Neutrals ───────────────────────────────────────────────────────── */
  --white: #f4fbfb;
  --text-primary: #eaf6f6;
  --text-secondary: #a9c6c7;
  --text-muted: #6e8f90;

  /* ── Semantic ───────────────────────────────────────────────────────── */
  --success: #3ed598;
  --error: #ff6b6b;

  /* ── The accents again, as raw channels ─────────────────────────────── */
  /* Wanted wherever a colour is used at partial alpha: `rgb(var(--rgb-accent)
     / 0.18)` keeps the value in one place, where `#00d2df2e` would not. */
  --rgb-accent: 0 210 223;
  --rgb-accent-soft: 95 227 236;
  --rgb-gold: 217 169 74;
  --rgb-success: 62 213 152;
  --rgb-white: 244 251 251;
  --rgb-text: 234 246 246;
  --rgb-abyss: 2 20 22;
  --rgb-surface: 10 58 61;
  --rgb-deep-teal: 6 46 48;
  --rgb-teal: 14 92 96;

  /* ── Spacing — AppTheme.s* ──────────────────────────────────────────── */
  --s4: 4px;
  --s8: 8px;
  --s12: 12px;
  --s16: 16px;
  --s20: 20px;
  --s24: 24px;
  --s32: 32px;
  --s48: 48px;
  --s64: 64px;
  --s96: 96px;

  /* ── Radii ──────────────────────────────────────────────────────────── */
  --r-sm: 10px;
  --r-md: 18px;
  --r-lg: 28px;
  --r-xl: 40px;

  /* ── Layout ─────────────────────────────────────────────────────────── */
  --max-content: 1240px;

  /* Fluid rather than stepped by breakpoint — one clamp() replaces the
     Responsive.value() ladder the Flutter build carried, and it has no
     breakpoint for a window to land awkwardly on. */
  --gutter: clamp(20px, 5vw, 72px);
  --section-gap: clamp(72px, 9vw, 140px);

  /* ── Motion ─────────────────────────────────────────────────────────── */
  --ease: cubic-bezier(0.33, 0, 0.2, 1);
  --fast: 0.2s;
  --base: 0.3s;
  --slow: 0.7s;

  color-scheme: dark;
}

/* base.css */
/*
 * The reset, the type scale and the page's own layout.
 */

*,
*::before,
*::after {
  box-sizing: border-box;
}

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

  /*
   * `clip`, deliberately, and not `hidden`.
   *
   * Nothing is meant to reach past the side of the page, but anything that
   * does — a glow, a ring, an animation mid-flight — makes the whole document
   * pannable, and on a phone that turns every vertical swipe into a small
   * sideways slide. This stops that.
   *
   * `hidden` would do it too, and would bring back everything it cost the
   * first time: it forces the other axis to compute to `auto`, which makes
   * the element a scroll container, which is what tied the viewport's width
   * to whether a scrollbar was on screen and what breaks `position: sticky`
   * in so many stylesheets. `clip` simply clips.
   */
  overflow-x: clip;

  /*
   * No `scrollbar-gutter: stable` here.
   *
   * It was added to quiet a layout-shift reading when the splash releases the
   * scroll lock — and that reading turned out not to be movement at all:
   * measured directly, the boxes underneath are identical before and after,
   * and what the browser was reporting was the overlay being removed.
   *
   * The cost is not imaginary. Where scrollbars are overlays that fade in and
   * out — macOS being the obvious case — reserving a gutter ties the
   * viewport's inline size to whether a scrollbar happens to be on screen, and
   * anything anchored to the viewport edge slides a dozen pixels a second or
   * two after a scroll ends. The floating buttons are anchored to exactly that
   * edge.
   */

  /* The ground lives here, not on <body>: the aurora is a fixed layer at
     z-index -1, which paints above the root background but below the page. A
     background on <body> would cover it. */
  background: var(--abyss);
}

/*
 * `overflow-x: hidden` here as well as `clip` on the root.
 *
 * It was taken out once, for a real reason: it forces `overflow-y` to compute
 * to `auto`, which makes <body> a scroll container, which is what tied the
 * viewport's width to the scrollbar gutter and what breaks `position: sticky`
 * in so many stylesheets. Neither of those applies any more — the gutter is
 * gone and the bar is `fixed`, not sticky — and belt and braces is the right
 * answer for a symptom this hard to reproduce off the device it happens on.
 */
body {
  overflow-x: hidden;

  margin: 0;
  background: transparent;
  color: var(--text-primary);
  font-family: 'Bahij', 'Lato', system-ui, sans-serif;
  font-size: 15px;
  line-height: 1.78;
  -webkit-font-smoothing: antialiased;
}

/* English swaps both faces for Lato, as AppTypography did for LTR. */
html[lang='en'] body {
  font-family: 'Lato', system-ui, sans-serif;
}

/* Peshang sits high with short descenders and ships a single weight, so on
   Kurdish and Arabic the hierarchy comes from size. Lato has real weights,
   so English gets them. */
h1,
h2,
h3,
h4 {
  margin: 0;
  font-family: 'Peshang', 'Bahij', 'Lato', system-ui, sans-serif;
  font-weight: 400;
  line-height: 1.16;
}

html[lang='en'] :is(h1, h2, h3, h4) {
  font-family: 'Lato', system-ui, sans-serif;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.08;
}

p,
figure,
blockquote {
  margin: 0;
}

img {
  max-width: 100%;
  display: block;
}

/* `display: contents` makes <picture> disappear from layout entirely, so the
   <img> inside it is positioned by the same rules as a bare <img> would be.
   Without this, every rule that targets an image would need a second one for
   the wrapper that only exists to offer an AVIF. */
picture {
  display: contents;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
}

ul,
ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

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

/* ── Layout ──────────────────────────────────────────────────────────────── */

.container {
  width: 100%;
  max-width: var(--max-content);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/*
 * Half the gap above, half below.
 *
 * Padding does not collapse the way margins do, so a full gap on each of two
 * neighbouring sections is two full gaps of empty page between them. Each
 * section contributes half, and the space between any two of them comes out
 * at `--section-gap` — which is the figure that was tuned.
 */
.section {
  padding-block: calc(var(--section-gap) / 2);
}

/* The band directly under a page header, which already carries its own top
   padding — so this one only needs the gap above the content. */
.section-top {
  padding-top: var(--s48);
}

.section-bottom {
  padding-bottom: var(--section-gap);
}

.pad {
  margin-top: var(--s48);
}

/* ── Type scale — AppTypography, expressed fluidly ───────────────────────── */

.display-xl {
  font-size: clamp(2.8rem, 7.5vw, 4.5rem);
  line-height: 1.06;
}

.headline {
  font-size: clamp(1.75rem, 4vw, 2.6rem);
  line-height: 1.18;
}

.title {
  font-size: clamp(1.25rem, 2.4vw, 1.6rem);
  line-height: 1.28;
}

.lead {
  font-size: clamp(1rem, 1.6vw, 1.125rem);
  line-height: 1.75;
  color: var(--text-secondary);
}

.body {
  font-size: 0.9375rem;
  line-height: 1.78;
  color: var(--text-secondary);
}

.label {
  font-family: 'Bahij', 'Lato', sans-serif;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
}

html[lang='en'] .label {
  font-family: 'Lato', sans-serif;
}

/* Latin content inside an RTL page — a phone number, a handle, an email. */
.ltr {
  font-family: 'Lato', sans-serif;
}

/*
 * A Latin run in an RTL page is marked `dir="ltr"` so that its digits and its
 * leading `+` are ordered the way they are written. That also flips what
 * `start` means for the run itself, so as a block it drifts to the left edge
 * while the Kurdish label above it stays on the right — a phone number and
 * its own caption ending up on opposite sides of the same card.
 *
 * The run keeps its direction; only the box is pulled back to the page's
 * start edge. Anything centred by its parent is left alone, because the box
 * there is only as wide as the text.
 */
[dir='rtl'] :is(.channel__v, .socials__h, .footer__col .ltr) {
  text-align: right;
}

/* ── Gradient text — the Flutter GradientText widget ─────────────────────── */

.gradient-text,
.gradient-gold,
.gradient-cyan {
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.gradient-text {
  background-image: linear-gradient(120deg, var(--white), var(--accent-soft) 55%, var(--accent));
}

.gradient-gold {
  background-image: linear-gradient(135deg, var(--gold-soft), var(--gold));
}

.gradient-cyan {
  background-image: linear-gradient(135deg, var(--accent), rgb(var(--rgb-accent) / 0.55));
}

/* components.css */
/*
 * The pieces that appear on more than one page: buttons, glass, badges,
 * the bar, the wordmark, the footer, the floating button.
 */

/* ── Glass card — core/widgets/glass_card.dart ───────────────────────────── */

.glass {
  background: linear-gradient(
    135deg,
    rgb(var(--rgb-surface) / 0.75),
    rgb(var(--rgb-deep-teal) / 0.45)
  );
  border: 1px solid rgb(var(--rgb-accent) / 0.18);
  border-radius: var(--r-lg);
  backdrop-filter: blur(14px);
  box-shadow: 0 18px 50px rgb(var(--rgb-abyss) / 0.55);
}

/* ── Glow button — core/widgets/glow_button.dart ─────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 14px 26px;
  border-radius: 100px;
  border: 1px solid transparent;
  font-family: 'Bahij', 'Lato', sans-serif;
  font-size: 0.9375rem;
  font-weight: 700;
  line-height: 1.4;
  cursor: pointer;
  transition:
    transform 0.25s var(--ease),
    box-shadow 0.25s ease,
    background var(--fast) ease,
    border-color var(--fast) ease;
}

html[lang='en'] .btn {
  font-family: 'Lato', sans-serif;
}

.btn svg {
  width: 18px;
  height: 18px;
  flex: none;
}

.btn-primary {
  background: linear-gradient(135deg, var(--accent), var(--accent-deep));
  color: var(--abyss);
  box-shadow: 0 8px 26px rgb(var(--rgb-accent) / 0.32);
}

.btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 14px 38px rgb(var(--rgb-accent) / 0.45);
}

.btn-outline {
  background: rgb(var(--rgb-white) / 0.04);
  border-color: rgb(var(--rgb-accent) / 0.42);
  color: var(--text-primary);
}

.btn-outline:hover {
  transform: translateY(-2px);
  border-color: var(--accent);
  background: rgb(var(--rgb-accent) / 0.1);
}

/* The third choice on the call-to-action: the same shape, standing back a
   little so the eye lands on the first two before it finds this one. */
.btn-ghost {
  background: rgb(var(--rgb-white) / 0.06);
  border-color: rgb(var(--rgb-white) / 0.24);
  color: var(--text-secondary);
}

.btn-ghost:hover {
  transform: translateY(-2px);
  border-color: var(--accent);
  color: var(--text-primary);
  background: rgb(var(--rgb-accent) / 0.08);
}

.more-btn {
  margin-top: var(--s32);
  display: grid;
  justify-items: center;
}

/* ── Eyebrow — the pill with the live dot ────────────────────────────────── */

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 8px 16px;
  border-radius: 100px;
  background: rgb(var(--rgb-accent) / 0.1);
  border: 1px solid rgb(var(--rgb-accent) / 0.3);
  color: var(--accent-soft);
  font-family: 'Bahij', 'Lato', sans-serif;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  line-height: 1.5;
}

html[lang='en'] .eyebrow {
  font-family: 'Lato', sans-serif;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.eyebrow .dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  flex: none;
}

/* ── Section heading ─────────────────────────────────────────────────────── */

.head {
  display: grid;
  gap: var(--s16);
}

.head--center {
  justify-items: center;
  text-align: center;
  max-width: 720px;
  margin-inline: auto;
}

.head--start {
  justify-items: start;
  text-align: start;
}

.sub {
  max-width: 660px;
}

/* ── The availability badge, in green ────────────────────────────────────── */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 9px 16px;
  border-radius: 100px;
  background: rgb(var(--rgb-success) / 0.1);
  border: 1px solid rgb(var(--rgb-success) / 0.32);
  color: var(--success);
  font-size: 0.78rem;
  font-weight: 700;
  line-height: 1.35;
}

.badge--avail {
  margin-bottom: var(--s32);
  font-size: 0.82rem;
}

.badge__dot {
  position: relative;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--success);
  flex: none;
}

.badge__dot::before {
  content: '';
  position: absolute;
  inset: -1px;
  border-radius: 50%;
  background: var(--success);
  animation: pulse 1.6s ease-out infinite;
}

/* ── Tone helpers, used by every icon chip on the site ───────────────────── */

.tone-cyan {
  background: rgb(var(--rgb-accent) / 0.12);
  border: 1px solid rgb(var(--rgb-accent) / 0.28);
  color: var(--accent);
}

.tone-gold {
  background: rgb(var(--rgb-gold) / 0.12);
  border: 1px solid rgb(var(--rgb-gold) / 0.3);
  color: var(--gold);
}

.tone-soft {
  background: rgb(var(--rgb-accent-soft) / 0.1);
  border: 1px solid rgb(var(--rgb-accent-soft) / 0.26);
  color: var(--accent-soft);
}

.tone-green {
  background: rgb(var(--rgb-success) / 0.14);
  border: 1px solid rgb(var(--rgb-success) / 0.3);
  color: var(--success);
}

/* ── Flags ───────────────────────────────────────────────────────────────── */

.flag {
  display: inline-block;
  border-radius: 3px;
  overflow: hidden;
  flex: none;
  box-shadow:
    0 1px 4px rgb(0 0 0 / 0.28),
    inset 0 0 0 0.5px rgb(255 255 255 / 0.22);
}

.flag svg {
  display: block;
}

/* ── Wordmark ────────────────────────────────────────────────────────────── */

.brand {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.brand__sign {
  height: var(--brand-h);
  width: auto;
  /* The ink is grey on transparency; this makes it flat white, so one file
     serves wherever the mark appears. */
  filter: brightness(0) invert(1);
  opacity: 0.95;
}

.brand__text {
  display: grid;
  min-width: 0;
}

.brand__name {
  /* Bahij carries the marks Peshang does not have — see .hero__role. */
  font-family: 'Peshang', 'Bahij', 'Lato', sans-serif;
  font-size: 1.125rem;
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

html[lang='en'] .brand__name {
  font-family: 'Lato', sans-serif;
  font-weight: 800;
}

/* Title case, not uppercase: brand_mark.dart set this as labelSmall at 9px
   with 1.1 of tracking, and Flutter does not upper-case a label the way a
   CSS `text-transform` would. */
.brand__role {
  font-family: 'Lato', sans-serif;
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.11em;
  color: rgb(var(--rgb-accent) / 0.8);
  white-space: nowrap;
}

@media (max-width: 480px) {
  .brand__text {
    display: none;
  }
}

/* ── The bar ─────────────────────────────────────────────────────────────── */

/*
 * Laid over the page, not part of it.
 *
 * This is the one thing `position: sticky` could not do here. The bar tightens
 * from 76px to 60px as it sticks, and a sticky element is still in flow — so
 * every time it shrank, the whole document was pulled up by those 16px, and a
 * visitor scrolling a few pixels past the threshold saw the page twitch under
 * them. `nav_bar.dart` sat in a Stack for the same reason.
 *
 * Nothing below reserves space for it, and nothing needs to: the hero and
 * every page header already open with 120px or more of top padding, which is
 * what lets the bar sit clear over the hero photograph in the first place.
 */
.nav {
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: 50;
  /* Fully clear over the hero — no colour, no blur, no rule — exactly as
     nav_bar.dart had it. The bar only materialises once the page moves. */
  background: transparent;
  backdrop-filter: none;
  border-bottom: 1px solid transparent;
  transition:
    background var(--base) ease,
    border-color var(--base) ease,
    backdrop-filter var(--base) ease;
}

.nav.is-scrolled {
  /* abyss at 0.72 behind an 18px blur — the pair the Flutter bar used. At
     that opacity the teal underneath still reads through, and that is what
     gives the bar its colour. At 0.9 it stopped being translucent and read as
     a flat black band laid over a teal page. */
  background: rgb(var(--rgb-abyss) / 0.72);
  backdrop-filter: blur(18px);
  border-bottom-color: rgb(var(--rgb-text) / 0.08);
}

/* The bar tightens as it sticks. */
.nav__inner {
  display: flex;
  align-items: center;
  gap: var(--s24);
  height: 76px;
  transition: height var(--base) ease;
}

.nav.is-scrolled .nav__inner {
  height: 60px;
}

.nav__links {
  display: none;
  gap: clamp(14px, 1.8vw, 26px);
  margin-inline: auto;
}

.nav__links a {
  position: relative;
  font-size: 0.925rem;
  color: var(--text-secondary);
  padding-block: 6px;
  white-space: nowrap;
  transition: color var(--fast) ease;
}

.nav__links a::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  height: 2px;
  background: var(--accent);
  border-radius: 2px;
  transform: scaleX(0);
  transition: transform 0.25s var(--ease);
}

.nav__links a:hover {
  color: var(--text-primary);
}

.nav__links a:hover::after,
.nav__links a[aria-current='page']::after {
  transform: scaleX(1);
}

.nav__links a[aria-current='page'] {
  color: var(--accent);
}

.nav__right {
  display: flex;
  align-items: center;
  gap: var(--s12);
  margin-inline-start: auto;
}

/* ── Language dropdown ───────────────────────────────────────────────────── */

.lang {
  display: none;
  position: relative;
}

.lang__current {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border-radius: 100px;
  background: rgb(var(--rgb-white) / 0.06);
  color: var(--text-secondary);
  font-size: 0.85rem;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
  /* Both are needed to be rid of the native disclosure triangle. */
  list-style: none;
  transition:
    background var(--fast) ease,
    color var(--fast) ease;
}

.lang__current::-webkit-details-marker {
  display: none;
}

.lang__current:hover {
  color: var(--text-primary);
}

.lang[open] .lang__current {
  background: rgb(var(--rgb-accent) / 0.14);
  color: var(--accent);
}

.lang__chev {
  flex: none;
  opacity: 0.7;
  transition: rotate var(--fast) ease;
}

.lang[open] .lang__chev {
  rotate: 180deg;
}

.lang__menu {
  position: absolute;
  inset-inline-end: 0;
  top: calc(100% + 8px);
  z-index: 1;
  min-width: 190px;
  padding: 6px;
  display: grid;
  gap: 2px;
  border-radius: var(--r-md);
  background: rgb(var(--rgb-abyss) / 0.97);
  border: 1px solid rgb(var(--rgb-white) / 0.1);
  box-shadow: 0 20px 44px rgb(var(--rgb-abyss) / 0.6);
  backdrop-filter: blur(18px);
}

.lang__opt {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: var(--r-sm);
  font-size: 0.9rem;
  color: var(--text-secondary);
  white-space: nowrap;
  transition:
    background var(--fast) ease,
    color var(--fast) ease;
}

.lang__opt:hover {
  background: rgb(var(--rgb-white) / 0.06);
  color: var(--text-primary);
}

.lang__opt.is-on {
  background: rgb(var(--rgb-accent) / 0.12);
  color: var(--accent);
  font-weight: 700;
}

.nav__cta {
  display: none;
  padding: 11px 22px;
  font-size: 0.9rem;
}

.burger {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border: 1px solid rgb(var(--rgb-white) / 0.12);
  border-radius: 12px;
  background: transparent;
  cursor: pointer;
  color: var(--text-primary);
}

.mobile-menu {
  border-top: 1px solid rgb(var(--rgb-white) / 0.07);
  background: rgb(var(--rgb-abyss) / 0.97);
  /* The bar is fixed, so the panel hanging off it cannot grow past the
     viewport and be scrolled to — it has to scroll on its own. */
  max-height: calc(100vh - 60px);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.mobile-menu nav {
  display: grid;
  padding-block: var(--s12);
}

.mobile-menu nav > a {
  padding: 13px 0;
  font-size: 0.95rem;
  color: var(--text-secondary);
  border-bottom: 1px solid rgb(var(--rgb-white) / 0.05);
}

.mobile-menu__title {
  margin-top: var(--s16);
  padding-top: var(--s16);
  border-top: 1px solid rgb(var(--rgb-white) / 0.07);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.mobile-menu__langs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s8);
  padding-block: var(--s12);
}

.mobile-menu__langs .lang-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 14px;
  border-radius: 100px;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--text-muted);
  background: rgb(var(--rgb-white) / 0.05);
  font-family: 'Bahij', 'Lato', sans-serif;
  transition:
    background var(--fast) ease,
    color var(--fast) ease;
}

html[lang='en'] .mobile-menu__langs .lang-btn {
  font-family: 'Lato', sans-serif;
}

.mobile-menu__langs .lang-btn.is-on {
  background: rgb(var(--rgb-accent) / 0.16);
}

.mobile-menu__langs .lang-btn:hover {
  color: var(--text-primary);
}

@media (min-width: 720px) {
  .lang {
    display: block;
  }
}

@media (min-width: 1100px) {
  .nav__links {
    display: flex;
  }

  /*
   * Only from here. `.nav__links ~ .nav__right` matches whether or not the
   * links are displayed — a sibling combinator asks what is in the document,
   * not what is painted — so written outside a query it cancelled the auto
   * margin at every width, and on a phone the menu button sat against the
   * wordmark in the middle of the bar instead of in the opposite corner.
   *
   * Once the links really are visible they take the middle themselves, and
   * the group on the end no longer needs to push itself over.
   */
  .nav__links ~ .nav__right {
    margin-inline-start: 0;
  }

  .nav__cta {
    display: inline-flex;
  }

  .burger,
  .mobile-menu {
    display: none;
  }
}

/* ── Visitor counter ─────────────────────────────────────────────────────── */

/* Hidden, but holding its place. Removing it from the layout until the number
   arrived meant the footer grew by 40px a moment after it was painted, which
   is a jump for anyone already reading the bottom of the page. `visibility`
   keeps the space and still hides it from a screen reader. */
.visitors {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 9px 14px;
  border-radius: 100px;
  background: rgb(var(--rgb-accent) / 0.07);
  border: 1px solid rgb(var(--rgb-accent) / 0.22);
  color: var(--accent-soft);
  font-size: 0.85rem;
  font-weight: 700;
  visibility: hidden;
  opacity: 0;
  transition: opacity 0.6s ease;
}

.visitors.is-in {
  visibility: visible;
  opacity: 1;
}

.visitors__ico {
  color: var(--accent);
  flex: none;
}

.visitors__label {
  color: var(--text-muted);
  font-weight: 400;
}

.visitors [data-visitor-count] {
  font-variant-numeric: tabular-nums;
}

/* ── Footer ──────────────────────────────────────────────────────────────── */

.footer {
  border-top: 1px solid rgb(var(--rgb-text) / 0.08);
  background: rgb(var(--rgb-deep-teal) / 0.55);
  padding-block: var(--s64);
}

/* On a phone the brand takes the full width and the two short columns sit
   side by side under it — the arrangement site_footer.dart used when
   Responsive reported isCompact. */
.footer__inner {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s48) var(--s24);
  align-items: start;
}

.footer__brand {
  grid-column: 1 / -1;
}

@media (min-width: 769px) {
  .footer__inner {
    grid-template-columns: 4fr 2fr 3fr;
    gap: var(--s48);
  }

  .footer__brand {
    grid-column: auto;
  }
}

.footer__tagline {
  margin-top: var(--s16);
  max-width: 320px;
}

.footer__socials {
  margin-top: var(--s24);
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.footer__socials a {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  background: rgb(var(--rgb-white) / 0.06);
  color: rgb(var(--rgb-text) / 0.75);
  transition:
    transform var(--fast) var(--ease),
    color var(--fast) ease,
    background var(--fast) ease,
    border-color var(--fast) ease,
    box-shadow var(--fast) ease;
}

.footer__socials a:hover {
  transform: translateY(-3px);
  color: var(--accent);
  background: rgb(var(--rgb-accent) / 0.16);
  border-color: rgb(var(--rgb-accent) / 0.6);
  box-shadow: 0 12px 14px -6px rgb(var(--rgb-accent) / 0.13);
}

.footer__col {
  display: grid;
  align-content: start;
  gap: 10px;
}

.footer__title {
  margin-bottom: 6px;
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--text-primary);
}

.footer__col a {
  font-size: 0.9375rem;
  color: rgb(var(--rgb-text) / 0.66);
  transition: color var(--fast) ease;
}

.footer__col a:hover {
  color: var(--accent);
}

.footer__avail {
  margin-top: 6px;
  justify-self: start;
  align-items: flex-start;
  border-radius: var(--r-md);
  background: rgb(var(--rgb-success) / 0.12);
  border-color: rgb(var(--rgb-success) / 0.35);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  line-height: 1.5;
  text-align: start;
}

/* Nudged onto the first line's optical centre, since the text beside it may
   run to two lines in the narrow column. */
.footer__avail .badge__dot {
  margin-top: 5px;
}

/* The closing block: tally, rule, copyright, credit. The spacing is set per
   row rather than as one grid gap, because the four steps are 32 / 32 / 24 /
   16 and not one repeated figure. */
.footer__meta {
  margin-top: var(--s32);
  display: grid;
  justify-items: center;
}

.footer__rule {
  width: 100%;
  height: 0;
  margin: var(--s32) 0 0;
  border: 0;
  border-top: 1px solid rgb(var(--rgb-text) / 0.08);
}

/* The copyright and the full name read as one line, centred together.
   Pushed to opposite edges they stop being a sentence, and the name lands
   under the floating buttons in the corner, which clip it. */
.footer__legal {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 4px var(--s24);
  margin-top: var(--s24);
  font-size: 0.84rem;
  color: var(--text-muted);
  text-align: center;
}

/* On the narrowest phone the Kurdish phrase plus the name is wider than the
   column, so the name is free to drop onto a line of its own. */
.footer__credit {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 2px 6px;
  margin-top: var(--s16);
  font-size: 0.84rem;
  color: rgb(var(--rgb-text) / 0.45);
}

.footer__credit a {
  font-family: 'Lato', sans-serif;
  font-weight: 600;
  color: rgb(var(--rgb-text) / 0.7);
  border-bottom: 1px solid transparent;
  transition:
    color var(--fast) ease,
    border-color var(--fast) ease;
}

.footer__credit a:hover {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* ── The two buttons that follow the page down ───────────────────────────── */

.floaties {
  position: fixed;
  /* The pulse ring swells 20px past the button on every side, so the button
     has to sit at least that far in or the ring is cut off by the edge of the
     screen — which is exactly what happened at 360px. */
  inset-inline-end: clamp(22px, 3vw, 28px);
  inset-block-end: clamp(22px, 3vw, 28px);
  z-index: 60;
  display: grid;
  justify-items: center;
  gap: 12px;
}

/* Out of the way until there is something to go back up from — the app
   showed it past 600px, and so does this. It slides down out of sight rather
   than merely fading, so it never sits there invisible and still clickable. */
.to-top {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: 12px;
  background: var(--surface-high);
  color: var(--accent);
  cursor: pointer;
  opacity: 0;
  transform: translateY(160%);
  pointer-events: none;
  transition:
    opacity var(--base) var(--ease),
    transform var(--base) var(--ease);
}

.to-top.is-in {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

/*
 * The idle nudge runs on `translate` and `rotate`, not on `transform`.
 *
 * They are separate properties that compose with it, so the hover below still
 * owns `transform` and the two never fight — which they would if both were
 * written as `transform`, since an animation beats a plain declaration and the
 * button would simply stop responding to the pointer.
 */
.fab {
  position: relative;
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 16px;
  background: var(--success);
  color: var(--abyss);
  box-shadow: 0 10px 30px rgb(var(--rgb-success) / 0.35);
  /* Up and inward — toward the middle of the page, away from the corner it
     is pinned to. Mirrored for Arabic and Kurdish, where the button sits in
     the other corner and "inward" is the other way. */
  --fab-drift: -7px;

  animation: fab-drift 2s ease-in-out infinite alternate;
  transition:
    transform 0.25s var(--ease),
    box-shadow 0.25s ease;
}

[dir='rtl'] .fab {
  --fab-drift: 7px;
}

/* Still while it is being aimed at — a target that is moving under the
   cursor is a target that is harder to hit. */
.fab:hover,
.fab:focus-visible {
  animation-play-state: paused;
}

.fab:hover {
  transform: translateY(-3px) scale(1.04);
  box-shadow: 0 16px 40px rgb(var(--rgb-success) / 0.45);
}

.fab > svg {
  position: relative;
}

/*
 * The ring that radiates out of the button, as site_shell.dart drew it: a
 * circle — not the button's rounded square — starting the same 56px across,
 * 2px of the success green at half opacity, growing to 96px as it fades to
 * nothing. `t` there is an eased 0..1 over 2200ms, which is this animation.
 *
 * It is deliberately crisp. Blurring it into a halo makes the button look
 * like it is merely glowing; the drawn ring is what reads as the button
 * asking to be pressed.
 */
.fab__pulse {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid var(--success);
  pointer-events: none;
  animation: fab-pulse 2.2s ease-out infinite;
}

/*
 * Smaller on a phone, where 56px of bright green is a lot of a small screen
 * and it sits over the page the whole time it is being read.
 *
 * The ring is sized from the button, so it shrinks with it: 48px stretching
 * to 82px reaches 17px past the corner, which the 22px inset still clears.
 */
@media (max-width: 640px) {
  .fab {
    width: 48px;
    height: 48px;
    border-radius: 14px;
  }

  .fab > svg {
    width: 22px;
    height: 22px;
  }

  .to-top {
    width: 36px;
    height: 36px;
    border-radius: 11px;
  }

  .to-top > svg {
    width: 18px;
    height: 18px;
  }

  .floaties {
    gap: 10px;
  }
}

/* sections.css */
/*
 * The bands: hero, stats, about, services, clients, gallery, contact, and the
 * title band that opens every inner page.
 */

/* ── Hero ────────────────────────────────────────────────────────────────── */

.hero {
  position: relative;
  isolation: isolate;
}

.cover {
  position: absolute;
  inset-inline: 0;
  top: 0;
  height: clamp(470px, 62vw, 740px);
  overflow: hidden;
  z-index: -1;
}

/* The frame is mirrored, and mirrored about `transform-origin` rather than
   its own centre — which is what actually moves the photographed figure
   across the frame. A 1.06 Ken Burns scale shifts nothing by itself, so the
   origin is the only lever here.

   The origin is tuned per direction so the figure lands in the open band
   between the copy and the portrait and stays visible, instead of sitting
   behind the circle. Unmirrored, the figure fell exactly where the portrait
   sits in LTR and disappeared. */
.cover__img {
  --cover-origin: 62%;

  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  scale: -1 1;
  transform-origin: var(--cover-origin) 57%;
  animation: kenburns 30s ease-in-out infinite alternate;
}

[dir='rtl'] .cover__img {
  --cover-origin: 60%;
}

.cover > div {
  position: absolute;
  inset: 0;
}

.cover__scrim-x {
  --to: right;

  background: linear-gradient(
    to var(--to),
    rgb(var(--rgb-abyss) / 0.9) 0%,
    rgb(var(--rgb-abyss) / 0.62) 46%,
    rgb(var(--rgb-abyss) / 0.12) 100%
  );
}

[dir='rtl'] .cover__scrim-x {
  --to: left;
}

.cover__scrim-y {
  background: linear-gradient(
    to bottom,
    rgb(var(--rgb-abyss) / 0.52) 0%,
    rgb(var(--rgb-abyss) / 0.18) 30%,
    rgb(var(--rgb-abyss) / 0.8) 78%,
    var(--abyss) 100%
  );
}

.cover__beam {
  background: linear-gradient(
    115deg,
    transparent 38%,
    rgb(var(--rgb-accent-soft) / 0.1) 50%,
    transparent 62%
  );
  mix-blend-mode: plus-lighter;
  animation: sweep 11s ease-in-out infinite;
}

.cover__vignette {
  background: radial-gradient(circle at 50% 50%, transparent 55%, rgb(var(--rgb-abyss) / 0.55) 100%);
}

.cover__grain {
  background-image: url('/images/noise.png');
  background-repeat: repeat;
  image-rendering: pixelated;
  opacity: 0.5;
}

.hero__inner {
  padding-top: clamp(120px, 13vw, 150px);
  padding-bottom: clamp(48px, 6vw, 72px);
}

.hero__grid {
  display: grid;
  gap: var(--s48);
  justify-items: center;
}

.hero__copy {
  display: grid;
  justify-items: center;
  text-align: center;
}

/* On a phone the portrait leads, as it did when Flutter's Responsive
   reported isCompact; from a laptop up the copy takes the first column. */
.portrait {
  order: -1;
}

@media (min-width: 1025px) {
  .hero__grid {
    grid-template-columns: 6fr 5fr;
    align-items: center;
    justify-items: stretch;
  }

  .hero__copy {
    justify-items: start;
    text-align: start;
    order: -1;
  }

  .portrait {
    order: 0;
    justify-self: end;
  }
}

.hero__greeting {
  margin-top: var(--s24);
  font-size: 1.0625rem;
  font-weight: 700;
  color: var(--text-secondary);
}

.hero__name {
  margin-top: var(--s8);
}

/*
 * The reserved height is one line of the *typed* text, not of this paragraph.
 *
 * The span inside is far larger than the line it sits on, so the line box is
 * its size — until the typing effect deletes the last character, at which
 * point an empty inline contributes nothing and the box collapses back to
 * whatever `min-height` says. At 2.6em of the parent's 15px that was 39px
 * against a natural 46px, so the hero dropped 7px and sprang back twice per
 * cycle, taking the buttons, the tagline and the re-centred portrait with it.
 *
 * Measured in the same clamp the span uses, the floor and the natural height
 * are the same number and nothing moves.
 */
.hero__role {
  margin-top: var(--s12);
  min-height: calc(clamp(1.1rem, 2.4vw, 1.625rem) * 1.78);
  display: flex;
  align-items: center;
  gap: 2px;
}

/* Bahij is not decoration in this stack, it is the safety net: Peshang has no
   shadda, no fathatan and no Arabic-Indic nine, so the Arabic role — مطوّر —
   renders with a missing-glyph box the moment it is dropped. This is the same
   `rtlFallback` AppFonts declared, and for the same reason. */
.hero__role [data-typed] {
  font-family: 'Peshang', 'Bahij', 'Lato', sans-serif;
  font-size: clamp(1.1rem, 2.4vw, 1.625rem);
  color: var(--accent-soft);
}

html[lang='en'] .hero__role [data-typed] {
  font-family: 'Lato', sans-serif;
  font-weight: 700;
}

.hero__caret {
  width: 2px;
  height: 1.15em;
  background: var(--accent);
  animation: blink 1s steps(2) infinite;
}

.hero__tagline {
  margin-top: var(--s24);
  max-width: 540px;
}

.hero__ctas {
  margin-top: var(--s32);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s16);
  justify-content: center;
}

.hero__rail {
  margin-top: var(--s32);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  justify-content: center;
}

@media (min-width: 1025px) {
  .hero__ctas,
  .hero__rail {
    justify-content: flex-start;
  }
}

.hero__rule {
  width: 34px;
  height: 1px;
  background: rgb(var(--rgb-text) / 0.25);
  margin-inline-end: var(--s8);
}

.hero__rail a {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 12px;
  color: rgb(var(--rgb-text) / 0.72);
  transition:
    color var(--fast) ease,
    background var(--fast) ease,
    transform var(--fast) ease;
}

.hero__rail a:hover {
  color: var(--accent);
  background: rgb(var(--rgb-accent) / 0.1);
  transform: translateY(-2px);
}

/* ── Portrait ────────────────────────────────────────────────────────────── */

/* Every layer is absolutely positioned and centred with `inset:0;margin:auto`.
   The container's height comes from its own aspect-ratio alone, so no child
   can stretch it — which is what turned the circle into an egg when these
   were grid items sized by aspect-ratio. */
.portrait {
  position: relative;
  width: clamp(260px, 30vw, 430px);
  aspect-ratio: 1 / 1;
  flex: none;
}

.portrait__glow,
.portrait__ring,
.portrait__face {
  position: absolute;
  inset: 0;
  margin: auto;
}

.portrait__glow {
  width: 108%;
  height: 108%;
  border-radius: 50%;
  background: radial-gradient(circle, rgb(var(--rgb-accent) / 0.26) 0%, transparent 68%);
}

/*
 * The gradient ring: a full disc of conic gradient with the portrait laid over
 * its middle, so what stays visible is a band of colour.
 *
 * The disc turns on a child, and the parent clips it. That looks like an extra
 * element for nothing — the circle is the same circle at every angle — but a
 * rotating element is measured by the box that encloses it, and the box around
 * a turning square is up to 41% wider than the square. On a 360px phone that
 * pushed the page four pixels wider than the screen, and every vertical swipe
 * picked up a sideways slide. The circle never moved; its bounding box did.
 */
.portrait__ring {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  overflow: hidden;
}

.portrait__ring::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: conic-gradient(
    from 210deg,
    var(--accent) 0deg,
    rgb(var(--rgb-accent) / 0.15) 70deg,
    var(--gold) 150deg,
    rgb(var(--rgb-gold) / 0.25) 215deg,
    var(--accent) 320deg,
    var(--accent) 360deg
  );
  animation: spin 14s linear infinite;
}

.portrait__face {
  width: 88%;
  height: 88%;
  border-radius: 50%;
  object-fit: cover;
  object-position: 50% 42%;
  box-shadow:
    0 0 0 5px var(--abyss),
    0 22px 55px rgb(var(--rgb-abyss) / 0.65);
  animation: float 6s ease-in-out infinite alternate;
}

/* ── Scroll hint ─────────────────────────────────────────────────────────── */

.scroll-hint {
  margin-top: clamp(20px, 2.5vw, 34px);
  display: grid;
  justify-items: center;
  gap: 10px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: rgb(var(--rgb-text) / 0.45);
}

.scroll-hint__mouse {
  width: 26px;
  height: 42px;
  border-radius: 20px;
  border: 1px solid rgb(var(--rgb-text) / 0.22);
  display: grid;
  justify-items: center;
  padding-top: 7px;
}

.scroll-hint__mouse i {
  width: 4px;
  height: 8px;
  border-radius: 4px;
  background: var(--accent);
  animation: wheel 1.8s ease-in-out infinite;
}

/* ── Stats ───────────────────────────────────────────────────────────────── */

.stats {
  display: grid;
  gap: var(--s24);
  padding: clamp(28px, 3vw, 40px) clamp(20px, 3vw, 40px);
  border-radius: var(--r-lg);
  background: linear-gradient(
    135deg,
    rgb(var(--rgb-surface) / 0.75),
    rgb(var(--rgb-deep-teal) / 0.45)
  );
  border: 1px solid rgb(var(--rgb-accent) / 0.18);
  box-shadow: 0 18px 50px rgb(var(--rgb-abyss) / 0.45);
}

.stat {
  text-align: center;
}

.stat + .stat {
  border-top: 1px solid rgb(var(--rgb-text) / 0.08);
  padding-top: var(--s24);
}

.stat__value {
  font-family: 'Bahij', 'Lato', sans-serif;
  font-weight: 900;
  font-size: clamp(1.9rem, 4.5vw, 2.75rem);
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}

html[lang='en'] .stat__value {
  font-family: 'Lato', sans-serif;
}

.stat__caption {
  margin-top: 6px;
  font-size: 0.875rem;
  color: var(--text-muted);
}

@media (min-width: 641px) {
  .stats {
    grid-template-columns: repeat(4, 1fr);
    gap: 0;
  }

  .stat + .stat {
    border-top: 0;
    border-inline-start: 1px solid rgb(var(--rgb-text) / 0.1);
    padding-top: 0;
  }
}

/* ── About ───────────────────────────────────────────────────────────────── */

.about__grid {
  display: grid;
  gap: var(--s48);
  align-items: center;
}

@media (min-width: 1025px) {
  .about__grid {
    grid-template-columns: 5fr 7fr;
    gap: var(--s64);
  }
}

/* _maxWidth = 420 and _offset = 22, straight from the widget. The offsets are
   physical rather than logical: Flutter used Positioned, not
   PositionedDirectional, so the frame falls to the bottom-right in Kurdish,
   Arabic and English alike. */
.frame {
  position: relative;
  justify-self: center;
  width: min(100%, 420px);
  /* The outline overhangs by 6px; nothing may clip it. */
  padding-inline-end: 6px;
  padding-block-end: 6px;
}

.frame__pic {
  position: relative;
  margin-right: 22px;
  margin-bottom: 22px;
  aspect-ratio: 844 / 1500;
  border-radius: var(--r-lg);
  overflow: hidden;
}

.frame__pic img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.frame__veil {
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, rgb(var(--rgb-abyss) / 0.05), rgb(var(--rgb-abyss) / 0.55));
}

.frame__outline {
  position: absolute;
  top: 22px;
  left: 22px;
  right: 0;
  bottom: 0;
  border: 1.6px solid rgb(var(--rgb-accent) / 0.45);
  border-radius: var(--r-lg);
  pointer-events: none;
}

.frame__years {
  position: absolute;
  bottom: 6px;
  right: 6px;
  padding: 16px 20px;
  border-radius: var(--r-md);
  background: linear-gradient(135deg, var(--gold-soft), var(--gold));
  box-shadow:
    0 0 34px rgb(var(--rgb-gold) / 0.45),
    0 12px 32px rgb(var(--rgb-abyss) / 0.5);
  display: grid;
  justify-items: center;
  gap: 1px;
  text-align: center;
}

.frame__n {
  font-size: 1.75rem;
  font-weight: 800;
  line-height: 1.22;
  color: var(--abyss);
}

.frame__c {
  font-family: 'Bahij', 'Lato', sans-serif;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: rgb(var(--rgb-abyss) / 0.8);
}

html[lang='en'] .frame__c {
  font-family: 'Lato', sans-serif;
}

.about__story {
  display: grid;
  gap: var(--s16);
  justify-items: start;
}

.about__story > .head {
  margin-bottom: var(--s8);
}

.quote {
  margin-block: var(--s8);
  padding: var(--s20) var(--s24);
  border-radius: var(--r-md);
  border: 1px solid rgb(var(--rgb-accent) / 0.18);
  border-inline-start: 3px solid var(--accent);
  background: linear-gradient(
    135deg,
    rgb(var(--rgb-surface) / 0.6),
    rgb(var(--rgb-deep-teal) / 0.3)
  );
  color: var(--text-primary);
  font-size: 1rem;
  line-height: 1.85;
}

.pillars {
  margin-block: var(--s8);
  display: grid;
  gap: var(--s20);
  width: 100%;
}

.pillars li {
  display: flex;
  gap: var(--s16);
  align-items: flex-start;
}

.pillars__ico {
  display: grid;
  place-items: center;
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: 14px;
}

.pillars__t {
  font-weight: 700;
  font-size: 1rem;
}

.pillars__d {
  font-size: 0.9rem;
  color: var(--text-muted);
  line-height: 1.7;
}

/* ── Services ────────────────────────────────────────────────────────────── */

.services {
  margin-top: var(--s48);
  display: grid;
  gap: var(--s24);
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}

.card {
  padding: var(--s32) var(--s24);
  display: grid;
  gap: var(--s12);
  align-content: start;
  transition:
    transform var(--base) var(--ease),
    border-color var(--base) ease,
    box-shadow var(--base) ease;
}

.card:hover {
  transform: translateY(-4px);
  border-color: rgb(var(--rgb-accent) / 0.42);
  box-shadow:
    0 26px 60px rgb(var(--rgb-abyss) / 0.6),
    0 0 0 1px rgb(var(--rgb-accent) / 0.12);
}

.card__ico {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  border-radius: 14px;
  color: var(--abyss);
  background: linear-gradient(135deg, var(--accent), var(--accent-deep));
  box-shadow: 0 8px 22px rgb(var(--rgb-accent) / 0.28);
}

.card__title {
  font-size: 1.15rem;
}

/* ── Clients ─────────────────────────────────────────────────────────────── */

/* `auto-fit` for the same reason as the gallery: seven clients must never be
   laid out against a track that is standing empty. */
.clients {
  display: grid;
  gap: var(--s16);
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}

.client {
  display: flex;
  align-items: center;
  gap: var(--s16);
  padding: var(--s20);
  transition:
    transform var(--base) var(--ease),
    border-color var(--base) ease;
}

.client:hover {
  transform: translateY(-3px);
  border-color: rgb(var(--rgb-accent) / 0.4);
}

.client--featured {
  border-color: rgb(var(--rgb-gold) / 0.3);
}

.client__ico {
  display: grid;
  place-items: center;
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: var(--r-sm);
}

.client__name {
  font-weight: 700;
  font-size: 0.95rem;
  line-height: 1.5;
  flex: 1;
}

.client__star {
  color: var(--gold);
  font-size: 0.85rem;
}

.clients__more {
  margin-top: var(--s32);
  text-align: center;
  color: var(--text-muted);
  font-size: 0.9rem;
}

/* ── Gallery ─────────────────────────────────────────────────────────────── */

/*
 * Flex, not grid, for one reason: a row that is not full must still be centred.
 *
 * `auto-fill` was laying down tracks whether or not there was anything to put
 * in them, so the three tiles on the home page sat in the first three of four
 * and left a tile-sized hole on the end — a row shoved to one side under a
 * centred heading. `auto-fit` fixes that, but neither one can centre a last
 * row that is short: with three tiles in two columns, the third is left
 * stranded against the left edge, and no grid property will move it.
 *
 * Flex will. The basis is a clean fraction of the row at each step, so every
 * tile is the same width, and `justify-content: center` puts whatever is left
 * over in the middle. Three across, then two, then one — stepped rather than
 * fluid, because a tile count is a whole number and there is nothing to
 * interpolate between.
 */
.gallery {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--s20);
}

.tile {
  position: relative;
  display: block;
  flex: 0 1 100%;
  padding: 0;
  border: 0;
  cursor: pointer;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--surface);
  box-shadow: 0 16px 44px rgb(var(--rgb-abyss) / 0.45);
  transition:
    transform 0.35s var(--ease),
    box-shadow 0.35s ease;
}

.tile:hover {
  transform: translateY(-4px);
  box-shadow: 0 26px 60px rgb(var(--rgb-abyss) / 0.6);
}

@media (min-width: 560px) {
  .tile {
    flex-basis: calc((100% - var(--s20)) / 2);
  }
}

@media (min-width: 900px) {
  .tile {
    flex-basis: calc((100% - 2 * var(--s20)) / 3);
  }
}

.tile img {
  width: 100%;
  height: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
}

.tile__veil {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top,
    rgb(var(--rgb-abyss) / 0.88) 0%,
    rgb(var(--rgb-abyss) / 0.15) 48%,
    transparent 75%
  );
}

.tile__cap {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  padding: var(--s16);
  display: grid;
  gap: 7px;
  text-align: start;
}

.tile__t {
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--text-primary);
  line-height: 1.5;
}

.tile__v {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--accent);
  opacity: 0;
  transform: translateY(6px);
  transition:
    opacity var(--base) ease,
    transform var(--base) ease;
}

.tile:hover .tile__v,
.tile:focus-visible .tile__v {
  opacity: 1;
  transform: translateY(0);
}

/* `<dialog>` rather than a hand-built overlay: the browser already knows how
   to trap focus inside a modal, close it on Escape and hand focus back. */
.lightbox {
  border: 0;
  padding: 0;
  background: transparent;
  max-width: 100vw;
  max-height: 100vh;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

.lightbox::backdrop {
  background: rgb(var(--rgb-abyss) / 0.92);
  backdrop-filter: blur(6px);
}

.lightbox[open] {
  display: grid;
  place-content: center;
  justify-items: center;
  gap: var(--s16);
}

.lightbox img {
  max-width: min(92vw, 640px);
  max-height: 78vh;
  width: auto;
  height: auto;
  border-radius: var(--r-md);
  box-shadow: 0 30px 80px rgb(0 0 0 / 0.6);
}

.lightbox p {
  color: var(--text-secondary);
  font-size: 0.95rem;
  text-align: center;
  max-width: 60ch;
  padding-inline: var(--s24);
}

.lightbox__close {
  position: fixed;
  inset-block-start: 20px;
  inset-inline-end: 20px;
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: rgb(var(--rgb-white) / 0.08);
  border: 1px solid rgb(var(--rgb-white) / 0.16);
  color: var(--text-primary);
  cursor: pointer;
}

.lightbox__close:hover {
  background: rgb(var(--rgb-accent) / 0.16);
}

/* ── Contact ─────────────────────────────────────────────────────────────── */

.channels {
  display: grid;
  gap: var(--s32);
}

@media (min-width: 1025px) {
  .channels {
    grid-template-columns: 1fr 1fr;
    gap: var(--s48);
    align-items: start;
  }
}

.channels__cards {
  display: grid;
  gap: var(--s16);
  align-content: start;
}

.channel {
  display: flex;
  align-items: center;
  gap: var(--s16);
  padding: var(--s20);
  transition:
    transform 0.28s ease,
    border-color 0.28s ease;
}

.channel:hover {
  transform: translateY(-3px);
}

.channel.tone-green:hover {
  border-color: rgb(var(--rgb-success) / 0.45);
}

.channel.tone-cyan:hover,
.channel.tone-soft:hover {
  border-color: rgb(var(--rgb-accent) / 0.45);
}

/* The tone classes carry the card's hover colour; the chip inside takes the
   fill, so the two cannot drift apart. */
.channel__ico {
  display: grid;
  place-items: center;
  flex: none;
  width: 50px;
  height: 50px;
  border-radius: var(--r-sm);
}

.tone-green > .channel__ico {
  background: rgb(var(--rgb-success) / 0.14);
  border: 1px solid rgb(var(--rgb-success) / 0.3);
  color: var(--success);
}

.tone-cyan > .channel__ico {
  background: rgb(var(--rgb-accent) / 0.14);
  border: 1px solid rgb(var(--rgb-accent) / 0.3);
  color: var(--accent);
}

.tone-soft > .channel__ico {
  background: rgb(var(--rgb-accent-soft) / 0.1);
  border: 1px solid rgb(var(--rgb-accent-soft) / 0.26);
  color: var(--accent-soft);
}

.channel__txt {
  display: grid;
  gap: 3px;
  flex: 1;
  min-width: 0;
}

.channel__t {
  font-weight: 700;
  font-size: 0.95rem;
}

.channel__v {
  font-family: 'Lato', sans-serif;
  font-size: 0.82rem;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
}

.channel__go {
  flex: none;
  opacity: 0.6;
}

.channels__side {
  display: grid;
  gap: var(--s16);
  align-content: start;
}

.channels__follow {
  font-weight: 700;
  font-size: 0.95rem;
}

.socials {
  display: grid;
  gap: var(--s8);
}

.socials a {
  display: flex;
  align-items: center;
  gap: var(--s12);
  padding: 11px 14px;
  border-radius: var(--r-sm);
  border: 1px solid rgb(var(--rgb-white) / 0.08);
  transition:
    border-color var(--fast) ease,
    background var(--fast) ease;
}

.socials a:hover {
  border-color: rgb(var(--rgb-accent) / 0.35);
  background: rgb(var(--rgb-accent) / 0.06);
}

.socials__ico {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 10px;
  flex: none;
  background: rgb(var(--rgb-white) / 0.05);
  color: var(--text-secondary);
}

.socials a:hover .socials__ico {
  color: var(--accent);
}

.socials__txt {
  display: grid;
  gap: 1px;
  flex: 1;
  min-width: 0;
}

.socials__l {
  font-size: 0.875rem;
  font-weight: 700;
}

.socials__h {
  font-family: 'Lato', sans-serif;
  font-size: 0.75rem;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
}

.qr {
  margin-top: var(--s8);
  padding: var(--s24);
  display: grid;
  justify-items: center;
  gap: 6px;
}

.qr img {
  border-radius: var(--r-sm);
  width: 150px;
  height: 150px;
}

.qr__t {
  margin-top: var(--s8);
  font-weight: 700;
  font-size: 0.9rem;
}

.qr__h {
  font-family: 'Lato', sans-serif;
  font-size: 0.8rem;
  color: var(--text-muted);
}

/* ── Closing invitation ──────────────────────────────────────────────────── */

/* Its own surface rather than the shared glass: a wider radius, a warmer
   gradient and a cyan glow around the whole panel — contact_cta.dart used
   teal at 0.55 over deepTeal at 0.85, and the glow at 0.45 strength. */
.cta {
  position: relative;
  overflow: hidden;
  padding: clamp(40px, 5vw, 64px) clamp(24px, 5vw, 64px);
  border-radius: var(--r-xl);
  background: linear-gradient(
    135deg,
    rgb(var(--rgb-teal) / 0.55),
    rgb(var(--rgb-deep-teal) / 0.85)
  );
  border: 1px solid rgb(var(--rgb-accent) / 0.28);
  box-shadow: 0 12px 40px -6px rgb(var(--rgb-accent) / 0.18);
  display: grid;
  justify-items: center;
  text-align: center;
}

.cta::before {
  content: '';
  position: absolute;
  inset: -40% 30% auto;
  height: 260px;
  background: radial-gradient(ellipse, rgb(var(--rgb-accent) / 0.18), transparent 70%);
  pointer-events: none;
}

.cta > * {
  position: relative;
}

.cta__lead {
  margin-top: var(--s16);
  max-width: 560px;
}

.cta__btns {
  margin-top: var(--s32);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s12) var(--s16);
  justify-content: center;
}

/* Not a button: a number to be read off the screen and written down. */
.cta__phone {
  margin-top: var(--s24);
  font-size: 1.0625rem;
  font-weight: 700;
  letter-spacing: 1.5px;
  color: var(--accent);
}

/* ── The title band on every inner page ──────────────────────────────────── */

.page-head {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}

.page-head__inner {
  padding-top: clamp(120px, 12vw, 160px);
  padding-bottom: clamp(16px, 3vw, 32px);
  display: grid;
  justify-items: center;
  text-align: center;
  gap: var(--s24);
}

.page-head__title {
  font-size: clamp(2.1rem, 5.6vw, 3.5rem);
  line-height: 1.12;
}

.page-head__aura {
  position: absolute;
  z-index: -1;
  inset-block-start: -55%;
  inset-inline: 15%;
  height: 420px;
  background: radial-gradient(ellipse at center, rgb(var(--rgb-accent) / 0.16), transparent 68%);
  pointer-events: none;
}

/* ── 404 ─────────────────────────────────────────────────────────────────── */

.notfound {
  padding-top: clamp(140px, 16vw, 200px);
  display: grid;
  justify-items: center;
  text-align: center;
  gap: var(--s16);
}

.notfound__code {
  font-family: 'Lato', sans-serif;
  font-size: clamp(5rem, 18vw, 10rem);
  font-weight: 900;
  line-height: 1;
  letter-spacing: -0.04em;
}

/* motion.css */
/*
 * Entrances, the drifting backdrop, and the keyframes everything else names.
 */

/* ── Reveal on scroll — core/widgets/reveal_on_scroll.dart ───────────────── */

/* The hidden state is only applied once main.js has added `js` to the root,
   so a block is never left invisible by a script that failed to load. */
html.js [data-reveal] {
  opacity: 0;
  transform: translateY(26px) scale(var(--reveal-scale, 1));
  transition:
    opacity var(--slow) var(--ease),
    transform var(--slow) var(--ease);
  transition-delay: var(--reveal-delay, 0ms);
}

html.js [data-reveal].is-visible {
  opacity: 1;
  transform: translateY(0) scale(1);
}

/* ── The wordmark signing itself ─────────────────────────────────────────── */

/* Added by the inline boot script, and only on the first page of a visit — so
   a browser with no JavaScript, or a repeat page, shows the finished mark.
   The sweep is always left to right: the artwork is a Latin signature, and a
   hand writing it moves that way whatever direction the page reads. */
html.sign-in .brand__sign--animate {
  animation: sign 1.5s var(--ease) 0.1s backwards;
}

@keyframes sign {
  from {
    clip-path: inset(0 100% 0 0);
  }
  to {
    clip-path: inset(0);
  }
}

/* ── The backdrop — core/widgets/aurora_background.dart ──────────────────── */

.aurora {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}

.aurora__grid {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to right, rgb(var(--rgb-accent-soft) / 0.045) 1px, transparent 1px),
    linear-gradient(to bottom, rgb(var(--rgb-accent-soft) / 0.045) 1px, transparent 1px);
  background-size: 58px 58px;
}

.aurora__blob {
  /* How far each blob wanders, as the painter had it: 12% of the width across
     and 10% of the height down. */
  --drift-x: 12vw;
  --drift-y: 10vh;

  position: absolute;
  /* The painter drew with BlendMode.plus on the dark theme: light adds. */
  mix-blend-mode: plus-lighter;
  will-change: transform;
}

/*
 * No sideways wander on a phone.
 *
 * 12vw is 47px on a 390px screen, and the backdrop is fixed while the page
 * scrolls past it — so a large soft glow slides under the content the whole
 * time you are reading, and the page reads as swaying. On a desktop the same
 * fraction is slow and wide enough to be atmosphere; on a phone it is just
 * movement. The blobs still breathe up and down, which is what the effect was
 * for.
 */
@media (max-width: 640px) {
  .aurora__blob {
    --drift-x: 0vw;
    --drift-y: 6vh;
  }
}

/* size = radius × shortestSide × 2; position = the painter's dx/dy. */
.aurora__blob--1 {
  width: 104vmin;
  height: 104vmin;
  left: 18%;
  top: 22%;
  background: radial-gradient(
    circle closest-side,
    rgb(var(--rgb-accent) / 0.357),
    rgb(var(--rgb-accent) / 0) 100%
  );
  animation: drift 34s ease-in-out infinite;
}

.aurora__blob--2 {
  width: 120vmin;
  height: 120vmin;
  left: 82%;
  top: 30%;
  background: radial-gradient(
    circle closest-side,
    rgb(var(--rgb-teal) / 0.357),
    rgb(var(--rgb-teal) / 0) 100%
  );
  animation: drift 34s ease-in-out infinite -12.6s;
}

.aurora__blob--3 {
  width: 68vmin;
  height: 68vmin;
  left: 55%;
  top: 88%;
  background: radial-gradient(
    circle closest-side,
    rgb(var(--rgb-gold) / 0.357),
    rgb(var(--rgb-gold) / 0) 100%
  );
  animation: drift 34s ease-in-out infinite -24.1s;
}

/* cos across, sin down — the same ellipse the painter walked, with the two
   radii read from the element so a narrow screen can flatten it. */
@keyframes drift {
  0% {
    transform: translate(-50%, -50%) translate(var(--drift-x), 0);
  }
  25% {
    transform: translate(-50%, -50%) translate(0, var(--drift-y));
  }
  50% {
    transform: translate(-50%, -50%) translate(calc(var(--drift-x) * -1), 0);
  }
  75% {
    transform: translate(-50%, -50%) translate(0, calc(var(--drift-y) * -1));
  }
  100% {
    transform: translate(-50%, -50%) translate(var(--drift-x), 0);
  }
}

/* ── Keyframes named elsewhere ───────────────────────────────────────────── */

@keyframes kenburns {
  from {
    scale: -1 1;
  }
  to {
    scale: -1.06 1.06;
  }
}

@keyframes sweep {
  0%,
  100% {
    transform: translateX(-60%);
    opacity: 0;
  }
  50% {
    transform: translateX(60%);
    opacity: 1;
  }
}

@keyframes pulse {
  from {
    transform: scale(1);
    opacity: 0.35;
  }
  to {
    transform: scale(2.6);
    opacity: 0;
  }
}

@keyframes blink {
  50% {
    opacity: 0;
  }
}

@keyframes spin {
  to {
    rotate: 360deg;
  }
}

@keyframes float {
  from {
    translate: 0 -8px;
  }
  to {
    translate: 0 8px;
  }
}

@keyframes wheel {
  0% {
    transform: translateY(0);
    opacity: 1;
  }
  100% {
    transform: translateY(15px);
    opacity: 0.3;
  }
}

/*
 * A slow drift of seven pixels on each axis — a true 45° — out over two
 * seconds and back over the next. Small enough to read as the button
 * breathing rather than sliding about, and it never travels far enough from
 * the corner to look misplaced.
 *
 * It animates `translate`, not `transform`, so the hover below still owns
 * `transform` and the two compose instead of fighting. Written both as
 * `transform`, the animation would beat the plain declaration and the button
 * would stop responding to the pointer altogether.
 */
@keyframes fab-drift {
  from {
    translate: 0 0;
  }
  to {
    translate: var(--fab-drift) -7px;
  }
}

/* 56px out to 96px — a scale of 1.714 — fading from half to nothing. */
@keyframes fab-pulse {
  from {
    transform: scale(1);
    opacity: 0.5;
  }
  to {
    transform: scale(1.714);
    opacity: 0;
  }
}

/* ── Reduced motion ──────────────────────────────────────────────────────── */

/* Everything above is decoration. Someone who has asked the system to reduce
   motion gets the finished state of all of it, immediately. */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  html.js [data-reveal] {
    opacity: 1;
    transform: none;
  }

  .aurora__blob {
    animation: none;
    transform: translate(-50%, -50%);
  }
}
