/* ===== Layout ===== */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section {
  padding-block: var(--sp-6);
  position: relative;
  z-index: var(--z-content);
}

main { position: relative; z-index: var(--z-content); }

/* --- dot grid (Figma/Miro-style) — bottom-most background layer ---
   Pure CSS, no cursor reaction. Absolute, not fixed: it spans the whole
   document and scrolls with the page, so the dots travel past you instead of
   sitting still like a screen overlay. `inset: 0` inside a `position: relative`
   body stretches to the body's full height even though that height is auto —
   a percentage height could not. */
/* FIXED, not absolute: the grid stays put in the viewport and the content
   scrolls over it. It used to be absolute + inset:0 inside a relative body so
   it spanned the whole document and travelled with the page — which meant the
   dots drifted upward as you read. Fixed positions it against the viewport
   instead, so `inset: 0` is now simply the screen and the body's auto height
   stops mattering. */
.dot-grid {
  position: fixed;
  inset: 0;
  z-index: var(--z-dot-grid);
  pointer-events: none;
  background-image: radial-gradient(circle, var(--dot-grid) 1px, transparent 1.5px);
  background-size: 24px 24px;
  transition: background-image var(--theme-dur) var(--ease-out);
}

/* --- nav dock: pill nav + sound toggle + theme toggle, side by side --- */
.nav-dock {
  position: fixed;
  bottom: var(--sp-3);
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--z-nav);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  max-width: calc(100vw - 2 * var(--sp-3));
}
/* A little more room on phones: content-width tabs need more than equal shares
   did, and the gutter either side of the dock is decoration, not layout. This
   buys 16px, which is the difference between comfortable and cramped gaps at
   375px. */
@media (max-width: 768px) {
  .nav-dock { max-width: calc(100vw - 2 * var(--space-2)); }
}

/* pill nav — ported from Notion "💊 Pill nav" (tested); colours themed.
   The capsule itself is .pill-bar (css/components/pill-bar.css), shared with
   the Hero controls; only the sliding-highlight machinery lives here. */
.nav {
  position: relative;
  isolation: isolate;
  /* How far the active highlight reaches past the link's own box, per side.
     Lives here rather than on .nav__pill so the capsule can budget for it
     below — both need the same number. */
  --nav-pill-grow: var(--space-1);
}
/* Two classes deep on purpose: .nav alone ties with .pill-bar on specificity,
   and pill-bar.css is linked after this file, so its box-shadow — and its
   `padding` shorthand, which resets padding-inline — would win. */
.nav-dock .nav {
  /* .pill-bar's uniform padding is the gap the highlight should keep from the
     capsule's inner edge, but the highlight starts --nav-pill-grow before the
     first link and ends that much after the last, which ate the whole gap at
     the two ends. Adding the grow back to the inline padding keeps the side
     gap equal to the top/bottom one, whatever either value is set to. */
  padding-inline: calc(var(--pill-bar-pad) + var(--nav-pill-grow));
  /* Heavier than the .pill-bar default: the dock is the one element that
     floats over everything and now that the cards beneath it are veiled
     (--c-surface-veil), it needs the shadow to sell that separation, not just
     the opacity difference. Deliberately its own value rather than
     --shadow-md — that step's 44px blur bands visibly on mobile Safari, which
     is why --shadow-sm is trimmed there in the first place. A tighter, darker
     spread reads as heavier without the gradient stepping. */
  box-shadow: 0 2px 8px rgba(var(--shadow-tint), .10),
              0 10px 24px rgba(var(--shadow-tint), .18);
}
/* highlight pill: a single element for the whole menu */
.nav__pill {
  /* Widened by --nav-pill-grow either side (declared on .nav) so the highlight
     has air around the label instead of hugging it. JS keeps writing the raw
     measured --x/--w (nav-pill.js) and the padding is applied here — the
     module stays a pure measurer, this stays a purely visual choice. */
  position: absolute;
  z-index: 0;
  /* must track .pill-bar's padding, or the highlight sits proud of the links */
  top: var(--pill-bar-pad);
  bottom: var(--pill-bar-pad);
  left: 0;
  width: calc(var(--w, 0px) + 2 * var(--nav-pill-grow));
  transform: translateX(calc(var(--x, 0px) - var(--nav-pill-grow)));
  background: var(--c-bg);
  border-radius: var(--radius-pill);
  /* The .38s is hand-tuned against the overshoot curve — the capsule has to
     arrive, overshoot and settle within one glance, and the pair was set by
     eye on this specific travel distance. It stays a literal rather than
     being snapped to a duration step; only the curve is tokenised.
     See MOTION_AUDIT.md. */
  transition: transform .38s var(--ease-overshoot),
              width .38s var(--ease-overshoot),
              background var(--theme-dur) var(--ease-out);
  pointer-events: none;
}
/* first frame without animation, so the pill doesn't fly in from the corner */
.nav__pill.is-instant { transition: none; }

