/* artiplans.com — site styles.
 * Built on tokens.css. Nothing here hardcodes a colour. */

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--fs-body-lg);
  line-height: 1.6;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { line-height: 1.2; margin: 0; font-weight: 600; letter-spacing: -.015em; }
p { margin: 0; }
a { color: var(--primary); text-underline-offset: 2px; }
img, svg { max-width: 100%; height: auto; display: block; }

/* The header is sticky, so an anchor jump would otherwise land with the
   target's first 62px hidden underneath it. */
[id] { scroll-margin-top: 78px; }

.wrap { max-width: var(--page); margin-inline: auto; padding-inline: var(--sp-5); }
.prose { max-width: var(--prose); }
.stack > * + * { margin-top: var(--sp-4); }
.muted { color: var(--text-secondary); }
.small { font-size: var(--fs-body); }
.center { text-align: center; }

/* --- header ------------------------------------------------------------ */

.site-head {
  position: sticky; top: 0; z-index: 20;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: saturate(160%) blur(12px);
  border-bottom: 1px solid var(--border);
}

.site-head .wrap { display: flex; align-items: center; gap: var(--sp-5); height: 62px; }

.brand {
  display: flex; align-items: center; gap: var(--sp-3);
  font-weight: 600; font-size: var(--fs-h3); color: var(--text); text-decoration: none;
  letter-spacing: -.02em;
}
/* The app icon itself, so the site and the Dock show the same thing. It
   already carries its own rounded corners and warm ground, so no radius or
   background here — just size, and a hairline to keep its light edge from
   dissolving into the sepia page in light theme. */
.brand-mark {
  width: 28px; height: 28px; flex: 0 0 auto;
  border-radius: 7px;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--border) 60%, transparent);
}

.site-nav { display: flex; gap: var(--sp-2); margin-left: auto; align-items: center; }

/* display: contents, so on a wide screen the wrapper is not a box at all and
   the links remain direct flex children of .site-nav — the desktop bar lays
   out exactly as it did before the wrapper existed. Below 640px the same
   element becomes the drop-down panel. */
.site-nav .nav-links { display: contents; }

/* The menu button exists only on narrow screens. Declared hidden here rather
   than shown there, so a browser that never reaches the media query shows the
   full navigation instead of a button that does nothing. */
.site-nav .nav-toggle { display: none; }
.site-nav .nav-toggle {
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm, 8px);
  background: transparent;
  color: var(--text-muted, var(--text));
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.site-nav .nav-toggle:hover { background: var(--surface); color: var(--text); }
.site-nav .nav-toggle:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.site-nav .nav-toggle-bars { display: inline-grid; gap: 3px; width: 16px; }
.site-nav .nav-toggle-bars span {
  display: block; height: 2px; width: 100%;
  background: currentColor; border-radius: 2px;
}
/* The middle bar goes, the outer two cross: the button says what it will do
   next without needing a second icon or a label change. */
.site-nav .nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(2) { opacity: 0; }
.site-nav .nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.site-nav .nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }
@media (prefers-reduced-motion: no-preference) {
  .site-nav .nav-toggle-bars span { transition: transform .18s ease, opacity .18s ease; }
}

/* A LONG UNBREAKABLE TOKEN PUSHES THE WHOLE PAGE SIDEWAYS. /support carries
   ~/Library/Containers/com.inventbetter.arti/Data/Documents/arti_db.sqlite —
   78 characters with no break opportunity in them — and it made the document
   523px wide inside a 390px viewport, so every page on the phone scrolled
   horizontally. Measured, not guessed.

   `anywhere` and not `break-word`: break-word only breaks when the word is
   alone on a line, which a path in the middle of a sentence never is. */
.prose p, .prose li, .doc p, .doc li, .card p, td, th { overflow-wrap: anywhere; }
/* :not(.btn) matters. A bare `.site-nav a` rule is (0,1,1) and outranks
   `.btn-primary` at (0,1,0), which painted the header's call-to-action in
   secondary text colour on the accent — an unreadable button in the one place
   every visitor looks. */
.site-nav a:not(.btn) {
  font-size: var(--fs-body); font-weight: 500; color: var(--text-secondary);
  text-decoration: none; padding: var(--sp-2) var(--sp-3); border-radius: var(--r-md);
}
.site-nav a:not(.btn):hover { color: var(--text); background: var(--surface); }

/* --- buttons ----------------------------------------------------------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  font: inherit; font-size: 15px; font-weight: 600; line-height: 1;
  padding: 14px 22px; border-radius: var(--r-lg);
  border: 1px solid transparent; cursor: pointer; text-decoration: none;
  transition: background .15s ease, border-color .15s ease, transform .08s ease;
}
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--primary); color: var(--on-primary); }
.btn-primary:hover { background: var(--primary-dark); }
.btn-ghost { background: var(--surface); color: var(--text); border-color: var(--border); }
.btn-ghost:hover { background: var(--surface-elevated); }
.btn-sm { padding: 9px 15px; font-size: var(--fs-body); border-radius: var(--r-md); }
.btn:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }

/* --- sections ---------------------------------------------------------- */

.section { padding-block: var(--sp-9); }
.section-tight { padding-block: var(--sp-8); }
.section + .section { padding-top: 0; }

.eyebrow {
  font-size: var(--fs-small); font-weight: 600; letter-spacing: .09em;
  text-transform: uppercase; color: var(--text-secondary); margin-bottom: var(--sp-3);
}

.section-title { font-size: clamp(26px, 3.4vw, 40px); letter-spacing: -.025em; }
.section-lede { font-size: var(--fs-body-lg); color: var(--text-secondary); margin-top: var(--sp-4); }

/* --- hero -------------------------------------------------------------- */

/* 48px above the pill, not the 112px a .section gets. A section's top padding
   separates it from the section before it; the hero's separates it from the
   header, which is a 60px bar and not a competing block of content. At 112 the
   first screen opened on an empty band as tall as the headline itself. */
.hero { padding-block: var(--sp-7) var(--sp-8); }
.hero h1 {
  font-size: clamp(32px, 4.2vw, 52px);
  letter-spacing: -.035em;
  line-height: 1.08;
  max-width: 18ch;
  text-wrap: balance;   /* stops a one-word last line where supported */
}
.hero-lede { font-size: clamp(17px, 1.7vw, 20px); color: var(--text-secondary); max-width: 54ch; margin-top: var(--sp-5); }
.hero-actions { display: flex; gap: var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-6); align-items: center; }
.hero-note { font-size: var(--fs-body); color: var(--text-secondary); margin-top: var(--sp-4); }

