/* =========================================================
   Gem Store — the reading page
   Loads after blog.css and uses its primitives (.pane, .sky,
   .sky__orb, .tag, .post). Everything here belongs to single.php
   and nothing else, which is why it is its own file: the archive
   should not carry the weight of a console block or a parallax
   sky it never renders.

   The idea the whole page is built on: reading the article is a
   flight. The header is the planet you are leaving, the rail is
   the flight plan, each section is a waypoint, and the ship on
   the rail is where you actually are.
   ========================================================= */

.page-article{
  /* `clip`, not the `hidden` style.css sets. They look identical, but `hidden`
     makes the body a scroll container — and a scroll container between a
     sticky element and the viewport is what stops it sticking. That single
     word is why the rail used to scroll away and leave the column bare.
     `clip` does the same visual job and creates no scrollport. */
  overflow-x:clip; overflow-y:visible;

  --pane-blur:22px;
  /* Type for reading, not for a card. Everything else on the site is UI. */
  --t-read: clamp(1.02rem, .96rem + .34vw, 1.16rem);
  --mono: 'Cascadia Code', 'Fira Code', Consolas, Menlo, monospace;
}

:root, :root[data-theme="light"]{
  --deep-op:    .5;    /* the parallax sky is ink on paper here — go easy */
  --cloud-op:   .16;
  --screen-bg:  #0B0A14;   /* a screen stays a screen in daylight */
  --screen-bar: #16151F;
  --screen-line:#2C2A40;
  --screen-ink: #DAD7EF;
  --screen-dim: #6F6B8C;
}
:root[data-theme="dark"]{
  --deep-op:    1;
  --cloud-op:   .3;
  --screen-bg:  #0A0913;
  --screen-bar: #14131F;
  --screen-line:#2C2A40;
  --screen-ink: #E4E1F5;
  --screen-dim: #7C789B;
}

/* ═════════════════ 1. deep space ═════════════════
   Three fixed star layers moving at three rates. Parallax is the only honest
   way to say "depth" on a flat page: the near stars sweep past while the far
   ones barely move, exactly as they do out of a window.

   Each layer is 200vh tall and holds its star pattern TWICE, one screen apart,
   so the transform can wrap with a modulo and never run out of sky. */
.deep{
  position:fixed; inset:0; z-index:-1;
  pointer-events:none; overflow:hidden;
  opacity:var(--deep-op);
}
.deep__layer{
  position:absolute; inset-inline:0; inset-block-start:0;
  block-size:200vh; will-change:transform;
}
.deep__layer .st{ position:absolute; display:block; }
.deep__layer .st svg{ inline-size:100%; block-size:100%; display:block; fill:var(--star); }

/* Two slow clouds. They give the glass panel something to refract — without
   them a frosted surface over an even background is just grey plastic. */
.deep__cloud{
  position:absolute; border-radius:50%;
  inline-size:clamp(320px, 46vw, 680px); aspect-ratio:1;
  filter:blur(90px); opacity:var(--cloud-op); will-change:transform;
}
.deep__cloud--a{ inset-block-start:-12%; inset-inline-end:-10%; background:radial-gradient(circle, var(--accent) 0%, transparent 68%); animation:cloud-a 34s ease-in-out infinite; }
.deep__cloud--b{ inset-block-end:-18%; inset-inline-start:-14%; background:radial-gradient(circle, var(--cyan) 0%, transparent 68%);   animation:cloud-b 41s ease-in-out infinite; }
@keyframes cloud-a{ 50%{ transform:translate(-5%, 7%) scale(1.14); } }
@keyframes cloud-b{ 50%{ transform:translate(6%, -6%) scale(.9); } }

/* ═════════════════ 2. the reading comet ═════════════════
   Pinned to the nav's own bottom edge. The nav is already the page's horizon
   line; a second bar floating above it would only add another edge. */
.comet{
  position:absolute; inset-block-end:-1px; inset-inline:0;
  block-size:2px; overflow:visible; pointer-events:none;
}
/* Physical `to left`, not a logical direction: the trail is anchored at the
   inline start (the right, here) and grows left, so its bright leading edge is
   always the physical left one. */