/* The capsule holds the section tabs and nothing else — the name that used to
   sit to the left of them is now the standalone .logo in the Hero's top-left. */

.nav__link {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  text-decoration: none;
  color: var(--c-ink-soft);
  white-space: nowrap;
  /* was .25s with no curve at all — the odd one out among the page's colour
     fades, which all run at --dur-fast through the house curve */
  transition: color var(--motion-hover);
  -webkit-tap-highlight-color: transparent;
}
.nav__link:hover { color: var(--c-ink); }
.nav__link.is-active { color: var(--c-ink); font-weight: 500; }
/* No focus ring on the tabs: the sliding highlight already follows keyboard
   focus (nav-pill.js moves it on 'focus', not just on hover), so the outline
   was a second indicator drawn on top of a working one. Every other focusable
   control on the page keeps its ring — they have nothing equivalent. */

/* theme icon swap */
[data-theme-toggle] .icon-sun  { display: none; }
[data-theme-toggle] .icon-moon { display: block; }
:root[data-theme="dark"] [data-theme-toggle] .icon-sun  { display: block; }
:root[data-theme="dark"] [data-theme-toggle] .icon-moon { display: none; }

/* --- colour picker: a button in the Hero controls bar + a panel under it ---
   Same surface/border/shadow language as the pill bar and the About panel.
   The swatch row is generated by bg-shade.js — the background options differ
   by theme, so there's nothing static to style here beyond the shell.
   Swatches paint themselves from the --shade-* token they select, so the menu
   previews the real colour. Opens DOWNWARD, right-aligned: the bar is
   horizontal and pinned to the top-right corner, so below is the only
   direction with room.

   Sized by its contents (no fixed width): it used to be a fixed 210px to hold
   two swatch rows plus their labels, which left it half empty once the
   particle row was removed. */
.shade-picker { position: relative; flex-shrink: 0; }

.shade-menu {
  position: absolute;
  top: calc(100% + var(--sp-2));
  right: 0;
  width: max-content;
  display: grid;
  /* label → swatches. Was --space-1 (4px), which read as one clump once the
     selected swatch's ring reached up into it. .lang-menu sets its own gap, so
     this only moves the colour picker. */
  gap: var(--space-2);
  padding: var(--sp-2);
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  opacity: 0;
  transform: translateY(-6px);
  pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out),
              background var(--theme-dur) var(--ease-out),
              border-color var(--theme-dur) var(--ease-out);
}
.shade-menu.is-open {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

.shade-menu__label {
  font-size: var(--fs-100);
  color: var(--c-ink-soft);
  /* Centred to match the swatch row below it. Which of the two is the wider
     element flips with the number of options — at three swatches the row was
     wider than the word and the label sat left inside it; at two the word is
     wider and the row was the one adrift. Centring both makes the block read
     the same either way. */
  text-align: center;
}

/* --- language menu: the same popup shell as the colour picker (it reuses
   .shade-menu), with text options instead of swatches. One row, because three
   two-letter codes are narrower stacked side by side than the shell's own
   padding. The current language is filled with --c-bg, the same wash the nav's
   sliding pill and the dock buttons use for "this one is selected". --- */
.lang-menu {
  grid-auto-flow: column;
  gap: var(--space-1);
}
.lang-option {
  padding: var(--space-1) var(--space-2);
  border: 0;
  border-radius: var(--radius-pill);
  background: none;
  color: var(--c-ink-soft);
  font-size: var(--fs-100);
  font-weight: 500;
  letter-spacing: .02em;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--motion-hover),
              color var(--motion-hover);
}
.lang-option:hover { color: var(--c-ink); background: var(--c-bg); }
.lang-option.is-active { color: var(--c-ink); background: var(--c-bg); }
.lang-option:focus-visible { outline: 2px solid var(--c-ink); outline-offset: 2px; }
/* No bottom margin: it existed to separate this row from the label of the
   row beneath it, and there is no row beneath it any more. */