/* --- cards ------------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-5);
}

.grid { display: grid; gap: var(--sp-4); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }

.feature h3 { font-size: var(--fs-h3); margin-bottom: var(--sp-2); }
.feature p { font-size: var(--fs-body); color: var(--text-secondary); }
.feature-icon {
  width: 38px; height: 38px; border-radius: var(--r-md);
  background: var(--surface-elevated); color: var(--text);
  display: grid; place-items: center; margin-bottom: var(--sp-4);
}

/* --- the plan frame: the app's window, on the page --------------------- */

.plan-frame {
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.plan-frame-bar {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 10px var(--sp-4);
  border-bottom: 1px solid var(--border);
  background: var(--surface-elevated);
}
.traffic { display: flex; gap: 7px; }
.traffic i { width: 11px; height: 11px; border-radius: 50%; display: block; }
.plan-frame-title { font-size: var(--fs-small); color: var(--text-secondary); font-weight: 500; }
.plan-frame-body { display: block; background: var(--bg); }

/* Slot still used by features.html for a clip that has not been shot. */
.embed-slot {
  aspect-ratio: 16 / 10;
  display: grid; place-items: center;
  background: var(--surface);
  border: 1px dashed var(--border);
  border-radius: var(--r-lg);
  color: var(--text-secondary);
  text-align: center;
  padding: var(--sp-6);
}

/* --- the embedded app ---------------------------------------------------
 * Arti itself, compiled for the browser, behind a button. The stage holds
 * the 16:10 box whether it is showing the poster or the running app, so
 * pressing Launch swaps one for the other without the page jumping under
 * the reader's cursor.
 */
.demo-stage {
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--border);
  box-shadow: var(--shadow);
  background: var(--surface);
}
.demo-poster { display: block; width: 100%; aspect-ratio: 16 / 10; border: 0; }
.demo-poster { position: relative; }
.demo-poster img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* Over the picture rather than beside it: the screenshot is the argument for
   pressing the button, so hiding it behind a solid panel would throw the
   argument away. Tinted enough that the text keeps its contrast. */
.demo-cta {
  position: absolute; inset: 0;
  display: grid; align-content: center; justify-items: center;
  gap: var(--sp-2);
  text-align: center;
  padding: var(--sp-5);
  /* 85%, not the 78% this started at: the extra opacity is what takes the
     headline to 9.3:1 over the darkest pixel in the screenshot behind it.
     The caption needed more than opacity could give — see --demo-note. */
  background: color-mix(in srgb, var(--bg) 85%, transparent);
}
.demo-cta > * { margin: 0; }
.demo-headline {
  font-size: clamp(18px, 2.2vw, 26px);
  font-weight: 650;
  letter-spacing: -.01em;
  color: var(--text);
  text-wrap: balance;
}
.demo-note { color: var(--demo-note); }

/* On the home page the stage is a LINK to try.html, not a frame around a
   running app — so it has to behave like one: no underline on the caption,
   and the whole card lifts on hover the way the other cards do. */
.demo-link { display: block; color: inherit; text-decoration: none; }
.demo-link:hover .demo-cta { background: color-mix(in srgb, var(--bg) 72%, transparent); }
.demo-link:hover img { transform: scale(1.012); }
.demo-link img { transition: transform .25s ease; }
.demo-link:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
  .demo-link img { transition: none; }
  .demo-link:hover img { transform: none; }
}

/* --- pill / badge ------------------------------------------------------ */

.pill {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-body); font-weight: 500;
  padding: 6px 12px; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--border); color: var(--text-secondary);
}

.check-list { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--sp-3); }
.check-list li { display: flex; gap: var(--sp-3); align-items: flex-start; font-size: var(--fs-body-lg); }
.check-list svg { flex: 0 0 auto; margin-top: 5px; color: var(--success); }

/* --- comparison table -------------------------------------------------- */

.table-scroll { overflow-x: auto; border-radius: var(--r-lg); border: 1px solid var(--border); }
table { border-collapse: collapse; width: 100%; font-size: var(--fs-body); background: var(--surface); }
th, td { text-align: left; padding: 13px var(--sp-4); border-bottom: 1px solid var(--border); }
thead th { background: var(--surface-elevated); font-weight: 600; white-space: nowrap; }
tbody tr:last-child td { border-bottom: 0; }
td.yes { color: var(--success); font-weight: 600; }
td.no { color: var(--text-disabled); }

/* --- footer ------------------------------------------------------------ */

.site-foot { border-top: 1px solid var(--border); padding-block: var(--sp-7); margin-top: var(--sp-9); }
.foot-grid { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: var(--sp-5); }
.foot-grid h3 { font-size: var(--fs-small); text-transform: uppercase; letter-spacing: .09em; color: var(--text-secondary); margin: 0 0 var(--sp-3); font-weight: 600; }
.foot-grid ul { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--sp-2); }
.foot-grid a:not(.brand) { font-size: var(--fs-body); color: var(--text-secondary); text-decoration: none; }
.foot-grid a:not(.brand):hover { color: var(--text); }
.foot-legal { margin-top: var(--sp-6); padding-top: var(--sp-5); border-top: 1px solid var(--border); font-size: var(--fs-small); color: var(--text-secondary); display: flex; justify-content: space-between; gap: var(--sp-4); flex-wrap: wrap; }

/* --- responsive -------------------------------------------------------- */

@media (max-width: 900px) {
  .grid-3 { grid-template-columns: repeat(2, 1fr); }
  .foot-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 640px) {
  .section { padding-block: var(--sp-8); }
  .grid-3, .grid-2 { grid-template-columns: 1fr; }
  .hero h1 { max-width: none; }
}

/* THE NAVIGATION SWITCHES AT 700, NOT 640.
 *
 * Measured on the French pages: the inline header needs 689px for its own
 * links — Fonctionnalités, Confidentialité, Télécharger are simply longer
 * words — so between 641 and 689 the desktop bar was shown in a space it
 * does not fit and the whole document scrolled sideways by up to 48px. The
 * English header fits at 641 and hid the bug completely.
 *
 * Split out of the 640 block rather than moving that whole block: the
 * single-column grids and the tighter section padding are right at 640 and
 * wrong at 690. Only the navigation needed the wider trigger.
 */