.comet__trail{
  position:relative; block-size:100%; inline-size:var(--p, 0%);
  background:linear-gradient(to left,
    transparent, color-mix(in oklch, var(--accent) 45%, transparent) 55%, var(--accent));
}
.comet__head{
  position:absolute; inset-inline-end:-3px; inset-block-start:50%;
  inline-size:7px; block-size:7px; border-radius:50%;
  transform:translateY(-50%);
  background:var(--accent);
  box-shadow:0 0 10px 2px color-mix(in oklch, var(--accent) 70%, transparent);
}

/* ═════════════════ 3. shell ═════════════════ */
.reader{
  position:relative; z-index:0;
  max-width:1160px; margin-inline:auto;
  padding:var(--s-5) var(--s-5) var(--s-9);
  --accent:var(--violet);
}
.reader > *{ position:relative; z-index:1; }

.crumb{
  display:flex; align-items:center; gap:var(--s-2); flex-wrap:wrap;
  font-size:var(--t-sm); color:var(--text-muted);
  padding-block:var(--s-2) var(--s-4);
}
.crumb a{ transition:color .18s var(--ease-out); }
.crumb a:hover{ color:var(--ink); }
.crumb span{ color:var(--ink); font-weight:700; }
/* The chevron points the way the text runs, so it flips with the document. */
.crumb svg{ inline-size:14px; block-size:14px; flex:none; fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round; stroke-linejoin:round; opacity:.5; }

/* ═════════════════ 4. head + cover ═════════════════ */
.art-head{ display:grid; gap:var(--s-4); justify-items:start; max-inline-size:62ch; padding-block:var(--s-4) var(--s-6); }
.art-head h1{ font-size:var(--t-2xl); font-weight:900; }
.art-head .lede{
  margin:0; max-inline-size:54ch;
  font-size:var(--t-lg); line-height:1.95; color:var(--text-muted);
}
.art-meta{
  display:flex; align-items:center; gap:var(--s-3); flex-wrap:wrap;
  font-size:var(--t-sm); color:var(--text-muted);
}
.art-meta .sep{ opacity:.45; }
.art-meta .post__read{ margin-inline-start:0; }

/* ── the cover ──
   One ratio, everywhere. It is declared once on .page-article and deliberately
   NOT touched in any media query: the whole point is that a phone and a laptop
   crop an uploaded photo to the same shape, so the editor can swap the image
   without ever re-checking the layout. */
.page-article{ --cover-ratio: 16 / 9; }
.cover{
  position:relative;
  aspect-ratio:var(--cover-ratio);
  margin-block-end:var(--s-7);
}
/* The galactic part lives AROUND the frame, not on it: an accent bloom leaking
   out from behind the photo. Anything printed over the image would fight
   whatever the editor uploads next week. */
.cover__aura{
  position:absolute; z-index:0; inset:2% -3% -9%;
  border-radius:50% / 42%;
  background:radial-gradient(60% 60% at 50% 60%,
    color-mix(in oklch, var(--accent) 62%, transparent), transparent 72%);
  filter:blur(52px); opacity:.55;
}
.cover__img{
  position:relative; z-index:1;
  inline-size:100%; block-size:100%;
  object-fit:cover;                 /* any upload, one shape */
  display:block; border-radius:var(--r-lg);
  border:1px solid var(--pane-line);
  box-shadow:var(--pane-shadow);
}

/* A streak every eleven seconds, crossing the whole page. Long gaps on
   purpose — a shooting star you can rely on stops being one. */
.shoot{
  position:absolute; z-index:2;
  inline-size:2px; block-size:2px; border-radius:50%;
  background:var(--star); opacity:0;
  animation:shoot 11s var(--sd, 0s) ease-in infinite;
}
.shoot::after{
  content:''; position:absolute; inset-block-start:0; left:2px;
  inline-size:88px; block-size:1px;
  background:linear-gradient(to right, color-mix(in srgb, var(--star) 75%, transparent), transparent);
  transform:rotate(-29deg); transform-origin:0 50%;
}
@keyframes shoot{
  0%   { transform:translate(0,0);        opacity:0; }
  2%   {                                   opacity:1; }
  8%   { transform:translate(-330px,183px); opacity:0; }
  100% { transform:translate(-330px,183px); opacity:0; }
}

.art-meta__who{ display:flex; align-items:center; gap:var(--s-2); color:var(--text); font-weight:700; }
/* The byline avatar is a small world of its own — same recipe as the planets,
   so the author reads as part of the same sky. */