/* The row is a grid item, so it stretches to the menu's content width — with
   two swatches that left the pair flush LEFT under a wider label and 17px of
   dead space on the right. Centred, the block stays balanced at any count.

   The padding is not decoration: the selected swatch's ring is a box-shadow
   (see [aria-checked] below), drawn 4px OUTSIDE the swatch, and box-shadow
   takes no part in layout. Without these 4px the ring silently ate into the
   menu's own padding — 12px of clearance where every other edge had 16. */
.shade-menu__swatches {
  display: flex;
  justify-content: center;
  /* 12, not 8: two neighbouring rings are 4px each, so at an 8px gap a
     selected swatch's ring finished exactly on its neighbour's edge. */
  gap: var(--space-3);
  padding: var(--space-1);
}

.shade-swatch {
  width: 26px; height: 26px;
  border-radius: 50%;
  border: 1px solid var(--c-line);
  background: var(--sw);
  cursor: pointer;
  padding: 0;
  transition: transform var(--motion-hover),
              box-shadow var(--motion-hover);
}
.shade-swatch:hover { transform: scale(1.12); }
.shade-swatch:focus-visible { outline: 2px solid var(--c-ink); outline-offset: 2px; }
/* selected: a ring in the ink colour, drawn outside the swatch */
.shade-swatch[aria-checked="true"] {
  box-shadow: 0 0 0 2px var(--c-surface), 0 0 0 4px var(--c-ink);
}

/* Background shades — the modifier only points at the token; the swatch's own
   size/hover/focus/checked styling above is shared by the particle swatches
   too (those set their background via an inline style instead, since a
   particle colour isn't a --shade-* token — see bg-shade.js). */
.shade-swatch--violet { --sw: var(--shade-violet); }
.shade-swatch--azure  { --sw: var(--shade-azure); }
.shade-swatch--indigo { --sw: var(--shade-indigo); }
.shade-swatch--noir   { --sw: var(--shade-noir); }

@media (prefers-reduced-motion: reduce) {
  .shade-menu   { transition: opacity var(--dur-fast) linear; transform: none; }
  .shade-swatch:hover { transform: none; }
}

/* sound icon swap (off by default) */
[data-sound-toggle] .icon-sound-on  { display: none; }
/* No extra opacity here any more: every bar icon is already muted at rest
   (.pill-bar__btn in pill-bar.css), and stacking .6 on top of that made the
   muted-sound state read as broken rather than off. */
[data-sound-toggle] .icon-sound-off { display: block; }
[data-sound-toggle][aria-pressed="true"] .icon-sound-on  { display: block; }
[data-sound-toggle][aria-pressed="true"] .icon-sound-off { display: none; }