@media (max-width: 699.98px) {

  /* THE LINKS BECOME A PANEL rather than disappearing. They used to be
     `display: none` with nothing in their place, so a phone could reach
     Features, Pricing, Guides and Support only by typing the URL.

     Anchored to .site-head, which is the positioned ancestor, so the panel
     spans the full width under the bar instead of hanging off the nav's right
     edge where it began. */
  .site-head { position: relative; }

  .site-nav .nav-toggle { display: inline-flex; }

  /* THE HEADER HAS TO FIT FIRST. Measured at 360px: brand 69 + gap 24 +
     nav 296 + 48 of wrap padding = 437 in a 360 viewport, so the bar itself
     scrolled sideways. The word "Menu" is 44 of those pixels and the icon
     already says it.
     Clipped rather than display:none — the span is the button's accessible
     name, and removing it from the tree leaves a screen reader announcing an
     unlabelled button. */
  .site-nav .nav-toggle-label {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0; border: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  .site-nav .nav-toggle { padding: 0 10px; }

  /* The remaining 16px come from the gutters. 16 is still a comfortable
     margin at this width and the content columns already use it. */
  .site-head .wrap { padding-inline: var(--sp-4); gap: var(--sp-3); }
  .site-nav { gap: var(--sp-2); }

  /* THE HEADER BUTTON MUST NOT WRAP. It is a flex item with the default
     `flex-shrink: 1`, so a tight bar squeezed it until "Download free" broke
     over two lines and the button grew to 48px — which is what the bar looked
     like on a real iPhone. It is the one element here that should take its
     space and let something else give. */
  .site-head .btn-sm { flex: 0 0 auto; white-space: nowrap; }

  /* The codes used to be squeezed to 26px here to buy back overflow. There is
     nothing left to squeeze: the bar now carries one 62px control whatever the
     language count, and the codes live inside the menu where width is free. */

  /* .lang-pick carries margin-left: var(--sp-3) from its base rule, to separate
     it from the call to action on a wide bar. On a narrow one the nav's own gap
     already does that, and the extra 12px was the last of the overflow —
     English only, because "Download free" is 18px wider than "Télécharger". */
  .site-nav .lang-pick { margin-left: 0; }

  /* The brand is a link people tap to get home, and it measured 69x29. */
  .site-head .brand { min-height: 44px; display: inline-flex; align-items: center; }

  .site-nav .nav-links {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    gap: 0;
    padding: var(--sp-2) 0 var(--sp-3);
    background: var(--bg);
    border-bottom: 1px solid var(--border);
    box-shadow: 0 12px 24px rgba(0, 0, 0, .08);
    z-index: 40;
  }
  .site-nav .nav-links.is-open { display: flex; }

  /* 48px of height, not 30. A row you tap with a thumb, with the whole width
     as the target rather than the width of the word. */
  .site-nav .nav-links a:not(.btn) {
    display: block;
    min-height: 48px;
    padding: 13px var(--sp-5);
    border-radius: 0;
    font-size: var(--fs-body);
  }
  .site-nav .nav-links a[aria-current="page"] {
    color: var(--text);
    background: var(--surface);
    font-weight: 600;
  }

  /* The language picker survives in the bar, where the rest of the navigation
     moves into the panel. Hiding it would leave a French visitor who landed on
     an English page with no way back to their own language on a phone — and a
     phone is where most of them arrive. */
  /* The control itself, not its contents: the menu rows lay themselves out
     with flex, and the old `display: inline-block` on every a/span in here
     overrode that and stacked the name on top of its code. */
  .site-nav .lang-pick { display: inline-block; }
}

/* The smallest phones still in use — an iPhone SE at 320px — cannot fit the
   wordmark, the menu, the download button and two language codes on one bar:
   measured at 335px of content in a 320px window, so every page scrolled
   sideways by 15px. The icon carries the brand on its own at this width; the
   word is clipped rather than removed so the link keeps its accessible name. */
/* 340 and not 380. At 380 this caught the iPhone SE, the 12 mini and the 13
   mini — all 375pt — and dropped the wordmark on three of the commonest phones
   in use, where the header has room for it. Measured: the bar fits with the
   word down to 360px and only stops fitting at 320. */
@media (max-width: 340px) {
  .site-head .brand { font-size: 0; gap: 0; }
  .site-head .brand .brand-mark { font-size: 1rem; }

  /* STAYS TWO COLUMNS. This used to drop to one, because at 320px two columns
     give 79px each and "In-app purchases" on /pricing would not fit — a grid
     column never shrinks below its longest word. But that punished the four
     short labels on the homepage with four full rows of one item each, on the
     screen with the least room to spare.
     minmax(0, …) plus a break opportunity lets the one long label wrap instead:
     it costs that label a line and saves every other list two. */
  .trust { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
  .trust dt { overflow-wrap: anywhere; }
}

/* --- tap targets ----------------------------------------------------------
 *
 * KEYED TO THE POINTER, NOT THE WIDTH. These were written inside the 640px
 * block first, which left an iPad in portrait — 768px wide, every target a
 * finger — with 38px links and 13px language codes. Width tells you about
 * layout; `pointer: coarse` is the only thing that tells you about thumbs.
 *
 * Measured at 390px before this: language codes 17x13 CSS px, the header
 * button 130x34, navigation links 82x38. The WCAG 2.5.8 minimum is 24px and
 * the 2.5.5 enhanced target is 44px; these aim at 44.
 *
 * Padding and min-height only. Nothing here changes a font size, a colour or
 * a position — the design is unchanged, the things your thumb has to hit are
 * simply big enough to hit.
 */
@media (pointer: coarse) {
  .site-head .brand { min-height: 44px; display: inline-flex; align-items: center; }
  .site-head .btn-sm { min-height: 44px; }

  .site-nav .nav-links a:not(.btn) {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }

  .site-nav .lang-pick .lang-menu a,
  .site-nav .lang-pick .lang-menu .on {
    min-width: 32px;
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
  }
}

/* --- media: video loops and posters ------------------------------------
 * The hero loop and the short per-feature clips. Built so the markup can sit
 * in the page before the files exist: the placeholder is a sibling that the
 * video covers once it loads.
 */

.media-frame { position: relative; background: var(--bg); }
.media-frame video,
.media-frame img.media { display: block; width: 100%; height: auto; }

/* THE FRAME IS THE DESIGN — the same rule .frow-media carries on the home
   page, for the same reason. `height: auto` above hands each row's height
   back to the picture's own ratio, so seven screenshots would set seven
   different heights down the page. The ratio class on the image holds the
   frame and object-fit fills it. */
.media-frame img.media {
  object-fit: cover;
  border-radius: var(--r-lg);
  border: 1px solid var(--border);
  box-shadow: var(--shadow);
}

/* Holds the box at the right shape before anything has loaded, so the page
   does not jump when the clip arrives. */
.media-16-10 { aspect-ratio: 16 / 10; }

/* The frame's own controls, bottom right. A flex row rather than two
   absolutely positioned buttons: "Pause" becomes "Play" when clicked, and a
   right offset computed for one word is wrong for the other. */
.media-actions {
  position: absolute; right: var(--sp-3); bottom: var(--sp-3);
  display: flex; gap: var(--sp-2);
}
.media-btn {
  display: inline-flex; align-items: center; gap: 6px;
  font: inherit; font-size: var(--fs-small); font-weight: 500;
  color: var(--text); background: color-mix(in srgb, var(--surface) 90%, transparent);
  border: 1px solid var(--border); border-radius: 999px;
  padding: 6px 12px; cursor: pointer;
  backdrop-filter: blur(8px);
}
.media-btn:hover { background: var(--surface-elevated); }
.media-btn:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }

/* FULLSCREEN. The frame goes fullscreen, not the bare <video>, so the pause
   and exit buttons come with it — a fullscreen <video> on a desktop has no
   controls at all here, because the element is deliberately control-less.
   Black rather than the page's cream: a 16:10 clip in a 16:9 screen is
   letterboxed either way, and cream bars read as a broken layout where black
   bars read as a video.

   ABSOLUTE, NOT A CENTRED GRID ITEM. `display: grid; place-items: center` with
   `height: 100%` on the video looked right and was not: the single row is
   auto-sized to its content, so the percentage height had nothing definite to
   resolve against, fell back to the 16:10 aspect-ratio, and produced a
   1920x1190 video inside a 1080px screen. Measured, not theorised. The
   fullscreen element is `position: fixed; inset: 0` by UA rule, so pinning the
   video to inset: 0 gives it the viewport exactly, and object-fit letterboxes
   inside that.

   The -webkit- selector is a SEPARATE RULE on purpose. A selector list is
   dropped whole by any browser that does not understand one of its parts, so
   grouping :fullscreen with :-webkit-full-screen loses the rule in Firefox,
   which knows only the first. */
.media-frame:fullscreen { background: #000; }
.media-frame:-webkit-full-screen { background: #000; }

.media-frame:fullscreen video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  aspect-ratio: auto;
}
.media-frame:-webkit-full-screen video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  aspect-ratio: auto;
}

/* A short clip beside a feature. Smaller, quieter, no chrome. */
.step { display: grid; gap: var(--sp-4); }
.step-media {
  border: 1px solid var(--border); border-radius: var(--r-lg);
  overflow: hidden; background: var(--surface); box-shadow: var(--shadow-sm);
}
.step-n {
  display: inline-grid; place-items: center;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--primary); color: var(--on-primary);
  font-size: var(--fs-small); font-weight: 600;
}
.step h3 { font-size: var(--fs-h3); margin-top: var(--sp-3); }
.step p { font-size: var(--fs-body); color: var(--text-secondary); margin-top: var(--sp-2); }