.avatar{
  inline-size:34px; block-size:34px; border-radius:50%; flex:none;
  background:radial-gradient(circle at 32% 28%,
    color-mix(in oklch, var(--accent) 58%, white) 0%,
    var(--accent) 40%, var(--limb-core) 100%);
  box-shadow:inset -4px -5px 12px var(--limb-shade), 0 0 16px color-mix(in oklch, var(--accent) 30%, transparent);
}
.avatar--lg{ inline-size:54px; block-size:54px; }

/* ═════════════════ 5. layout ═════════════════ */
.art-layout{
  display:grid; gap:var(--s-6); align-items:start;
  grid-template-columns:minmax(0,1fr);
  margin-block-start:var(--s-7);
}
@media (min-width:1000px){
  /* The rail goes on the reading-start side. In RTL that is the right, which
     is where the eye already is at the start of every line. */
  .art-layout{ grid-template-columns:minmax(0,272px) minmax(0,1fr); }
  .art-rail{
    position:sticky; inset-block-start:92px;   /* 92px clears the nav */
    /* Bounded no matter what: a sticky column taller than the screen pins its
       top and hides its foot forever. No scrollbar of its own though — a
       second scrollbar next to the page's own is a thing to think about, and
       the rail is furniture. The route below absorbs any overflow instead. */
    max-block-size:calc(100vh - 112px);
  }
}
/* On a screen with the height to spare, the rail takes the WHOLE of it and the
   route stretches to fill what the cards leave over. That is the fix for the
   column going bare halfway down the article: there is no "left over" space
   for it to be bare in — the track simply grows into it. */
@media (min-width:1000px) and (min-height:740px){
  .art-rail{
    block-size:calc(100vh - 112px);
    align-content:stretch;
    grid-template-rows:minmax(0,1fr) auto;
  }
  .flight{ display:grid; min-block-size:0; grid-template-rows:auto minmax(0,1fr) auto; }
  .flight__scroll{ min-block-size:0; }
}
/* The list absorbs anything that does not fit — a twenty-heading article, a
   short laptop screen — but shows no scrollbar for it. The orbit line, the
   ship and the stops are positioned against the TRACK, which keeps its real
   height inside this box; putting the overflow on the track itself would size
   the line to the visible area and cut it in half. */
.flight__scroll{
  overflow-y:auto; overscroll-behavior:contain;
  scrollbar-width:none;
}
.flight__scroll::-webkit-scrollbar{ inline-size:0; block-size:0; }
.art-rail{ display:grid; gap:var(--s-4); align-content:start; }

/* ═════════════════ 6. the flight plan ═════════════════
   The contents, rebuilt as a route. Every h2 is a waypoint on the track, the
   lit part of the track is what you have flown, and the ship is where you
   actually are — it moves continuously with the scroll rather than snapping
   between headings, so the rail answers "how far in am I" and not only
   "which section". */
.flight{ padding:var(--s-4); }
.flight__title{
  display:flex; align-items:baseline; gap:var(--s-2);
  margin:0 0 var(--s-4); font-size:var(--t-sm); font-weight:900;
}
.flight__pct{ margin-inline-start:auto; font-weight:900; color:var(--accent); font-variant-numeric:tabular-nums; }

.flight__track{ position:relative; padding-inline-end:32px; }
.flight__rail, .flight__lit{
  position:absolute; inset-inline-end:12px;
  inline-size:2px; border-radius:2px;
}
.flight__rail{ inset-block:12px; background:var(--toc-idle); }
.flight__lit{
  inset-block-start:12px; block-size:var(--lit, 0px);
  max-block-size:calc(100% - 24px);
  background:linear-gradient(to bottom, color-mix(in oklch, var(--accent) 35%, transparent), var(--accent));
}
/* The ship. Its tail points back the way it came, so the rail has a direction
   even in a still frame. */