@media (max-width: 768px) {
  /* mobile: name gone → just the tabs. The sound/colour/theme buttons moved
     out to the Hero controls bar, so the dock is only the pill now.

     Width is pinned to the page gutter rather than left to the tabs' own
     content width: the dock floats over the page, and a capsule that ends
     somewhere mid-margin reads as misaligned against everything it overlaps
     (the "contacts" heading, the contact pills, the card edges). Matching
     .container's inline padding puts its two ends on the same vertical lines
     as the text behind it. --gutter, not a literal, so it can't drift from
     .container. */
  .nav-dock {
    gap: var(--space-2);
    width: calc(100vw - 2 * var(--gutter));
    max-width: none;
  }
  /* the capsule fills the dock, and the five tabs share it evenly — with
     content-width tabs the extra room would all pool at the ends. Equal
     shares also keep the sliding highlight's travel even.

     min-width: 0 is what stops the capsule bursting its container. A flex
     item refuses to shrink below its min-content width by default, so on a
     320px screen the five tabs pushed the capsule 14px (EN) to 38px (RU)
     past the dock's width — and in Russian the last tab was shoved off the
     screen and could not be tapped at all. With min-width: 0 the tabs give
     up their own padding first and the capsule stays inside the gutter. */
  .nav {
    flex: 1;
    min-width: 0;
    /* the capsule spans the dock, so the leftover width goes into the gaps
       between content-width tabs — see .nav__link below */
    justify-content: space-between;
  }
  /* Type size is the nav's, not the footer's, hand-me-down: the dock is the
     primary control on the page and the contact pills are secondary, so the
     dock reads at least as large. .9rem is the same size the tabs use on
     desktop — the mobile shrink to .78rem existed only because the capsule
     was content-width and had to fit five labels; now that it spans the
     gutter there's room. See the footer for the matching step down. */
  /* Content width, NOT equal shares. `flex: 1` gave every tab exactly 1/5 of
     the capsule, so "Adventures" overflowed its box while "Home" sat in a box
     half empty — the labels were evenly spaced but the WORDS were not. Measured
     at 945px: five identical 59px boxes, and ink gaps of 26.8 / 26.9 / 6.9 /
     11.9px. The ≤360px block below already worked this out and switched to
     content width for the same reason; it just never applied above 360.

     With content-width tabs, `justify-content: space-between` on the capsule
     distributes the slack into the four gaps instead, so the space between one
     word and the next is the same everywhere. `0 1 auto` — no grow, but shrink
     allowed, so a language with longer labels compresses rather than bursting
     the capsule. */
  .nav__link {
    flex: 0 1 auto;
    min-width: 0;                  /* same reason as .nav above */
    justify-content: center;
    padding: var(--space-2) var(--space-1);
    /* a point under the desktop .9rem: with the highlight now widened
       (--nav-pill-grow) the labels needed to give that capsule some room, and
       the smaller type also buys back the slack the five tabs were using up */
    font-size: var(--text-sm);
  }
}
/* Below 360px the five tabs need one more step down to fit honestly.
   min-width: 0 alone only stops the capsule bursting the dock — the labels
   then overlap each other inside their shrunken boxes, which measures as
   "no overflow" while reading as broken. Dropping to --text-xs is what
   actually makes them fit: measured, the English set needs 228px of text at
   14px and 208px at 12.8px, against 224px of room at 320px. */
@media (max-width: 360px) {
  .nav__link {
    font-size: var(--text-xs);
    padding-inline: var(--space-1);
    /* Equal shares are what actually broke here. `flex: 1` gives every tab
       1/5 of the capsule, so "Adventures" cannot borrow the room "Blog" is
       not using — it overflows its own box and collides with its neighbour.
       Content width, shrinkable, lets the row distribute honestly. The even
       highlight travel that equal shares bought is worth less than legible
       labels at the size where the two conflict.

       0 0 — no shrink either. A shrinkable tab quietly squeezes its text
       out of its own box, which measures as "fits" and reads as overlap;
       refusing to shrink is what pushes the overflow up to the capsule,
       where it turns into a scroll instead of a collision. */
    flex: 0 0 auto;
    min-width: auto;
  }
  /* Last resort at heavy browser zoom: five tabs cannot fit 320px at a 20px
     base whatever the flex rules say, so the capsule scrolls instead of
     clipping. The sliding highlight is positioned inside it and scrolls with
     the tabs, so it stays aligned. */
  .nav-dock .nav { overflow-x: auto; scrollbar-width: none; }
  .nav-dock .nav::-webkit-scrollbar { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .nav__pill { transition: none; }
}

/* --- About panel: TWO layouts, split at 768px ---
   Same card language as the nav (surface, border, shadow) in both.

   • Phone (the rules below, up to 767px) — a centered modal with a dimming
     backdrop and a × button. A phone screen has no room for a panel that
     sits *beside* the page, so it takes the whole thing over.
   • Desktop (the `min-width: 768px` block further down) — the original
     dropdown: a compact card that expands upward out of the nav dock, no
     backdrop, no × , page stays visible and clickable behind it.

   Mobile-first on purpose: the modal is the more constrained layout, so it
   reads as the base and the desktop block only lifts the restrictions. */
.about-panel-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-about-panel);
  background: var(--overlay-scrim);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--motion-enter);
}
.about-panel-backdrop.is-open {
  opacity: 1;
  pointer-events: auto;
}