/* Anyone who has asked the OS to reduce motion gets the poster frame and no
   loop at all. The JS pauses playback; this hides the control that would then
   be lying about what it does. The fullscreen button stays — a paused frame is
   still worth seeing full size, and that button tells no lies. */
@media (prefers-reduced-motion: reduce) {
  .media-btn[data-pause] { display: none; }
}

/* .prose caps width for readability; .wrap caps it for the page and centres
   it. Putting both on one element makes .prose win and centres the whole
   section — which is how the page heads ended up narrower and indented
   relative to everything under them. Nest them instead: .wrap > .prose. */
.wrap.prose { max-width: var(--page); }

/* --- long-form documents: legal pages and articles ---------------------
 * One column, capped for reading. Type is a step down from the marketing
 * pages: these are read, not scanned.
 */

.doc { max-width: 760px; margin-inline: auto; padding-block: var(--sp-7) var(--sp-8); }
.doc-head { margin-bottom: var(--sp-6); }
.doc-head h1 { font-size: clamp(28px, 3.6vw, 40px); letter-spacing: -.03em; }
.doc-date { font-size: var(--fs-body); color: var(--text-secondary); margin-top: var(--sp-3); }

.doc h2 {
  font-size: var(--fs-h2); margin-top: var(--sp-7); margin-bottom: var(--sp-3);
  padding-top: var(--sp-4); border-top: 1px solid var(--border);
}
.doc h3 { font-size: var(--fs-h3); margin-top: var(--sp-5); margin-bottom: var(--sp-2); }
.doc h4 { font-size: var(--fs-body-lg); margin-top: var(--sp-4); margin-bottom: var(--sp-2); }
.doc p { margin-bottom: var(--sp-4); color: var(--text-secondary); }
.doc h2 + p, .doc h3 + p { margin-top: 0; }
.doc strong { color: var(--text); font-weight: 600; }
.doc ul, .doc ol { margin: 0 0 var(--sp-4); padding-left: var(--sp-5); color: var(--text-secondary); }
.doc li { margin-bottom: var(--sp-2); }
.doc li::marker { color: var(--text-disabled); }
.doc hr { border: 0; border-top: 1px solid var(--border); margin-block: var(--sp-6); }

.doc blockquote {
  margin: 0 0 var(--sp-4); padding: var(--sp-4) var(--sp-5);
  background: var(--surface); border: 1px solid var(--border);
  border-left: 3px solid var(--primary); border-radius: var(--r-md);
  color: var(--text);
}
.doc blockquote p:last-child { margin-bottom: 0; }

.doc code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .9em; background: var(--surface-elevated);
  padding: 2px 6px; border-radius: var(--r-xs);
  color: var(--text); word-break: break-word;
}
.doc pre {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: var(--sp-4);
  overflow-x: auto; margin-bottom: var(--sp-4);
}
.doc pre code { background: none; padding: 0; font-size: var(--fs-small); }
.doc .table-scroll { margin-bottom: var(--sp-4); }