.flight__ship{
  position:absolute; inset-inline-end:6px; inset-block-start:12px;
  inline-size:14px; block-size:14px; border-radius:50%;
  transform:translateY(calc(var(--y, 0px) - 50%));
  background:radial-gradient(circle at 36% 32%, #FFF 0%, color-mix(in oklch, var(--accent) 60%, white) 30%, var(--accent) 100%);
  box-shadow:0 0 12px 3px color-mix(in oklch, var(--accent) 60%, transparent);
  z-index:2;
}
.flight__ship::after{
  content:''; position:absolute; inset-inline-start:50%; inset-block-end:100%;
  inline-size:2px; block-size:26px; transform:translateX(-50%);
  background:linear-gradient(to top, color-mix(in oklch, var(--accent) 70%, transparent), transparent);
}

.flight__stops{ list-style:none; margin:0; padding:0; }
/* 6px, not 8: ten waypoints and a product card have to share one screen, and
   the route is what has to give — its job is done by the dots, not the air
   around them. */
.flight__stops a{
  position:relative; display:block; padding-block:6px;
  font-size:var(--t-sm); line-height:1.5; color:var(--text-muted);
  transition:color .2s var(--ease-out);
}
/* Waypoints are little worlds, not stars: the archive already uses stars for
   its constellation, and the two lists should not look like the same list. */
.flight__stops a::before{
  content:''; position:absolute; inset-inline-end:-26px; inset-block-start:1.05em;
  inline-size:11px; block-size:11px; border-radius:50%;
  background:var(--surface); border:1.5px solid var(--toc-idle);
  transition:background .25s var(--ease-out), border-color .25s var(--ease-out), box-shadow .25s var(--ease-out);
}
.flight__stops a:hover{ color:var(--ink); }
.flight__stops li.is-passed a::before{
  background:color-mix(in oklch, var(--accent) 45%, var(--surface));
  border-color:color-mix(in oklch, var(--accent) 60%, transparent);
}
.flight__stops a[aria-current="true"]{ color:var(--ink); font-weight:700; }
.flight__stops a[aria-current="true"]::before{
  background:var(--accent); border-color:var(--accent);
  box-shadow:0 0 0 4px color-mix(in oklch, var(--accent) 16%, transparent),
             0 0 12px color-mix(in oklch, var(--accent) 65%, transparent);
}
/* Sub-headings are moons: smaller, and set in from the line. */
.flight__stops li[data-depth="3"] a{ font-size:.8125rem; padding-inline-end:var(--s-3); }
.flight__stops li[data-depth="3"] a::before{ inline-size:7px; block-size:7px; inset-inline-end:-24px; }

/* The route ends at a world. It sits in the footer line rather than at the
   bottom of the track, so it stays put while the list scrolls — and it lights
   up when you actually get there. */
.flight__left{
  display:flex; align-items:center; gap:var(--s-2);
  margin:var(--s-4) 0 0; font-size:.75rem; color:var(--text-muted);
}
.flight__dest{
  position:relative; flex:none; inline-size:13px; block-size:13px; border-radius:50%;
  background:radial-gradient(circle at 34% 30%,
    color-mix(in oklch, var(--accent) 58%, white), var(--accent) 62%, var(--limb-core));
  opacity:.5; transition:opacity .35s var(--ease-out), box-shadow .35s var(--ease-out);
}
.flight__dest::before{
  content:''; position:absolute; inset:-5px; border-radius:50%;
  border:1px solid color-mix(in oklch, var(--accent) 45%, transparent);
  transform:rotate(-20deg) scaleY(.4);
}
.flight.is-arrived .flight__dest{
  opacity:1; box-shadow:0 0 11px color-mix(in oklch, var(--accent) 70%, transparent);
}

/* ── the offer ──
   The product the article is about, sitting in the rail for the whole read.
   Same art recipe as the cards on the archive, at a smaller size, so it reads
   as part of the shop rather than as an ad dropped into the page. */
.offer{ display:grid; gap:6px; padding:0 0 var(--s-4); overflow:hidden; }
/* A wide, shallow strip rather than a card-sized picture: the rail has to hold
   three things and the screen is the budget. */
.offer__art{ aspect-ratio:3.5 / 1; }
.offer__kicker{ margin:var(--s-3) var(--s-4) 0; font-size:.6875rem; color:var(--text-muted); }
.offer__title{ margin-inline:var(--s-4); font-size:var(--t-base); font-weight:900; }
.offer__meta{ margin:0 var(--s-4); font-size:.6875rem; line-height:1.8; color:var(--text-muted); }
.offer__price{ margin:6px var(--s-4) 0; font-size:var(--t-lg); font-weight:900; color:var(--accent); line-height:1.4; }
.offer__price small{ font-size:.6875rem; font-weight:400; color:var(--text-muted); }
.offer__cta{ margin:var(--s-3) var(--s-4) 0; min-block-size:40px; }

/* ── share ──
   Lives at the foot of the article now, so it reads as one row rather than as
   a card: label first, then the buttons beside it. */
.share{ display:flex; align-items:center; gap:var(--s-2); flex-wrap:wrap; margin-block-end:var(--s-5); }
.share__label{ font-size:var(--t-sm); color:var(--text-muted); margin-inline-end:var(--s-2); }
.share button, .share a{
  inline-size:38px; block-size:38px; display:grid; place-items:center;
  border-radius:50%; border:1px solid var(--line); background:var(--surface);
  color:var(--text-muted); cursor:pointer;
  transition:color .18s var(--ease-out), border-color .18s var(--ease-out), transform .18s var(--ease-out);
}
.share button:hover, .share a:hover{ color:var(--accent); border-color:var(--accent); transform:translateY(-2px); }
.share svg{ inline-size:17px; block-size:17px; fill:none; stroke:currentColor; stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; }
.share__done{ font-size:.75rem; color:var(--emerald); }

/* ═════════════════ 7. the article body ═════════════════ */
.art-body{ padding:clamp(var(--s-5), 3vw, var(--s-8)); min-inline-size:0; }
.prose{
  font-size:var(--t-read);
  line-height:2.05;                 /* Persian text needs the room */
  max-inline-size:70ch;
}
.prose > * + *{ margin-block-start:var(--s-5); }
.prose p{ margin:0; }
.prose a:not(.btn){
  color:var(--accent); font-weight:700;
  border-block-end:1px solid color-mix(in oklch, var(--accent) 40%, transparent);
}
.prose a:not(.btn):hover{ border-block-end-color:var(--accent); }
.prose strong{ font-weight:900; }

/* The opening paragraph. Bigger, and lit from the start edge — the first block
   of an article has to look like a beginning. */
.prose .lead{
  position:relative; font-size:1.1em; color:var(--text);
  padding-inline-start:var(--s-5);
}
.prose .lead::before{
  content:''; position:absolute; inset-block:.35em; inset-inline-start:0;
  inline-size:3px; border-radius:3px;
  background:linear-gradient(to bottom, var(--accent), color-mix(in oklch, var(--accent) 15%, transparent));
  box-shadow:0 0 14px color-mix(in oklch, var(--accent) 50%, transparent);
}

/* ── waypoint headings ──
   Each h2 gets a numbered world with a tilted ring, injected by article.js.
   The grid keeps the badge on the first line of a heading that wraps, and the
   hairline underneath is what turns a heading into a section break. */
.prose h2{
  display:grid; grid-template-columns:auto minmax(0,1fr);
  align-items:center; gap:var(--s-3);
  margin-block-start:var(--s-9); scroll-margin-block-start:104px;
  font-size:var(--t-xl); font-weight:900; line-height:1.5;
}
.prose h2::after{
  content:''; grid-column:1 / -1;
  block-size:1px; margin-block-start:var(--s-3);
  background:linear-gradient(to left, color-mix(in oklch, var(--accent) 55%, transparent), transparent 72%);
}
.way{
  position:relative; display:grid; place-items:center;
  inline-size:46px; block-size:46px; flex:none; border-radius:50%;
  font-size:var(--t-sm); font-weight:900; color:var(--accent);
  background:radial-gradient(circle at 34% 28%,
    color-mix(in oklch, var(--accent) 26%, var(--surface)),
    color-mix(in oklch, var(--accent) 7%, var(--surface)));
  box-shadow:inset 0 0 0 1px color-mix(in oklch, var(--accent) 42%, transparent),
             0 0 24px color-mix(in oklch, var(--accent) 18%, transparent);
}
/* Its ring — the same orthographic trick the planets use, so a section marker
   and a planet are visibly the same kind of object. */
.way::before{
  content:''; position:absolute; inset:-8px; border-radius:50%;
  border:1px solid color-mix(in oklch, var(--accent) 45%, transparent);
  transform:rotate(-22deg) scaleY(.4);
}
/* One pulse when the section becomes the current one. Announcing arrival is
   the only motion on the page that is triggered rather than looping. */
.prose h2.is-here .way::after{
  content:''; position:absolute; inset:-8px; border-radius:50%;
  border:1px solid var(--accent);
  animation:way-ping 1.1s var(--ease-out) 1;
}
@keyframes way-ping{
  from{ transform:scale(.9); opacity:.9; }
  to  { transform:scale(1.7); opacity:0; }
}

.prose h3{
  position:relative; font-size:var(--t-lg); font-weight:700;
  margin-block-start:var(--s-7); scroll-margin-block-start:104px;
  padding-inline-start:20px;
}
.prose h3::before{
  content:''; position:absolute; inset-inline-start:0; inset-block-start:.62em;
  inline-size:10px; block-size:10px; background:var(--accent); opacity:.8;
  -webkit-mask:var(--star-mask) center/contain no-repeat;
          mask:var(--star-mask) center/contain no-repeat;
  filter:drop-shadow(0 0 5px color-mix(in oklch, var(--accent) 55%, transparent));
}

/* ── lists ── */
.prose ul, .prose ol{ margin:0; padding-inline-start:var(--s-5); display:grid; gap:var(--s-3); }
.prose ul{ list-style:none; padding-inline-start:var(--s-6); }
.prose ul li{ position:relative; }
.prose ul li::before{
  content:''; position:absolute; inset-inline-start:calc(-1 * var(--s-5)); inset-block-start:.72em;
  inline-size:9px; block-size:9px; border-radius:50%;
  background:radial-gradient(circle at 32% 30%, color-mix(in oklch, var(--accent) 55%, white), var(--accent) 70%, var(--limb-core));
}
.prose ol li::marker{ color:var(--accent); font-weight:900; }

/* ═════════════════ 8. the console ═════════════════
   Terminal blocks stay dark in both themes. A screen is a screen; a "light
   mode terminal" is a screenshot of something nobody's machine looks like. */
.console{
  direction:ltr; text-align:left;
  margin:var(--s-6) 0; border-radius:var(--r-md); overflow:hidden;
  background:var(--screen-bg); border:1px solid var(--screen-line);
  box-shadow:var(--e-2), 0 0 40px -10px color-mix(in oklch, var(--accent) 55%, transparent);
}
.console__bar{
  display:flex; align-items:center; gap:6px;
  padding:9px var(--s-4);
  background:var(--screen-bar); border-block-end:1px solid var(--screen-line);
}
.console__bar i{ inline-size:9px; block-size:9px; border-radius:50%; background:#3A3752; }
.console__name{ margin-inline-start:8px; font-family:var(--mono); font-size:.72rem; color:var(--screen-dim); }
.console__copy{
  margin-inline-start:auto; padding:3px 10px; border-radius:var(--r-full);
  font-family:inherit; font-size:.7rem; cursor:pointer;
  background:transparent; color:var(--screen-dim);
  border:1px solid var(--screen-line);
  transition:color .18s var(--ease-out), border-color .18s var(--ease-out);
}
.console__copy:hover{ color:var(--screen-ink); border-color:var(--screen-dim); }
.console__copy[data-done]{ color:var(--emerald); border-color:var(--emerald); }
.console pre{
  position:relative; margin:0; padding:var(--s-4) var(--s-5);
  overflow-x:auto;                  /* long commands scroll here, not the page */
}
/* The faintest scanline. At this opacity it is not visible as lines — it just
   stops the black from looking like a flat swatch. */
.console pre::after{
  content:''; position:absolute; inset:0; pointer-events:none;
  background:repeating-linear-gradient(to bottom, rgba(255,255,255,.025) 0 1px, transparent 1px 3px);
}
.console code{
  font-family:var(--mono); font-size:.84rem; line-height:1.95;
  color:var(--screen-ink); white-space:pre;
}
/* .c-cmd marks a line the reader is meant to run — the copy button in
   article.js treats its presence as "this block is a session, hand over the
   commands only". Anything that is a file rather than a session uses .c-key. */
.c-dim{ color:var(--screen-dim); }
.c-cmd{ color:#7DD3FC; }
.c-key{ color:#C4B5FD; }
.c-ok { color:#4ADE80; }
.c-warn{ color:#FBBF24; }

/* Inline code and keys. Both forced LTR — a command reversed by the paragraph's
   direction is not a typo the reader can see, which makes it the worst kind. */
.prose :not(pre) > code{
  display:inline-block; direction:ltr;
  padding:1px 7px; border-radius:6px;
  font-family:var(--mono); font-size:.84em; line-height:1.7;
  color:var(--accent);
  background:color-mix(in oklch, var(--accent) 11%, var(--surface));
  border:1px solid color-mix(in oklch, var(--accent) 24%, transparent);
}
kbd{
  display:inline-block; direction:ltr;
  padding:1px 8px; border-radius:7px;
  font-family:var(--mono); font-size:.8em; font-weight:700;
  background:var(--surface-2); color:var(--ink);
  border:1px solid var(--line); box-shadow:0 1.5px 0 var(--line);
}

/* ═════════════════ 9. blocks inside the text ═════════════════ */
.quote{
  position:relative; margin:var(--s-7) 0; padding:var(--s-5) var(--s-6);
  border-radius:var(--r-md); border:0;
  background:linear-gradient(to left,
    color-mix(in oklch, var(--accent) 12%, transparent), transparent 78%);
  font-size:var(--t-lg); font-weight:700; line-height:1.95;
}
.quote::before{
  content:''; position:absolute; inset-block:0; inset-inline-start:0;
  inline-size:3px; border-radius:3px;
  background:linear-gradient(to bottom, var(--accent), color-mix(in oklch, var(--accent) 20%, transparent));
  box-shadow:0 0 14px color-mix(in oklch, var(--accent) 55%, transparent);
}
.quote p{ margin:0; }
.quote cite{ display:block; margin-block-start:var(--s-3); font-size:var(--t-sm); font-weight:400; font-style:normal; color:var(--text-muted); }

/* Callouts. --tone is the only thing that changes between them. */
.callout{
  display:grid; grid-template-columns:auto minmax(0,1fr); gap:var(--s-3);
  margin:var(--s-6) 0; padding:var(--s-4) var(--s-5);
  border-radius:var(--r-md);
  border:1px solid color-mix(in oklch, var(--tone) 34%, transparent);
  background:color-mix(in oklch, var(--tone) 9%, var(--surface));
  font-size:var(--t-base); line-height:1.95;
}
.callout--note{ --tone:var(--cyan); }
.callout--warn{ --tone:var(--amber); }
.callout--tip { --tone:var(--emerald); }
  /* color و نه stroke — symbol خودش stroke="currentColor" دارد و صفت
     نمایشی از مقدار به‌ارث‌رسیده قوی‌تر است. */
.callout svg{ inline-size:21px; block-size:21px; margin-block-start:.35em; flex:none; color:var(--tone); }
.callout strong{ display:block; color:var(--tone); font-weight:900; margin-block-end:2px; }
.callout p{ margin:0; }
.callout p + p{ margin-block-start:var(--s-2); }

/* Numbered steps, each on its own little orbit. */
.steps{ list-style:none; margin:var(--s-6) 0; padding:0; display:grid; gap:var(--s-4); }
.steps li{ display:grid; grid-template-columns:auto minmax(0,1fr); gap:var(--s-4); align-items:start; }
.steps b{
  inline-size:38px; block-size:38px; border-radius:50%; display:grid; place-items:center;
  font-size:var(--t-sm); font-weight:900; color:var(--accent);
  background:color-mix(in oklch, var(--accent) 12%, var(--surface));
  border:1px solid color-mix(in oklch, var(--accent) 34%, transparent);
  box-shadow:0 0 18px color-mix(in oklch, var(--accent) 16%, transparent);
}
.steps h4{ margin:0 0 2px; font-size:var(--t-base); font-weight:900; line-height:1.7; }
.steps p{ margin:0; }
.steps p + p{ margin-block-start:var(--s-2); }

/* Tables: hairlines only. A boxed grid fights the glass panel it sits on. */
.table-wrap{ margin:var(--s-6) 0; overflow-x:auto; border-radius:var(--r-md); border:1px solid var(--line); }
.prose table{ inline-size:100%; border-collapse:collapse; font-size:var(--t-sm); }
.prose th, .prose td{ padding:var(--s-3) var(--s-4); text-align:start; border-block-end:1px solid var(--line); }
.prose th{ font-weight:900; background:var(--surface-2); white-space:nowrap; }
.prose tbody tr:last-child td{ border-block-end:0; }
.prose td b{ color:var(--accent); }

.prose figure{ margin:var(--s-7) 0; }
.prose figure .sky{ aspect-ratio:16 / 9; border-radius:var(--r-md); border:1px solid var(--line); }
.prose figcaption{ margin-block-start:var(--s-3); font-size:var(--t-sm); color:var(--text-muted); text-align:center; }

/* ═════════════════ 10. arrival ═════════════════
   The end of the flight. Same horizon device as the header, upside down and
   much smaller: you left one world and you are setting down on another. */
.landing{
  position:relative; overflow:hidden;
  margin-block-start:var(--s-8); padding:var(--s-7) clamp(var(--s-5), 3vw, var(--s-7)) var(--s-6);
  border-radius:var(--r-md); border:1px solid var(--pane-line);
}
/* Only a sliver of this one shows — a lit edge along the foot of the panel.
   420% is not decoration: at that radius the visible cap still spans the whole
   width, and a narrower circle would end mid-panel and read as a bump. */
.landing__ground{
  position:absolute; z-index:0;
  inset-block-start:88%; left:50%; transform:translateX(-50%);
  inline-size:420%; aspect-ratio:1; border-radius:50%;
  background:radial-gradient(circle at 50% 8%,
    color-mix(in oklch, var(--accent) 40%, white) 0%,
    color-mix(in oklch, var(--accent) 70%, var(--limb-core)) 14%,
    var(--limb-core) 46%);
  box-shadow:0 -10px 60px 2px color-mix(in oklch, var(--accent) 30%, transparent),
             0 0 0 1px color-mix(in oklch, var(--accent) 55%, white);
  opacity:.9;
}
.landing > *{ position:relative; z-index:1; }
.landing__eyebrow{
  display:inline-flex; align-items:center; gap:var(--s-2);
  margin:0 0 var(--s-4); font-size:var(--t-sm); font-weight:900; color:var(--accent);
}
.landing__eyebrow::before{
  content:''; inline-size:12px; block-size:12px; background:currentColor;
  -webkit-mask:var(--star-mask) center/contain no-repeat;
          mask:var(--star-mask) center/contain no-repeat;
}
.chips{ display:flex; flex-wrap:wrap; gap:var(--s-2); margin-block-end:var(--s-5); }
.chips a{
  padding:5px var(--s-4); border-radius:var(--r-full); font-size:var(--t-sm);
  background:var(--surface);
  border:1px solid var(--line); color:var(--text-muted);
  transition:color .18s var(--ease-out), border-color .18s var(--ease-out);
}
.chips a:hover{ color:var(--ink); border-color:var(--ink); }
.author{
  display:flex; gap:var(--s-4); align-items:flex-start;
  padding:var(--s-5); border-radius:var(--r-md);
  background:color-mix(in srgb, var(--pane-bg) 70%, var(--surface));
  border:1px solid var(--pane-line);
}
.author h3{ font-size:var(--t-base); font-weight:900; }
.author p{ margin:4px 0 0; font-size:var(--t-sm); color:var(--text-muted); max-inline-size:56ch; }

.related{ margin-block-start:var(--s-9); }

/* ═════════════════ 11. mobile ═════════════════ */
@media (max-width:1000px){
  /* The rail stops being a rail and becomes a contents card above the text.
     A sticky sidebar with nowhere to sit is just a box in the way. */
  .art-rail{ position:static; max-block-size:none; overflow:visible; }
  .flight__ship{ display:none; }   /* the comet in the nav already says where you are */
  /* The offer goes to the end of the column so it lands after the article on a
     phone instead of between the reader and the first paragraph. */
  .offer{ order:2; }
}
@media (max-width:768px){
  /* --cover-ratio is NOT touched here on purpose. See the .cover block. */
  .cover{ margin-block-end:var(--s-6); }
  .cover__img{ border-radius:var(--r-md); }
  .prose h2{ gap:var(--s-2); }
  .way{ inline-size:38px; block-size:38px; font-size:.8125rem; }
  .art-body{ padding:var(--s-4); }
  .deep__cloud{ filter:blur(70px); }
}

/* ═════════════════ 12. reduced motion ═════════════════ */
@media (prefers-reduced-motion: reduce){
  .deep__cloud, .shoot{ animation:none; }
  .shoot{ display:none; }
  .prose h2.is-here .way::after{ animation:none; display:none; }
  /* The parallax is driven from JS; article.js checks the same query and
     leaves the layers where they are. */
}