.about-panel {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(.97);
  z-index: calc(var(--z-about-panel) + 1);
  width: min(94vw, 960px);
  height: min(90vh, 860px);
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  display: flex;
  flex-direction: column;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--motion-enter),
              transform var(--motion-enter),
              background var(--theme-dur) var(--ease-out),
              border-color var(--theme-dur) var(--ease-out);
}
.about-panel.is-open {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
  pointer-events: auto;
}
.about-panel__close {
  position: absolute;
  top: var(--sp-3);
  right: var(--sp-3);
  z-index: 1;
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--c-ink);
  background: var(--c-bg);
  transition: background var(--motion-hover);
}
.about-panel__close:hover { background: var(--c-line); }
.about-panel__close:focus-visible { outline: 2px solid var(--c-ink); outline-offset: 2px; }
.about-panel__close svg { width: 18px; height: 18px; }

.about-panel__inner { flex: 1; min-height: 0; }
.about-panel__scroll {
  height: 100%;
  overflow-y: auto;
  padding: var(--space-12);
}

/* ===== Desktop: the original dropdown =====
   Expands upward out of the nav dock instead of taking over the screen.
   The grid-template-rows 0fr→1fr trick animates it open without measuring
   content height in JS.

   Layout per the reference sketch (the original two-column form): title,
   then a portrait photo in the LEFT column with the text beside it on the
   right and the contact links under the text — and NO internal scrolling:
   the panel is exactly as tall as its content, everything visible at once.
   The columns come from the base .about-panel__grid rule (200px 1fr);
   nothing here overrides them. */
@media (min-width: 768px) {
  /* nothing dims or blocks the page — that's the whole point of the revert */
  .about-panel-backdrop { display: none; }
  /* Escape, a click outside, or another nav link all still close it, so the
     × would be redundant chrome on a panel this small. */
  .about-panel__close { display: none; }

  .about-panel {
    top: auto;
    bottom: var(--about-panel-bottom, 76px);   /* set by JS: just above the nav dock */
    transform: translateX(-50%) translateY(8px);
    /* under the dock it hangs off, unlike the modal which covers everything */
    z-index: var(--z-about-dropdown);
    width: min(92vw, 780px);
    height: auto;
    display: grid;
    grid-template-rows: 0fr;
    transform-origin: bottom center;
    transition: grid-template-rows var(--dur-slow) var(--ease-out),
                opacity var(--motion-enter),
                transform var(--motion-enter),
                background var(--theme-dur) var(--ease-out),
                border-color var(--theme-dur) var(--ease-out);
  }
  .about-panel.is-open {
    grid-template-rows: 1fr;
    transform: translateX(-50%) translateY(0);
  }

  /* Rhythm per the reference mockup: a bold title with more air under it,
     a bolder lede, the points running as one contiguous block (no gap
     between them — they read as a single paragraph broken into lines by
     topic, not as separate paragraphs), then a wide gap before a bold
     contacts row. Wider column gap to match the mockup's airier columns. */
  .about-panel__grid { gap: var(--space-12); }
  .about-panel__title { margin-bottom: var(--sp-4); }
  .about-panel__body p.about-panel__lede { font-weight: 700; }
  .about-panel__points p { margin-bottom: 0; }
  .about-panel__points { margin-bottom: var(--sp-5); }
  .about-panel__contacts a { font-weight: 700; opacity: 1; }

  .about-panel__inner { flex: none; overflow: hidden; min-height: 0; }
  /* no scroll: the panel grows to its content and shows all of it.
     Extra bottom padding so the contact links don't sit right on the
     panel's lower edge. */
  .about-panel__scroll {
    height: auto;
    max-height: none;
    overflow: visible;
    padding: var(--space-6) var(--space-6) var(--space-12);
  }

  /* The photo frame and the text column share a grid row top, but the first
     text line carries half its leading ((1.6 − 1) / 2 = 0.3em) as blank
     space above the glyphs, so the text reads as sitting lower than the
     frame. Pull the column up by exactly that half-leading so the CAP of
     the first line sits level with the photo's top edge. */
  .about-panel__body { margin-top: -0.3em; }

}