/* On-page contents, built from the h2s. */
.doc-toc { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg); padding: var(--sp-4) var(--sp-5); margin-bottom: var(--sp-6); }
.doc-toc h2 { font-size: var(--fs-small); text-transform: uppercase; letter-spacing: .09em; color: var(--text-secondary); margin: 0 0 var(--sp-3); border: 0; padding: 0; }
.doc-toc ol { list-style: none; padding: 0; margin: 0; columns: 2; column-gap: var(--sp-5); }
.doc-toc li { margin-bottom: 6px; break-inside: avoid; }
.doc-toc a { font-size: var(--fs-body); text-decoration: none; color: var(--text-secondary); }
.doc-toc a:hover { color: var(--primary); }

/* Article index cards. */
.guide-card { display: block; text-decoration: none; color: inherit; }
.guide-card:hover { border-color: var(--primary); }
.guide-card h3 { font-size: var(--fs-h3); margin-bottom: var(--sp-2); }
.guide-card p { font-size: var(--fs-body); color: var(--text-secondary); margin-bottom: var(--sp-3); }
.guide-meta { font-size: var(--fs-small); color: var(--text-disabled); }

@media (max-width: 640px) { .doc-toc ol { columns: 1; } }

/* Markdown '---' rules sit right before an h2, whose own border-top then drew
   a second line a few pixels below the first. The rule wins; the heading drops
   its border. */
.doc hr + h2 { border-top: 0; padding-top: 0; margin-top: var(--sp-6); }

/* ═══════════════════════════════════════════════════════════════════════
   SPLIT HERO — copy left, the app's own artwork right
   ═══════════════════════════════════════════════════════════════════════ */

.hero-split {
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  /* COLUMN GAP ONLY. The copy used to be one div, so the space between the
     headline and the lede came from .hero-lede's own 24px top margin. Splitting
     it into .hero-head and .hero-body put a grid row boundary there, and a
     48px row gap would have added itself to that margin. Rows are spaced by
     the elements' own margins; only the columns are spaced by the grid. */
  column-gap: var(--sp-7);
  row-gap: 0;
  align-items: start;
}

/* Two rows of copy in the left column, the icon centred against both, and the
   loop on its own full-width row underneath — which is exactly where it sat
   as a separate section, so the desktop hero is unchanged by the move. */
.hero-head { grid-column: 1; grid-row: 1; }
.hero-body { grid-column: 1; grid-row: 2; }
.hero-art  { grid-column: 2; grid-row: 1 / span 2; align-self: center; }
.hero-tour { grid-column: 1 / -1; grid-row: 3; margin-top: var(--sp-8); }

.hero-art { position: relative; justify-self: center; width: 100%; max-width: 390px; }

/* The glow below is inset -10% horizontally on purpose, and an absolutely
   positioned pseudo-element still counts toward its ancestor's scroll width —
   so on a 320px phone the decoration, which cannot even be clicked, scrolled
   the page 15px sideways.
   `clip` and not `hidden`: hidden would make this a scroll container and break
   any position:sticky inside it. clip only clips. A browser too old to know
   the keyword ignores the line and keeps today's behaviour. */
.hero-split { overflow-x: clip; }

/* The glow is the gold doing the work it is good at: setting the mood without
   having to pass a contrast check. */
.hero-art::before {
  content: "";
  position: absolute; inset: -14% -10% -18%;
  background: radial-gradient(closest-side, var(--gold-glow), transparent 72%);
  filter: blur(28px);
  z-index: 0;
  pointer-events: none;
}

.hero-art img {
  position: relative; z-index: 1;
  width: 100%; height: auto;
  border-radius: 28px;
  box-shadow: var(--shadow-lg);
}

/* A very slow drift. Long enough that it reads as depth rather than motion —
   anything faster on a hero becomes something you notice, then resent. */
@media (prefers-reduced-motion: no-preference) {
  .hero-art img { animation: drift 9s ease-in-out infinite; }
}
@keyframes drift {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-10px); }
}

/* Gold underline behind the last words of the headline. */
.mark-gold {
  background: linear-gradient(transparent 62%, var(--gold-glow) 62%);
  padding-inline: 2px;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

/* --- eyebrow, tinted gold ---------------------------------------------- */
/* THE SELECTOR BUG THESE THREE SHARE, and why they are wrapped.
 *
 * `:root:not([data-theme="light"])` matches whenever no theme is stamped — the
 * default state for every visitor who has not pressed a toggle — so WITHOUT a
 * prefers-color-scheme guard it also matches in light mode and overrides the
 * light value. Measured on the rendered page: .eyebrow came out #E7AD54 at
 * 1.54:1 on paper, where --gold-deep was meant to give 2.26:1. --gold-deep was
 * dead code; the light branch never ran anywhere on this site.
 *
 * tokens.css has this right — every dark override in it sits inside the media
 * query. site.css did not, in three places. The pattern is: bare rule = light,
 * media query = system dark, [data-theme="dark"] = explicit dark, and the last
 * two both needed because the toggle has to win in either direction.
 *
 * STILL NOT A PASS. --gold-deep on paper is 2.26:1, under the 4.5 small text
 * wants and under the 3 that non-text wants. That is a decision about the
 * brand's look on every page, not a bug to fix in passing, so it is flagged
 * rather than changed. The language picker below does not wait for it: it sets
 * its current item in ink and lets the gold underline do the marking.
 */
.eyebrow { color: var(--gold-deep); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .eyebrow { color: var(--gold); }
}
:root[data-theme="dark"] .eyebrow { color: var(--gold); }

.pill-gold {
  background: var(--gold-wash);
  border-color: color-mix(in srgb, var(--gold) 40%, transparent);
  color: var(--text);
}

/* --- trust row ---------------------------------------------------------- */
.trust {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sp-4);
  padding-top: var(--sp-5); margin-top: var(--sp-6);
  border-top: 1px solid var(--border);
}
/* minmax(0, 1fr), not 1fr. `1fr` is `minmax(auto, 1fr)`, so a column refuses
   to shrink below its longest word — "LANGUAGES" under a 0.08em letter-spacing
   — and the list pushed the page 29px sideways at 320px. The four-column rule
   above already had the minmax; the two-column override did not. */