/* same lowercase-italic family as .section__title/.footer__title */
.about-panel__title {
  font-size: var(--fs-400);
  font-style: italic;
  text-transform: lowercase;
  margin-bottom: var(--sp-3);
}

.about-panel__grid {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: var(--space-6);
  align-items: start;
}
@media (max-width: 640px) {
  .about-panel__grid { grid-template-columns: 1fr; }
}

/* Portrait frame filling the grid's left column, per the reference mockup:
   a plain soft-cornered fill, no placeholder border. Only desktop ever
   shows it — the phone modal hides it below. */
.about-panel__photo {
  aspect-ratio: 3 / 4;
  border-radius: var(--radius-lg);
  /* Still painted: it shows through for the moment before a lazy image
     decodes, so the slot never flashes as a hole in the panel. */
  background: var(--c-bg);
  overflow: hidden;   /* the radius has to clip the photo, not just the box */
}
/* The wrapper owns the slot's shape; the image just fills it. `cover` rather
   than `contain` because the source is 0.744 against the slot's 0.750 — a
   crop of well under one percent, and letterboxing that would be absurd. */
.about-panel__photo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
@media (max-width: 640px) {
  /* the modal is already tight on a phone screen — drop the photo rather
     than shrink it further into a postage stamp */
  .about-panel__photo { display: none; }
}

/* Each paragraph reads as its own block — the separation is spacing and
   nothing else, no rules or cards, to match the rest of the site. The gap is
   deliberately wider than the line height so the eye lands on one thought at
   a time instead of a slab of text. */
.about-panel__body p {
  color: var(--c-ink-soft);
  line-height: var(--leading-relaxed);
  margin-bottom: var(--sp-3);
  max-width: 58ch;
}

/* The five points are one tight group, matching how they're written (single
   line breaks, not blank lines): a smaller gap between them than the one under
   the lede, so the lede reads as the heading they hang off. */
.about-panel__points p { margin-bottom: var(--sp-2); }
.about-panel__points p:last-child { margin-bottom: 0; }

/* Opening line: full-contrast ink and a touch more air under it, so it reads
   as the summary the rest hangs off — no size jump, no decoration.
   Selector needs the element too: `.about-panel__body p` above is (0,1,1) and
   would otherwise outrank a lone class. */
.about-panel__body p.about-panel__lede {
  color: var(--c-ink);
  margin-bottom: var(--sp-4);
}
/* sits straight under the last point now that the signature squiggle is gone,
   so it needs a clear gap of its own rather than the old 14px that only had to
   clear the mark */
.about-panel__contacts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  margin-top: var(--sp-4);
}
.about-panel__contacts a {
  color: var(--c-ink);
  text-decoration: underline;
  text-underline-offset: 3px;
  opacity: .8;
  /* curve was omitted here too — see the note on .nav__link */
  transition: opacity var(--motion-hover);
}
.about-panel__contacts a:hover { opacity: 1; }

/* Mobile: plain underlined links are easy to miss (and hard to tap precisely)
   in a phone-width modal — same pill shape and background as the × close
   button, so they read as the modal's own set of controls. */
@media (max-width: 640px) {
  .about-panel__contacts a {
    display: inline-flex;
    align-items: center;
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-pill);
    background: var(--c-bg);
    text-decoration: none;
    opacity: 1;
    transition: background var(--motion-hover);
  }
  .about-panel__contacts a:hover { background: var(--c-line); }
}

/* Phone: fit the whole text on screen without scrolling.
   The copy is fixed and fairly long, so this is a width-and-rhythm problem
   rather than a font-size one — the padding is cut hardest (40px a side left
   only 262px of measure on a 375px screen, which is what was pushing the
   paragraphs to five and six lines), the panel takes a little more of the
   viewport, and only then does the type step down. Spacing between the
   points shrinks more than the type does: on a block this dense the gaps
   were costing more vertical room than the glyphs.

   Whether it all fits is content-dependent by nature. Overflow is not
   disabled anywhere — if the copy grows, the panel scrolls again rather than
   clipping. */