@media (max-width: 560px) { .trust { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.trust dt { font-size: var(--fs-small); color: var(--text-secondary); text-transform: uppercase; letter-spacing: .08em; font-weight: 600; }
.trust dd { margin: 4px 0 0; font-size: var(--fs-body-lg); font-weight: 600; color: var(--text); }

/* --- screenshot gallery -------------------------------------------------- */
.shot {
  /* THE BROWSER'S DEFAULT FIGURE MARGIN IS 1em 40px, and nothing reset it —
     so every screenshot card sat 80px narrower than the column it was in, at
     every width. On a 390px phone that is a picture using 67% of the screen
     with no visible reason; on a desktop it is 992px in a 1072px column.
     Measured, not guessed: the number 262 in a 342px column is exactly
     342 minus the two 40px margins. */
  margin: 0;
  border: 1px solid var(--border); border-radius: var(--r-lg);
  overflow: hidden; background: var(--surface); box-shadow: var(--shadow);
}
.shot img { display: block; width: 100%; height: auto; }
.shot figcaption {
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-body); color: var(--text-secondary);
  border-top: 1px solid var(--border); background: var(--surface-elevated);
}
.shot-lead { grid-column: 1 / -1; }

/* --- motion: scroll reveal ---------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
  .reveal { opacity: 0; transform: translateY(18px); transition: opacity .55s ease, transform .55s ease; }
  .reveal.is-in { opacity: 1; transform: none; }
  .reveal:nth-child(2) { transition-delay: .07s; }
  .reveal:nth-child(3) { transition-delay: .14s; }
  .reveal:nth-child(4) { transition-delay: .21s; }
  .reveal:nth-child(5) { transition-delay: .28s; }
  .reveal:nth-child(6) { transition-delay: .35s; }
}

/* --- card lift ----------------------------------------------------------- */
.card, .shot {
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.grid .card:hover, .shot:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-lg);
  border-color: color-mix(in srgb, var(--gold) 45%, var(--border));
}

/* A hairline of gold along the top of the section that follows the hero,
   so the brand colour recurs rather than appearing once. */
.rule-gold { border-top: 2px solid var(--gold); width: 56px; margin-bottom: var(--sp-4); }

@media (max-width: 900px) {
  /* One column, so the explicit row/column placement above has to be released
     or the items keep their assigned rows and the loop lands in row 3 with two
     empty rows above it. `order: -1` on the art then does what it always did:
     the icon first, and the DOM order — headline, loop, pitch — follows. */
  .hero-split { grid-template-columns: 1fr; gap: var(--sp-6); }
  .hero-head, .hero-body, .hero-art, .hero-tour { grid-column: auto; grid-row: auto; }
  .hero-tour { margin-top: 0; }
  .hero-art { max-width: 300px; order: -1; align-self: auto; }
}

/* THE FIRST SCREEN OF A PHONE IS THE WHOLE PITCH, and this one was spending it
   on a picture. Measured at 390x844 before this change:
   art 300x300 starting at y=175, the headline at y=590, and "Download free for
   Mac" at y=850 — six pixels below the fold, on every phone size tested.
   So the first thing a visitor saw was a logo render, and the thing we want
   them to do was off screen.
   The art keeps its place above the headline: at 180px it reads as a mark
   rather than as the content, which is what it is. */
/* THE LOOP GOES FIRST ON A PHONE — well, third.
 *
 * Measured at 390px before any of this: the hero ran 1143px, so the video —
 * the one asset that shows the product actually working — began at y=1206, a
 * screen and a half down. Everything above it was words and a logo render.
 *
 * Two earlier attempts at this are worth recording, because both were the
 * wrong shape. Making <main> a flex container and giving the loop `order: -1`
 * put the video above everything, including the headline, and turned every
 * OTHER page's sections into flex items — /privacy went to 602px inside a
 * 390px phone because a stretched flex item will not shrink below its own
 * min-content and the page quotes a long file path. Then the icon, hidden to
 * make room, had to come back as a duplicate <img> because `order` could not
 * reach it two containers deep.
 *
 * Moving the loop into .hero-split ends both problems. The DOM order is now
 * the phone order — headline, loop, pitch — the art's existing `order: -1`
 * puts the icon on top, and nothing outside the homepage is touched at all.
 */
@media (max-width: 640px) {
  .hero-split { gap: var(--sp-5); }
  .hero-art { max-width: 180px; }

  /* The hero's bottom padding had a 390px image above it on a desktop; with
     the art shrunk to a mark at the top, it sits directly under the trust row
     and stacks with the next section's 72px top. Zero here still leaves 88px
     to "The app itself" — tighter than every other section boundary on this
     page, where two sections' padding stacks to 144. */
  .hero { padding-block: var(--sp-6) 0; }

  /* "See how it works" points at the loop, which is now three elements above
     it rather than a scroll away. */
  .hero-actions .btn-ghost[href="#tour"] { display: none; }
}

/* Narrower still, and the headline is what has to survive. */
@media (max-width: 380px) {
  .hero-art { max-width: 150px; }
}

/* Section heading with an action beside it. The CTA repeats where interest
   peaks — a visitor convinced by the feature list should not have to scroll
   back to the hero or on to the footer to act on it. */
.feature-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--sp-5); margin-bottom: var(--sp-6); flex-wrap: wrap;
}
/* max-width, because `flex: 0 0 auto` means "never shrink" and the French
   label is longer than the English one: "Télécharger gratuitement pour Mac"
   measured 299px inside a 272px column at 320px, so the French homepage
   scrolled sideways where the English one did not. The kind of thing that only
   ever shows up in the second language. */
.feature-head .btn { flex: 0 0 auto; max-width: 100%; }
@media (max-width: 700px) { .feature-head { align-items: flex-start; } }

/* ═══════════════════════════════════════════════════════════════════════
   FEATURE ROWS — one major capability per row, alternating sides
   ═══════════════════════════════════════════════════════════════════════ */

.frow {
  display: grid;
  grid-template-columns: 1fr 1.15fr;
  gap: var(--sp-7);
  align-items: center;
  padding-block: var(--sp-7);
}
.frow + .frow { border-top: 1px solid var(--border); }
.frow:nth-child(even) .frow-media { order: -1; }

.frow h3 { font-size: clamp(22px, 2.4vw, 30px); letter-spacing: -.02em; }
.frow .lede { color: var(--text-secondary); margin-top: var(--sp-3); font-size: var(--fs-body-lg); }
.frow ul { list-style: none; padding: 0; margin: var(--sp-4) 0 0; display: grid; gap: var(--sp-2); }
.frow li { display: flex; gap: var(--sp-3); font-size: var(--fs-body); color: var(--text-secondary); }
.frow li b { color: var(--text); font-weight: 600; }
.frow li::before { content: "→"; color: var(--gold-deep); flex: 0 0 auto; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .frow li::before { color: var(--gold); }
}
:root[data-theme="dark"] .frow li::before { color: var(--gold); }

.frow-media { border-radius: var(--r-lg); overflow: hidden; }
/* THE FRAME IS THE DESIGN, and the picture goes inside it.
   Every media slot was a .slot at 16/10, so the rows kept one rhythm no
   matter what was in them. Dropping bare <img>s in place of those slots
   handed the height back to each picture's own ratio — rows came out 296,
   341, 356 and 410 instead of matching, and the export sheet (1836x1522,
   ratio 1.21) blew its row out entirely. The ratio belongs to the FRAME. */
.frow-media img {
  display: block; width: 100%;
  aspect-ratio: 16 / 10; object-fit: cover;
  border-radius: var(--r-lg); border: 1px solid var(--border); box-shadow: var(--shadow);
}
/* The export sheet is a drawing, not a screenshot: cropping it to 16/10
   would cut off the title block and the scale bar, which are the whole
   reason it is on the page. It sits INSIDE the frame instead of filling it. */
.frow-media .sheet img { object-fit: contain; background: var(--surface); }

/* One feature gets the spotlight. Gold wash rather than a different layout —
   the emphasis should read as "this one matters", not as a different section. */
.frow-hero {
  background: var(--gold-wash);
  border: 1px solid color-mix(in srgb, var(--gold) 35%, transparent);
  border-radius: var(--r-lg);
  padding: var(--sp-6);
  margin-block: var(--sp-5);
}
.frow-hero + .frow { border-top: 0; }

.badge-gold {
  display: inline-block; font-size: var(--fs-small); font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--gold-deep); margin-bottom: var(--sp-2);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .badge-gold { color: var(--gold); }
}
:root[data-theme="dark"] .badge-gold { color: var(--gold); }

/* --- reserved asset slot -------------------------------------------------
   Names the exact file it is waiting for, so the capture list is the page
   itself rather than a document that drifts from it. */
.slot {
  aspect-ratio: 16 / 10;
  display: grid; place-items: center; text-align: center;
  background: var(--surface);
  border: 1px dashed color-mix(in srgb, var(--gold) 55%, var(--border));
  border-radius: var(--r-lg);
  padding: var(--sp-5);
}
.slot code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--fs-small); color: var(--text);
  background: var(--surface-elevated); padding: 3px 7px; border-radius: var(--r-xs);
}
.slot p { font-size: var(--fs-body); color: var(--text-secondary); margin-top: var(--sp-3); max-width: 34ch; }
.slot .slot-title { font-weight: 600; color: var(--text); font-size: var(--fs-body-lg); }

@media (max-width: 900px) {
  .frow { grid-template-columns: 1fr; gap: var(--sp-5); padding-block: var(--sp-6); }
  .frow:nth-child(even) .frow-media { order: 0; }
}


/* --- narrow screens: the Mac app, not the browser build -------------------
 *
 * Arti's browser build needs a viewport about 900 px across — the same minimum
 * lib/main.dart asks for as a desktop window. Below that the editor renders
 * into a space it cannot be used in, so the site stops offering it rather than
 * sending someone to a page whose only job would be to refuse them. The header
 * link, the home page's "try it" button and the demo card all fall back to the
 * Mac download instead.
 *
 * 900 px and not the 640 px used elsewhere in this file: this breakpoint is not
 * about layout, it is the app's own minimum. A tablet at 800 px has a perfectly
 * good layout and still cannot run the editor.
 *
 * Any .wide-only CALL TO ACTION needs a .narrow-only twin, or the narrow screen
 * gets a hole where a button belongs. A .wide-only note does not: the line
 * "works best on a screen at least 900 px wide" explains the browser version,
 * which a narrow screen is never offered in the first place.
 */
@media (max-width: 899px) {
  /* .nav-draw, not a[href="/draw"]: the French slug is /fr/dessiner, so the old
     attribute selector missed it and the French header offered the browser
     editor on screens that cannot run it. */
  .site-nav .nav-draw { display: none !important; }
  .wide-only { display: none !important; }
}
@media (min-width: 900px) {
  .narrow-only { display: none !important; }
}

/* --- full-size screenshots --------------------------------------------
 *
 * Every screenshot on this site is a 2844x1764 export shown at a fraction of
 * that. The detail — a room's area, the label on a piece of furniture, the
 * numbers along a dimension line — is in the file and was not reachable.
 *
 * The images are not wrapped in links: the markup is shared with the prototype
 * and an <a> around a <picture> would change how several of them lay out. They
 * are given a button ROLE instead, by the script, which also means the page
 * still renders exactly as before when the script has not run — and a
 * screenshot you cannot enlarge is the behaviour the site has had all along.
 */
.zoomable {
  cursor: zoom-in;
}
.zoomable:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
}

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(12px, 3vw, 40px);
  /* Nearly opaque rather than a tint: these are pale screenshots and a light
     scrim left them floating on a page that was still legible behind. */
  background: rgba(20, 17, 13, .92);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.lightbox[hidden] { display: none; }

.lightbox-img {
  max-width: 100%;
  /* Leaves room for the caption under it without either one scrolling. */
  max-height: calc(100% - 4.5rem);
  border-radius: var(--r-lg);
  box-shadow: 0 24px 60px rgba(0, 0, 0, .5);
  cursor: zoom-in;
}

/* FIT IS NOT FULL SIZE. Fitted to a 390px phone, a 2844px screenshot is shown
   at 13% — barely larger than it was in the page, which is not what "view full
   size" promises. A click swaps to the file's own pixels and the overlay
   scrolls, so the detail that only exists at 2844px is actually reachable.
   Two states, fit and 1:1, because a zoom slider on a picture of a floor plan
   is a control nobody asked for. */
.lightbox.is-zoomed {
  overflow: auto;
  align-items: flex-start;
  justify-content: flex-start;
}
.lightbox.is-zoomed .lightbox-img {
  max-width: none;
  max-height: none;
  border-radius: 0;
  cursor: zoom-out;
}
/* The caption would sit over the middle of a scrolled image, so it goes. */
.lightbox.is-zoomed .lightbox-cap { display: none; }
.lightbox.is-zoomed .lightbox-close { position: fixed; }

.lightbox-cap {
  position: absolute;
  left: 50%;
  bottom: clamp(10px, 2vw, 22px);
  transform: translateX(-50%);
  max-width: min(72ch, 92vw);
  margin: 0;
  text-align: center;
  font-size: var(--fs-small);
  line-height: 1.45;
  /* The alt text, which on this site describes what the screenshot shows —
     so it is worth reading rather than hiding. */
  color: rgba(255, 255, 255, .82);
}