@media (max-width: 640px) {
  .about-panel {
    /* the modal's own inset, shared by the scroll box's padding and the close
       button's placement so the two can be aligned against each other */
    --about-pad: var(--space-6);
    --about-close-size: 32px;
    --about-title-line: 1.1;
    width: 96vw;
    /* now that the copy fits, let the panel end where the text ends instead
       of holding a fixed 90vh box with ~240px of blank below the links. The
       max-height keeps the scroll behaviour as the fallback if the copy ever
       outgrows the screen. */
    height: auto;
    max-height: min(94vh, 860px);
  }
  .about-panel__scroll {
    /* Per the reference sketch: a clear, even inset on all four sides rather
       than text running close to the rounded edge. The copy now fits with
       room to spare, so the space goes back into the frame. */
    padding: var(--about-pad) var(--space-6) var(--space-8);
    /* The base rule's height:100% needs a definite parent height, which the
       panel no longer has now that it sizes to its content — so on a screen
       too short for the copy the text ran straight past the panel's rounded
       bottom instead of scrolling (measured: 72px past, at 320×568). Giving
       the scroll box the same viewport cap as the panel keeps that fallback
       working: it stops growing at the panel's edge and scrolls inside. */
    height: auto;
    max-height: calc(min(94vh, 860px) - 2px);   /* less the panel's 1px border, top and bottom */
  }
  /* Centred on the title's line box rather than parked at a fixed offset from
     the panel's corner: the button and the heading are one row visually, and
     at their own sizes (32px button vs a ~22px line) a shared `top` left them
     5px out of line. Starts from the same --about-pad the text does, then
     shifts by half the height difference. */
  .about-panel__close {
    top: calc(var(--about-pad) + (var(--about-title-line) * 1em - var(--about-close-size)) / 2);
    right: var(--sp-2);
    width: var(--about-close-size);
    height: var(--about-close-size);
    font-size: var(--fs-300);          /* so the 1em above is the TITLE's size */
  }
  .about-panel__close svg { width: 16px; height: 16px; }

  /* line-height stated rather than inherited: the close button's vertical
     placement is derived from it (--about-title-line), so it can't be left to
     whatever the cascade happens to give this element. */
  .about-panel__title {
    font-size: var(--fs-300);
    line-height: var(--about-title-line, 1.1);
    margin-bottom: var(--sp-3);
  }
  .about-panel__body p {
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
    margin-bottom: var(--space-3);
    max-width: none;          /* the modal's own width is the measure here */
  }
  /* bold on the phone too, per the sketch — the desktop dropdown already
     does this (the min-width:768px block), the modal was the odd one out */
  .about-panel__body p.about-panel__lede { font-weight: 700; margin-bottom: var(--space-3); }
  .about-panel__points p { margin-bottom: var(--space-2); }
  /* the links are a separate block from the copy, not its last paragraph —
     they need a gap wider than the one between points to read that way */
  .about-panel__contacts { gap: var(--space-2); margin-top: var(--sp-4); }
  .about-panel__contacts a { padding: var(--space-2) var(--space-3); font-size: var(--text-xs); }
}

/* Short desktop screens (small laptops): with no internal scroll the panel
   must actually fit between the dock and the top of the screen, so trim the
   per-paragraph air until it does. */
@media (min-width: 768px) and (max-height: 760px) {
  .about-panel__scroll { padding: var(--space-4) var(--space-6) var(--space-8); }
  .about-panel__title { margin-bottom: var(--sp-2); }
  .about-panel__body p { margin-bottom: var(--space-2); }
  .about-panel__body p.about-panel__lede { margin-bottom: var(--space-3); }
  .about-panel__contacts { margin-top: var(--sp-2); }
}

@media (prefers-reduced-motion: reduce) {
  .about-panel { transition: opacity var(--dur) var(--ease-out); }
}