.lightbox-close {
  position: absolute;
  top: clamp(8px, 2vw, 18px);
  right: clamp(8px, 2vw, 18px);
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(255, 255, 255, .28);
  border-radius: 50%;
  background: rgba(255, 255, 255, .1);
  color: #fff;
  font: inherit;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}
.lightbox-close:hover { background: rgba(255, 255, 255, .2); }
.lightbox-close:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

@media (prefers-reduced-motion: no-preference) {
  .lightbox { animation: lb-in .18s ease both; }
  .lightbox-img { animation: lb-pop .2s cubic-bezier(.22,.61,.36,1) both; }
}
@keyframes lb-in  { from { opacity: 0 } to { opacity: 1 } }
@keyframes lb-pop { from { opacity: 0; transform: scale(.97) } to { opacity: 1; transform: none } }

/* --- the phone's call to action ------------------------------------------
 *
 * Replaces "Download free for Mac" below 900px, where that button asks for
 * something the device cannot do. Styled as a card rather than a bare button
 * because it has to say WHY before it asks for anything — a lone "Send this
 * page to your Mac" on a page about a Mac app reads as a non-sequitur.
 */
.mac-cta {
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
  padding: var(--sp-5);
  text-align: left;
}
.mac-cta-title {
  margin: 0;
  font-weight: 700;
  font-size: var(--fs-h3);
  color: var(--text);
}
.mac-cta-line {
  margin: var(--sp-2) 0 var(--sp-4);
  color: var(--text-secondary);
  font-size: var(--fs-body);
}
/* Full width and 48px tall: it is the only action on the screen. */
.mac-cta .mac-cta-send {
  width: 100%;
  justify-content: center;
  min-height: 48px;
}
/* On /draw the page has already said why, so the compact form is the action
   alone. AFTER .mac-cta, not before it: same specificity, and the later rule is
   the one that wins — declared above, the card kept its border and the button
   sat inside a box with nothing else in it. */
.mac-cta.mac-cta-compact {
  border: 0;
  background: none;
  padding: 0;
}

/* The URL in plain text under the button, so the block still works when the
   script has not run and when the share sheet is declined. */
.mac-cta-url {
  margin: var(--sp-3) 0 0;
  text-align: center;
  font-size: var(--fs-small);
  color: var(--text-secondary);
  letter-spacing: .01em;
}

/* --- language picker ------------------------------------------------------
 *
 * EN · FR after the call to action: uppercase codes, the current one in the
 * accent and bold, a middle dot between them. The same shape as Invent
 * Better's .navStrip-lang, so the two sites read as coming from the same hand
 * — with Arti's palette, because the colours are not shared.
 *
 * A list rather than a toggle: more languages are coming, and "the other one"
 * stops existing at three.
 *
 * EVERY DECLARATION IS TWO CLASSES DEEP, deliberately. `.site-nav a:not(.btn)`
 * is (0,2,1) and a bare `.lang-pick a` is (0,1,1), so the navigation rule wins
 * and the codes come out at body size in body colour. Measured: they rendered
 * at 14px against the 12px set here. Same collision this stylesheet already
 * had once, over the header's call to action — and guarding the colour while
 * forgetting the size is how it comes back.
 */
/* A DISCLOSURE. Was an inline row of codes; six languages measured 195px in a
   bar with 88px spare at 390px. This is 62px at any number of languages, and
   does not grow. Markup and measurements in partials/head_nav.php. */
.lang-pick {
  position: relative;
  flex: none;
  margin-left: var(--sp-3);
}
.site-nav .lang-pick > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-height: 44px;
  padding-inline: 9px;
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  font-size: .8rem;
  line-height: 1;
  letter-spacing: .04em;
  font-weight: 700;
  /* INK, NOT GOLD. --gold-deep on this paper measures 1.54:1 against a 4.5
     requirement — the same measurement that sent the old current-language
     marker to ink with a gold underline. A gold globe label would have walked
     straight back into it, on the chrome of every page. */
  color: var(--text);
}
.site-nav .lang-pick > summary::-webkit-details-marker { display: none; }
.site-nav .lang-pick .lang-globe { color: var(--text-secondary); flex: none; }
.site-nav .lang-pick > summary:hover { background: var(--surface); }
.site-nav .lang-pick[open] > summary { background: var(--surface); border-color: var(--gold-deep); }
.site-nav .lang-pick > summary:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 3px;
}

/* THREE CLASSES DEEP, for the reason the old rules were two: `.site-nav
   a:not(.btn)` is (0,2,1) and would otherwise win on size and colour. */
.site-nav .lang-pick .lang-menu {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  min-width: 170px;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm, 10px);
  box-shadow: 0 14px 28px rgba(60, 38, 10, .16);
  z-index: 50;
}
.site-nav .lang-pick .lang-menu a,
.site-nav .lang-pick .lang-menu .on {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 44px;
  padding: 10px;
  border-radius: var(--r-xs);
  font-size: var(--fs-body);
  line-height: 1.2;
  text-decoration: none;
  color: var(--text);
  background: none;
}
.site-nav .lang-pick .lang-menu .code {
  font-size: var(--fs-small);
  letter-spacing: .04em;
  color: var(--text-secondary);
}
.site-nav .lang-pick .lang-menu a:hover { background: var(--surface-elevated); color: var(--text); }
.site-nav .lang-pick .lang-menu a:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: -2px;
  background: var(--surface-elevated);
}
/* Same decision as the marker it replaces: ink for the text, gold for the mark. */
.site-nav .lang-pick .lang-menu .on {
  background: var(--surface-elevated);
  font-weight: 700;
  box-shadow: inset 2px 0 0 var(--gold-deep);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .site-nav .lang-pick .lang-menu .on { box-shadow: inset 2px 0 0 var(--gold); }
  :root:not([data-theme="light"]) .site-nav .lang-pick .lang-menu { box-shadow: 0 14px 28px rgba(0, 0, 0, .45); }
  :root:not([data-theme="light"]) .site-nav .lang-pick[open] > summary { border-color: var(--gold); }
}
:root[data-theme="dark"] .site-nav .lang-pick .lang-menu .on { box-shadow: inset 2px 0 0 var(--gold); }
:root[data-theme="dark"] .site-nav .lang-pick .lang-menu { box-shadow: 0 14px 28px rgba(0, 0, 0, .45); }
:root[data-theme="dark"] .site-nav .lang-pick[open] > summary { border-color: var(--gold); }
