/* Security BSides Dehradun 0x03
   apple.css — the "Evolve" premium re-theme (rebuild pass).
   ==========================================================================
   Loaded LAST on the page, after tokens/base/components/{page}.css. Selectors
   are deliberately UNLAYERED, so at equal specificity they beat the @layer
   rules they restyle without a specificity war. The original design system
   underneath is untouched and fully recoverable — remove this one <link> and
   the site is exactly as it shipped. We are on the `redesign` git branch, so
   base files are also recoverable there.

   WHY THIS PASS EXISTS: the first attempt only rounded corners, softened
   shadows and washed a gradient behind the hero. The verdict was that it
   "still feels like the old site", the "gradients look cheap", there is "not
   enough air", and the "shadows feel off". All four are the same root cause:
   the site's bones are dense Swiss editorial — left-aligned 12-column grids,
   hairline dividers on every seam, an ultra-compressed 0.88 line-height hero —
   and rounding corners on top of that does not read as Apple.

   So this pass RESTRUCTURES rather than decorates:
     • AIR         — bigger section rhythm; the hairline dividers that chopped
                     the page into editorial registers are softened toward pure
                     whitespace separation, the way Apple/Antigravity pages
                     breathe.
     • CENTRED HERO— the hero is rebuilt from a left-aligned 12-column split
                     into a centred, spacious, big-type stage: eyebrow, a
                     headline given room to breathe, one calm lead line, facts
                     as a centred row, buttons centred. This is the single
                     biggest "it looks like Apple now" lever.
     • FLAT CLEAN  — no gradient washes anywhere. Surfaces are flat. Cards lose
                     the border-AND-shadow busy-ness and become clean panels
                     with one soft, barely-there shadow, lifting on hover.
     • PILL FORMS  — buttons are Apple capsules; inputs round.
     • CALM MOTION — Apple's decelerating curve on the new transforms.

   COLOUR: inherited from tokens.css untouched (AA-pinned, dark mode flips
   there), so this follows light/dark for free. Blue stays action, red stays
   marker. We add only new material/shadow/radius tokens. */

/* ==========================================================================
   1. NEW TOKENS — additive only. Shadows are Apple-subtle: barely visible,
   felt not seen. Ink-hue tinted (265) so they sit warm on paper rather than
   as grey smudges — the "shadows feel off" fix is here: lighter and cleaner.
   ========================================================================== */
:root {
  --r-sm:   8px;
  --r-md:   12px;
  --r-lg:   18px;
  --r-xl:   24px;
  --r-2xl:  30px;

  /* Deliberately faint. Apple's tile shadows are almost subliminal in light
     mode; the depth comes from a clean surface floating on generous white,
     not from a dark halo. */
  --shadow-1:
    0 1px 2px oklch(20% 0.03 265 / 0.04),
    0 2px 4px oklch(20% 0.03 265 / 0.04);
  --shadow-2:
    0 2px 6px oklch(20% 0.03 265 / 0.05),
    0 6px 16px oklch(20% 0.03 265 / 0.06);
  --shadow-3:
    0 4px 10px oklch(20% 0.03 265 / 0.06),
    0 12px 28px oklch(20% 0.03 265 / 0.08);
  --shadow-4:
    0 8px 16px oklch(20% 0.03 265 / 0.07),
    0 24px 52px oklch(20% 0.03 265 / 0.11);

  --shadow-overlay:
    0 12px 24px oklch(18% 0.02 265 / 0.09),
    0 32px 64px oklch(18% 0.02 265 / 0.16);

  --glass-bg:        color-mix(in oklab, var(--paper) 72%, transparent);
  --glass-bg-strong: color-mix(in oklab, var(--paper) 86%, transparent);
  --glass-blur:      blur(20px) saturate(180%);
  --glass-hairline:  color-mix(in oklab, var(--ink) 7%, transparent);

  /* Apple's workhorse decelerating curve, for the small transforms this theme
     adds (button press, card lift). Never linear, never bouncy. */
  --ease-apple: cubic-bezier(0.25, 0.1, 0.25, 1);

  /* Breathing room for the rebuilt sections. Corrected: the first pass ran to
     11rem (cavernous voids), then over-corrected to ~6rem (cramped — "the
     margin change made it worse"). This is the Apple rhythm: generous, calm,
     ~72–128px, so sections read as air, not vacuum and not clutter. Now that
     the page carries a colour field and glass panels, this air reads as
     composure rather than emptiness. */
  --air-section: clamp(4.5rem, 7vw, 8rem);
  --air-hero:    clamp(5rem, 8vw, 9rem);

  scroll-behavior: smooth;
}

/* Dark mode: shadows need more presence on a dark ground; frost highlight
   brightens. Mirror tokens.css's two triggers exactly. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --shadow-1: 0 1px 2px oklch(0% 0 0 / 0.28), 0 2px 4px oklch(0% 0 0 / 0.28);
    --shadow-2: 0 2px 6px oklch(0% 0 0 / 0.34), 0 6px 16px oklch(0% 0 0 / 0.40);
    --shadow-3: 0 4px 10px oklch(0% 0 0 / 0.40), 0 12px 28px oklch(0% 0 0 / 0.48);
    --shadow-4: 0 8px 16px oklch(0% 0 0 / 0.44), 0 24px 52px oklch(0% 0 0 / 0.56);
    --shadow-overlay: 0 12px 24px oklch(0% 0 0 / 0.44), 0 32px 64px oklch(0% 0 0 / 0.60);
    --glass-hairline: color-mix(in oklab, var(--ink) 14%, transparent);
  }
}
:root[data-theme="dark"] {
  --shadow-1: 0 1px 2px oklch(0% 0 0 / 0.28), 0 2px 4px oklch(0% 0 0 / 0.28);
  --shadow-2: 0 2px 6px oklch(0% 0 0 / 0.34), 0 6px 16px oklch(0% 0 0 / 0.40);
  --shadow-3: 0 4px 10px oklch(0% 0 0 / 0.40), 0 12px 28px oklch(0% 0 0 / 0.48);
  --shadow-4: 0 8px 16px oklch(0% 0 0 / 0.44), 0 24px 52px oklch(0% 0 0 / 0.56);
  --shadow-overlay: 0 12px 24px oklch(0% 0 0 / 0.44), 0 32px 64px oklch(0% 0 0 / 0.60);
  --glass-hairline: color-mix(in oklab, var(--ink) 14%, transparent);
}

/* ==========================================================================
   2. AIR — the references separate sections with whitespace, not rules. Bump
   the vertical rhythm and soften the hairline dividers that made the page read
   as stacked editorial registers. This is most of the "not enough air" and
   "still feels like the old site" fix.
   ========================================================================== */
.section {
  padding-block: var(--air-section);
}
.section--tight {
  padding-block: calc(var(--air-section) * 0.6);
}

/* The ruled top-border on sections was the Swiss seam. Fade it to near-nothing
   so sections float on whitespace; a whisper of a line remains for structure. */
.section--ruled {
  border-block-start-color: color-mix(in oklab, var(--ink) 5%, transparent);
}

/* Give the section headings room to breathe before their content. */
.section-head {
  margin-block-end: clamp(2.5rem, 4vw, 4rem);
}

/* ==========================================================================
   2b. THE SKY — removed.
   ==========================================================================
   An earlier revision floated the whole page on a fixed blue→red light-key
   gradient (body::before). It was rejected. The hero now carries a live blue
   particle orb (§4b) as its own dark stage instead, and the rest of the page
   sits on the plain --paper ground it always had. No body::before, no
   viewport wallpaper, no content-lifting stacking rule (that last one was the
   nav bug). The `body::before { display:none }` guards in the accessibility
   media queries below are now harmless no-ops and are left as-is.
   ========================================================================== */

/* ==========================================================================
   3. TYPOGRAPHY — big and tight, the reference signature. Tighten display
   tracking a hair and lift eyebrow tracking toward the a.security/pi.security
   technical voice. No size/weight change to shared tokens (documented overflow
   risk); size lives in the hero rules below where it is scoped.
   ========================================================================== */
.display,
.hero__title,
.h1 {
  letter-spacing: -0.03em;
}

/* Wide-tracked mono eyebrows. Restricted to the short EYEBROW roles — NOT
   .link-mono (long emails/links overflow if tracked wide). */
.eyebrow,
.card__eyebrow,
.section-head__eyebrow {
  letter-spacing: 0.18em;
}

/* ==========================================================================
   4. HERO — rebuilt as a centred, spacious Apple stage. The base laid this out
   as a left-aligned 12-column editorial split (lead 5 / facts 3 / actions 2)
   under a hand-set two-line headline with a 0.88 line-height. That reads as a
   magazine cover, not a product page. We recompose it to a centred column with
   real breathing room, and give the headline air by loosening its line-height.
   The hand-set two-line rag is preserved — it just centres now.
   ========================================================================== */
.hero {
  /* ----------------------------------------------------------------------
     THEME VARS. The hero used to be dark-only. It now themes with the site:
     these are the LIGHT (default) values; the dark overrides live in a block
     right after this rule. Everything below reads var(--hero-*), so light
     and dark share one set of rules.

     Light mode: a soft blue-white sky the (recoloured, opaque) orb reads on,
     with dark ink and a *lightening* scrim + white glyph halo. Dark mode: the
     near-black stage the additive-glow orb needs, with light ink and a dark
     scrim + dark halo. The orb's own colours/blending flip in JS (§4b).
     ---------------------------------------------------------------------- */
  --hero-bg:
    radial-gradient(125% 95% at 50% 8%,
      oklch(0.99 0.006 250) 0%,
      oklch(0.95 0.022 250) 52%,
      oklch(0.905 0.034 255) 100%);
  --hero-title-ink: var(--ink);
  --hero-lead-ink: var(--ink-2);
  --hero-dt-ink: var(--ink-3);
  --hero-dd-ink: var(--ink);
  --hero-icon: var(--accent);
  --hero-cd-ink: var(--ink);
  --hero-cd-label: var(--ink-3);
  /* Light mode: dark ink reads on the light stage on its own, and the frosted
     scrim below does the heavy lifting over the particle core — so NO tight
     white outline (that "sticker" edge was the cheap-looking part). Only a
     whisper of soft, wide glow remains, purely to feather the type edges. */
  --hero-halo-1: transparent;
  --hero-halo-2: oklch(1 0 0 / 0.34);
  --hero-halo-sm-1: transparent;
  --hero-halo-sm-2: oklch(1 0 0 / 0.3);
  /* a soft *frosted* scrim behind the text column (see .shell::before). Very
     light now — dark ink already reads on the light stage, so this only takes
     the faintest edge off the orb directly under the words; the orb stays
     visible around and through it. */
  --hero-scrim-1: oklch(1 0 0 / 0.24);
  --hero-scrim-2: oklch(1 0 0 / 0.06);
  --hero-hairline: color-mix(in oklab, var(--ink) 14%, transparent);
  /* glass secondary button — tinted dark-ink glass on the light stage */
  --hero-glass-bg: color-mix(in oklab, var(--ink) 6%, transparent);
  --hero-glass-border: color-mix(in oklab, var(--ink) 20%, transparent);
  --hero-glass-ink: var(--ink);
  --hero-glass-bg-hover: color-mix(in oklab, var(--ink) 11%, transparent);
  --hero-glass-border-hover: color-mix(in oklab, var(--ink) 30%, transparent);
  --hero-glass-ink-hover: var(--ink);
  /* light-mode vignette: a soft brightening at the edges toward the light
     stage, so corners fall away to light, not dark */
  --hero-vignette:
    radial-gradient(circle at 50% 40%,
      transparent 34%,
      oklch(0.97 0.014 250 / 0.4) 74%,
      oklch(0.95 0.02 252 / 0.72) 100%);

  /* Tightened top only: trim the space above the .hero__top kicker row so the
     headline sits close under the nav. The BOTTOM stays at the base's 0 — the
     ridge (home.css) is "anchored to the bottom of the hero" and must sit flush
     against the section edge. An earlier build set a bottom pad here, which
     lifted the ridge off the floor and opened a dark band between the countdown
     and the terrain; keeping it 0 closes that gap. */
  padding-block-start: clamp(1.5rem, 3vw, 2.5rem);
  padding-block-end: 0;
  /* No text-align here: the base (home.css) lays the hero out as a left-aligned
     12-column split — lead far left, facts centre, buttons far right, countdown
     bottom-left. apple.css only re-keys the ink/halo for the dark orb stage, so
     the ORIGINAL placement stands. (A stray `text-align: center` here used to
     override all of that; removed.) */
  /* `isolation` + `overflow` contain the orb canvas and its glow to this
     section; the background is the themed stage the orb sits on. */
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--hero-bg);
  color: var(--hero-title-ink);
}

/* Dark overrides: the near-black stage the additive orb needs. Both the forced
   theme and the OS-dark (unless forced light) paths, mirroring tokens.css. */
:root[data-theme="dark"] .hero {
  --hero-bg:
    radial-gradient(125% 95% at 50% 8%,
      #0a0e1c 0%, #070a14 55%, #04060e 100%);
  --hero-title-ink: oklch(0.985 0.004 250);
  --hero-lead-ink: oklch(0.92 0.008 250);
  --hero-dt-ink: oklch(0.82 0.012 250);
  --hero-dd-ink: oklch(0.97 0.006 250);
  --hero-icon: oklch(0.78 0.11 235);
  --hero-cd-ink: oklch(0.99 0.004 250);
  --hero-cd-label: oklch(0.8 0.012 250);
  --hero-halo-1: oklch(0 0 0 / 0.65);
  --hero-halo-2: oklch(0 0 0 / 0.6);
  --hero-halo-sm-1: oklch(0 0 0 / 0.75);
  --hero-halo-sm-2: oklch(0 0 0 / 0.55);
  --hero-scrim-1: oklch(0.04 0.008 260 / 0.42);
  --hero-scrim-2: oklch(0.04 0.008 260 / 0.24);
  --hero-hairline: oklch(1 0 0 / 0.14);
  --hero-glass-bg: oklch(1 0 0 / 0.14);
  --hero-glass-border: oklch(1 0 0 / 0.32);
  --hero-glass-ink: oklch(0.98 0.005 250);
  --hero-glass-bg-hover: oklch(1 0 0 / 0.2);
  --hero-glass-border-hover: oklch(1 0 0 / 0.42);
  --hero-glass-ink-hover: oklch(1 0 0);
  --hero-vignette:
    radial-gradient(circle at 50% 40%,
      transparent 30%,
      oklch(0.06 0.01 260 / 0.55) 72%,
      oklch(0.03 0.008 260 / 0.92) 100%);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .hero {
    --hero-bg:
      radial-gradient(125% 95% at 50% 8%,
        #0a0e1c 0%, #070a14 55%, #04060e 100%);
    --hero-title-ink: oklch(0.985 0.004 250);
    --hero-lead-ink: oklch(0.92 0.008 250);
    --hero-dt-ink: oklch(0.82 0.012 250);
    --hero-dd-ink: oklch(0.97 0.006 250);
    --hero-icon: oklch(0.78 0.11 235);
    --hero-cd-ink: oklch(0.99 0.004 250);
    --hero-cd-label: oklch(0.8 0.012 250);
    --hero-halo-1: oklch(0 0 0 / 0.65);
    --hero-halo-2: oklch(0 0 0 / 0.6);
    --hero-halo-sm-1: oklch(0 0 0 / 0.75);
    --hero-halo-sm-2: oklch(0 0 0 / 0.55);
    --hero-scrim-1: oklch(0.04 0.008 260 / 0.42);
    --hero-scrim-2: oklch(0.04 0.008 260 / 0.24);
    --hero-hairline: oklch(1 0 0 / 0.14);
    --hero-glass-bg: oklch(1 0 0 / 0.14);
    --hero-glass-border: oklch(1 0 0 / 0.32);
    --hero-glass-ink: oklch(0.98 0.005 250);
    --hero-glass-bg-hover: oklch(1 0 0 / 0.2);
    --hero-glass-border-hover: oklch(1 0 0 / 0.42);
    --hero-glass-ink-hover: oklch(1 0 0);
    --hero-vignette:
      radial-gradient(circle at 50% 40%,
        transparent 30%,
        oklch(0.06 0.01 260 / 0.55) 72%,
        oklch(0.03 0.008 260 / 0.92) 100%);
  }
}

/* Headline: centred, wider measure. No top margin now — nothing sits above it,
   so any margin here is pure dead gap between the nav and the title, which is
   exactly what we were asked to remove. Light ink for the dark stage. */
/* Keep the base's left-aligned 12-column placement (home.css). We only re-key
   the INK for the dark orb stage and add a legibility halo — no layout here, so
   the title, lead, facts, buttons and countdown sit exactly where the base put
   them. */
.hero__title {
  color: var(--hero-title-ink);
  /* A halo per glyph so the type reads over the orb no matter what pixel is
     behind it — tight edge + wide soft glow. Colour flips with the theme
     (dark halo on the dark stage, white halo on the light one). */
  text-shadow:
    0 1px 2px var(--hero-halo-1),
    0 2px 30px var(--hero-halo-2);
}

/* Lead: base layout (grid-column 1/span 5) stays; only ink + halo change. */
.hero__lead {
  color: var(--hero-lead-ink);
  text-shadow:
    0 1px 2px var(--hero-halo-sm-1),
    0 1px 18px var(--hero-halo-sm-2);
}

/* --------------------------------------------------------------------------
   Dark-stage text. The hero is now dark (§4), so every descendant that was
   coloured with dark-ink tokens (--ink, --ink-2) or dark-ink color-mixes has
   to be re-keyed light or it disappears. Scoped to .hero so nothing else on
   the page is touched.
   -------------------------------------------------------------------------- */
.hero .hero__facts dt {
  color: var(--hero-dt-ink);
}
.hero .hero__facts dd {
  color: var(--hero-dd-ink);
}
.hero .hero__facts .icon {
  color: var(--hero-icon);   /* the blue accent, echoing the orb */
}
.hero .countdown__value {
  color: var(--hero-cd-ink);
}
.hero .countdown__label,
.hero .countdown__sep {
  color: var(--hero-cd-label);
}
/* Halo every smaller text run in the facts/countdown so it survives the orb
   the same way the headline does. */
.hero .hero__facts dt,
.hero .hero__facts dd,
.hero .countdown__value,
.hero .countdown__label,
.hero .countdown__sep {
  text-shadow:
    0 1px 2px var(--hero-halo-sm-1),
    0 1px 14px var(--hero-halo-sm-2);
}

/* The restored .hero__top kicker row (0x03 · Third edition | coordinate stamp).
   Its base colours (--ink-3 eyebrow, --ink-2 stamp, --ink-3 separator) are
   dark-on-light and would nearly vanish on the orb stage, so re-key them light
   exactly as the facts/countdown above. The eyebrow__num stays --brand (red),
   which already reads on dark. The row's hairline uses the same --hero-hairline
   token as .hero__stamp so both rules match, and it gets the same small halo. */
.hero .hero__top {
  border-block-end-color: var(--hero-hairline);
}
.hero .hero__top .eyebrow {
  color: var(--hero-dt-ink);
}
.hero .hero__top .meta-stamp {
  color: var(--hero-cd-ink);
}
.hero .hero__top .meta-stamp__sep {
  color: var(--hero-cd-label);
}
.hero .hero__top .eyebrow,
.hero .hero__top .meta-stamp {
  text-shadow:
    0 1px 2px var(--hero-halo-sm-1),
    0 1px 14px var(--hero-halo-sm-2);
}

/* --------------------------------------------------------------------------
   Content scrim. The orb's bright core sits dead-centre, right where the text
   column is. Halos handle each glyph; this adds a soft, wide darkening behind
   the whole content column so the baseline under the text is darker too — it
   mutes the core a little but the orb still glows around the column's edges.
   Lives BETWEEN the orb and the text: .shell is lifted to its own layer, the
   scrim is its ::before one step behind, the orb is further back at z-index:-1
   in the hero. Scoped to the hero's own shell only. */
.hero > .shell {
  position: relative;
  z-index: 1;
}
.hero > .shell::before {
  content: "";
  position: absolute;
  inset: -8% -12%;
  z-index: -1;
  pointer-events: none;
  /* A gentle calming of the ground DIRECTLY under the words only — tight and
     narrow so the orb's core and rim stay visible around and between the text
     lines instead of being wiped by a big blurred disc. The radial fades to
     nothing well within the column, so there's no rectangle and no wide veil
     over the orb. */
  background:
    radial-gradient(ellipse 46% 42% at 50% 44%,
      var(--hero-scrim-1) 0%,
      var(--hero-scrim-2) 46%,
      transparent 70%);
  -webkit-mask-image:
    radial-gradient(ellipse 46% 42% at 50% 44%,
      #000 0%, #000 44%, transparent 72%);
  mask-image:
    radial-gradient(ellipse 46% 42% at 50% 44%,
      #000 0%, #000 44%, transparent 72%);
}
/* A whisper of frost — just enough to settle the type onto the orb without
   turning the orb into a blurred smear. Much lighter than before so the
   particle sphere still reads through it. */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .hero > .shell::before {
    -webkit-backdrop-filter: blur(2.5px) saturate(112%);
    backdrop-filter: blur(2.5px) saturate(112%);
  }
}

/* Secondary "Submit a talk" button: a real glass chip so it reads as a
   sibling action to the primary, not a faint outline. Frosted fill, a defined
   hairline, an inset top highlight for the lit-glass edge, and a soft lift —
   all themed via --hero-glass-*. This is the visibility fix. */
.hero .btn--secondary {
  background: var(--hero-glass-bg);
  border-color: var(--hero-glass-border);
  color: var(--hero-glass-ink);
  box-shadow:
    0 1px 2px oklch(0 0 0 / 0.18),
    inset 0 1px 0 oklch(1 0 0 / 0.22);
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .hero .btn--secondary {
    -webkit-backdrop-filter: blur(10px) saturate(150%);
    backdrop-filter: blur(10px) saturate(150%);
  }
}
@media (hover: hover) and (pointer: fine) {
  .hero .btn--secondary:hover {
    background: var(--hero-glass-bg-hover);
    border-color: var(--hero-glass-border-hover);
    color: var(--hero-glass-ink-hover);
  }
}

/* Facts and buttons keep their base placement (facts in the middle columns,
   buttons in the right column). No layout override — only the countdown hairline
   is re-keyed to the light-on-dark tone so it reads on the orb stage. */
.hero__stamp {
  border-block-start-color: var(--hero-hairline);
}

/* ==========================================================================
   4b. THE ORB — a live blue particle sphere behind the hero.
   ==========================================================================
   Replaces the rejected sky gradient. This is the scroll animation from the
   supplied reference (Three.js particle orb + dust halo + bloom), recoloured
   green → blue and adapted to live INSIDE the hero instead of hijacking the
   whole page:

     • The reference made <body> the scroll track: a position:fixed full-
       viewport hero + a 220vh spacer + global Lenis smooth-scroll. That would
       fight our sticky header and the site's own scroll-reveal. So the bundle's
       bootstrap is rewritten (assets/js/nimbus-orb.js) to size the canvas to
       the .hero box, drive uDispersion from the hero's OWN scroll offset, and
       run its own rAF loop — no Lenis, no body takeover.
     • uDispersion goes 0→1 across the first viewport of scroll, so the orb
       blooms while the hero is in view and disperses into dust as you scroll
       into the (light) content below — which doubles as the dark→light handoff.

   The canvas is decorative and aria-hidden; if WebGL is unavailable or the
   module never loads, the hero is just a dark stage with its text, fully
   legible. The orb never carries meaning.
   ========================================================================== */
.hero__orb {
  position: absolute;
  inset: 0;
  z-index: -1;              /* behind .shell content, inside the hero's isolate */
  pointer-events: none;     /* re-enabled by JS only for the hover parallax */
  display: block;
}
.hero__orb-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
/* Vignette: in dark mode it darkens the edges so the orb reads as a light
   source in a dark room; in light mode it fades the edges to the light stage
   instead (a bright vignette), keeping the orb the focus without muddying the
   light ground. Theme-driven via --hero-vignette (default = light). */
.hero__orb-vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: var(--hero-vignette);
}
/* Reduced motion: the orb's whole point is the animation. Respect the setting
   by hiding the canvas entirely — the dark stage + text stand alone. The JS
   also checks this and never starts the loop, but the CSS guarantees it even
   if the script runs. */
@media (prefers-reduced-motion: reduce) {
  .hero__orb-canvas { display: none; }
}

/* --------------------------------------------------------------------------
   PHONE — the orb becomes the full-width backdrop (JS pulls the camera back so
   the sphere fills the narrow column). Two things have to give so it reads:

   1. VIGNETTE. On desktop the vignette darkens/fades the far edges to frame the
      orb as a light source. On a narrow phone the orb's rim now reaches those
      edges, so the vignette was biting into the glow and dimming the sphere.
      Ease it right back here so the orb's outer glow survives to the edges.

   2. SCRIM. The frosted disc behind the copy (.shell::before) is sized for the
      desktop text column; on a phone it spread as a wide blurred veil across
      the whole orb ("the full part"). Pull it in tight and drop the blur so it
      sits just behind the stacked text — the per-glyph halos already carry most
      of the legibility — and the orb core reads clean everywhere else.
   Scoped to the home hero, phone width only; desktop/tablet keep their look. */
@media (max-width: 40rem) {
  /* 1 — let the orb glow reach the edges (both themes). */
  .hero__orb-vignette { opacity: 0.4; }

  /* 2 — blur hugs the text, not the orb. Narrower + shorter ellipse, softer
     fill, and a barely-there blur so the sphere shows through around the copy. */
  .hero > .shell::before {
    inset: 0;
    background:
      radial-gradient(ellipse 62% 30% at 50% 46%,
        var(--hero-scrim-1) 0%,
        var(--hero-scrim-2) 52%,
        transparent 74%);
    -webkit-mask-image:
      radial-gradient(ellipse 62% 30% at 50% 46%,
        #000 0%, #000 50%, transparent 76%);
    mask-image:
      radial-gradient(ellipse 62% 30% at 50% 46%,
        #000 0%, #000 50%, transparent 76%);
  }
  @supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .hero > .shell::before {
      -webkit-backdrop-filter: blur(1px) saturate(108%);
      backdrop-filter: blur(1px) saturate(108%);
    }
  }
}

/* ==========================================================================
   4c. PHOTO PLATES — round the frames, drop the captions.
   ==========================================================================
   The base `.plate` figure is a crisp 2px-cornered rectangle (base tokens run
   sharp). Everywhere else the premium layer rounds its material — cards at
   --r-xl, card media to match — so a hard-edged photograph reads as the one
   thing that never got the treatment. Round the frame to the same family so a
   photo sits in the page like every other surface.

   The box already clips with overflow:hidden, so rounding it rounds the image;
   we round the <img> too as belt-and-braces against the 1px corner bleed some
   engines show when a filled child meets a rounded, clipped parent. --bleed
   plates drop their border in the base but still want the same corner. */
.plate__box {
  border-radius: var(--r-lg);
}
/* Set the value directly (not `inherit`): most plate images are wrapped in a
   <picture>, so an <img>'s parent is that <picture>, not the rounded box —
   inherit would resolve to 0 there. The value matches the box either way. */
.plate__img,
.plate__box > img {
  border-radius: var(--r-lg);
}

/* Captions off on the home page. apple.css only loads here, so this is scoped
   to home by construction — the deeper pages keep their captions. Safe for
   assistive tech: every plate carries real alt text, so hiding the visible
   caption removes a duplicate label, not the only one. */
.plate__caption {
  display: none;
}

/* ==========================================================================
   5. BUTTONS — Apple capsules with a clean press. Primary keeps its accent
   fill; secondary becomes a tinted "material" rather than a bare hairline.
   ========================================================================== */
.btn {
  border-radius: var(--r-pill);
  transition:
    background-color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease),
    box-shadow var(--dur) var(--ease-apple),
    scale var(--dur-fast) var(--ease-apple);
}
.btn:active { translate: none; }
.btn--primary:active,
.btn--secondary:active,
.btn--on-accent:active,
.btn--ghost:active { scale: 0.97; }

.btn--primary {
  box-shadow: var(--shadow-1), inset 0 1px 0 oklch(100% 0 0 / 0.16);
}
@media (hover: hover) and (pointer: fine) {
  .btn--primary:hover {
    box-shadow: var(--shadow-2), inset 0 1px 0 oklch(100% 0 0 / 0.18);
  }
}
.btn--primary:active { box-shadow: var(--shadow-1); }

.btn--secondary {
  background: color-mix(in oklab, var(--ink) 5%, transparent);
  border-color: color-mix(in oklab, var(--ink) 9%, transparent);
  box-shadow: none;
}
@media (hover: hover) and (pointer: fine) {
  .btn--secondary:hover {
    background: color-mix(in oklab, var(--ink) 9%, transparent);
    border-color: color-mix(in oklab, var(--ink) 14%, transparent);
    color: var(--ink);
  }
}

.btn[aria-disabled="true"] {
  border-radius: var(--r-pill);
  box-shadow: none;
}

/* ==========================================================================
   6. CARDS — clean floating panels. The base drew a border AND a top-edge
   accent rule; stacking a border, a shadow and a drawn rule reads busy, not
   premium. Here a card is a rounded surface with NO visible border and one
   soft shadow, lifting on hover. This is the "flat clean Apple tile" look and
   the "shadows feel off" fix together.
   ========================================================================== */
.card {
  border-radius: var(--r-xl);
  /* Fallback for browsers without backdrop-filter: a nearly-solid raised
     surface, so text contrast never depends on the blur being applied. */
  background: color-mix(in oklab, var(--paper-raised) 90%, transparent);
  border: var(--bw) solid var(--glass-hairline);
  /* Float + a bright inner top-edge: Apple's frosted tiles catch light on
     their upper rim. The inset highlight is what makes the panel read as a
     physical pane of glass rather than a flat translucent rectangle. */
  box-shadow:
    var(--shadow-2),
    inset 0 1px 0 oklch(100% 0 0 / 0.5);
  transition:
    box-shadow var(--dur) var(--ease-apple),
    border-color var(--dur) var(--ease),
    translate var(--dur) var(--ease-apple);
}
.card::after { display: none; }

/* The frosted glass itself — translucent surface + blur, so the sky refracts
   through every card. THIS is the glassmorphism. Gated behind @supports so
   only browsers that can blur get the translucency; the rest keep the
   near-solid fallback above and stay perfectly legible. */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .card {
    background: color-mix(in oklab, var(--paper-raised) 58%, transparent);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
  }
}

@media (hover: hover) and (pointer: fine) {
  .card:has(a:hover),
  .card--link:hover {
    box-shadow: var(--shadow-4), inset 0 1px 0 oklch(100% 0 0 / 0.6);
    translate: 0 -4px;
  }
}
.card:has(a:active),
.card--link:active {
  box-shadow: var(--shadow-2), inset 0 1px 0 oklch(100% 0 0 / 0.5);
  translate: 0 -1px;
}
@media (hover: hover) and (pointer: fine) {
  .card:has(a:hover),
  .card--link:hover {
    border-color: color-mix(in oklab, var(--ink) 12%, transparent);
  }
}

/* Glass on the other floating surfaces — the nav dropdown and the empty-state
   panels — so the frosted material reads as a system, not a one-off. */
.empty-state {
  border-radius: var(--r-xl);
  border: var(--bw) solid var(--glass-hairline);
  background: color-mix(in oklab, var(--paper-raised) 90%, transparent);
  box-shadow: var(--shadow-2), inset 0 1px 0 oklch(100% 0 0 / 0.5);
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .empty-state {
    background: color-mix(in oklab, var(--paper-raised) 58%, transparent);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
  }
}

/* The nav dropdown is deliberately NOT glass. Its ::details-content ancestor
   carries a `translate` (the drop-in animation), and a transform on an ancestor
   breaks backdrop-filter sampling — so glass there renders as a half-transparent
   panel with sharp text bleeding through from the hero behind it. An overlay
   floating over arbitrary scrolling content also simply must be opaque to be
   legible. So we keep the base's solid --paper-raised panel and only round its
   corners to match the theme's radius. */
.nav-group__panel {
  border-radius: var(--r-md);
}

/* Media that bleeds to the card edge must round with the card. */
.card__plate,
.card__plate img,
.card__media,
.card__media img {
  border-radius: calc(var(--r-xl) - var(--card-pad, 0px));
  overflow: clip;
}

/* ==========================================================================
   7. HEADER — Apple's frosted material; float it on scroll.
   ========================================================================== */
@supports (backdrop-filter: blur(1px)) {
  .site-header::before {
    background: var(--glass-bg);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
  }
  .site-header[data-scrolled]::before {
    background: var(--glass-bg-strong);
  }
}
.site-header::before {
  box-shadow: inset 0 1px 0 var(--glass-hairline);
}
.site-header[data-scrolled] {
  box-shadow: var(--shadow-2);
}

/* ==========================================================================
   7b. FOOTER OVERFLOW GUARD — pre-existing, the premium finish shouldn't ship a
   horizontal scrollbar. The footer contact column is a narrow track, and
   .site-footer__mailto is a flex row [mail icon · email · JS-injected Copy].
   The long monospace address plus the Copy button is a hair wider than the
   track at tight widths, so the button escapes past the viewport edge. Let the
   row wrap, and let the address itself break as a last resort on very narrow
   screens. Layout, not theme, but lives here to keep base files recoverable.
   ========================================================================== */
.site-footer__mailto {
  flex-wrap: wrap;
}
.site-footer__email {
  overflow-wrap: anywhere;
}

/* Desktop email alignment + icon removal. The contact column is only
   `11 / span 2` (~160–190px across desktop widths), narrower than the ~198px
   mono address, so the overflow guard above broke the address mid-word
   (…co / m) — the exact misalignment in the footer. Two fixes:
     • Drop the decorative (already aria-hidden) mail icon so the row is just
       the address — the icon also stole ~28px the narrow track couldn't spare.
     • Widen contact to 3 tracks (nav gives one back). This is the same span
       the base already uses just below 75rem, so nav still holds all four
       groups on one row, and the 3-track contact (~250–300px) fits the
       address on a single line with room to spare.
   Scoped 1px above the base's own 75rem footer breakpoint so tablet/mobile —
   where the column is already full/near-full width — are untouched. Layout,
   not theme, but lives here to keep base files recoverable. */
.site-footer__mailto .icon {
  display: none;
}
@media (min-width: 75.0625rem) {
  .site-footer__nav     { grid-column: 4 / span 6; }
  .site-footer__contact { grid-column: 10 / span 3; }
}

/* ==========================================================================
   8. FORM CONTROLS — rounded to match the capsules.
   ========================================================================== */
.field__input,
.field__control,
input[type="text"],
input[type="email"],
input[type="url"],
input[type="tel"],
input[type="search"],
input[type="number"],
textarea,
select {
  border-radius: var(--r-md);
}

/* ==========================================================================
   8b. PROGRESSIVE DISCLOSURE — the density fix. apple.js collapses any
   [data-disclose] block and inserts a .disclose__toggle before it. The page
   then reads sparse (headline + lead + a quiet toggle), and the supporting
   prose/tables/lists open on click. Content is visible by default; the
   collapsed state is added by JS, so nothing hides if the script never runs.
   ========================================================================== */

/* Collapsed: the panel is removed from flow. (display:none also keeps its
   focusable children out of the tab order while hidden, which is correct.) */
.disclose__panel.is-collapsed {
  display: none;
}

/* The toggle — a calm, premium pill. Mono label in the site's technical voice
   (the security-site tone), accent-blue so it reads as the actionable thing it
   is (colour grammar preserved), on a faint tinted material like the secondary
   button. Sits centred under the section head. */
.disclose__toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  margin-block-start: clamp(1.5rem, 3vw, 2.25rem);
  padding: 0.7em 1.25em;
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.02em;
  color: var(--accent);
  background: color-mix(in oklab, var(--ink) 4%, transparent);
  border: var(--bw) solid color-mix(in oklab, var(--ink) 9%, transparent);
  border-radius: var(--r-pill);
  cursor: pointer;
  transition:
    background-color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease),
    box-shadow var(--dur) var(--ease-apple),
    scale var(--dur-fast) var(--ease-apple);
}
@media (hover: hover) and (pointer: fine) {
  .disclose__toggle:hover {
    background: color-mix(in oklab, var(--ink) 7%, transparent);
    border-color: color-mix(in oklab, var(--accent) 40%, transparent);
    box-shadow: var(--shadow-1);
  }
}
.disclose__toggle:active { scale: 0.97; }

/* Chevron drawn in CSS (no icon-sprite dependency); rotates when open. */
.disclose__toggle-chevron {
  inline-size: 0.5em;
  block-size: 0.5em;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  rotate: 45deg;                 /* points down: "expand" */
  translate: 0 -0.12em;
  transition: rotate var(--dur) var(--ease-apple);
}
.disclose__toggle.is-open .disclose__toggle-chevron {
  rotate: -135deg;               /* points up: "collapse" */
  translate: 0 0.12em;
}

/* One-shot entrance when a panel opens: a soft fade+rise, Apple-calm. */
@keyframes disclose-in {
  from { opacity: 0; translate: 0 0.75rem; }
  to   { opacity: 1; translate: 0 0; }
}
.disclose__panel.is-revealing {
  animation: disclose-in var(--dur) var(--ease-out) both;
}

/* ==========================================================================
   9. ACCESSIBILITY GUARDS
   ========================================================================== */

/* Reduced motion: this theme adds transforms + smooth scroll the base token
   zeroing does not cover. Remove them. */
@media (prefers-reduced-motion: reduce) {
  :root { scroll-behavior: auto; }
  .btn--primary:active,
  .btn--secondary:active,
  .btn--on-accent:active,
  .btn--ghost:active { scale: 1; }
  .card:has(a:hover),
  .card--link:hover,
  .card:has(a:active),
  .card--link:active { translate: 0; }
  .btn, .card { transition-duration: 1ms; }
  .disclose__toggle:active { scale: 1; }
  .disclose__toggle-chevron { transition-duration: 1ms; }
  .disclose__panel.is-revealing { animation: none; }
}

/* Reduced transparency: swap every glass surface for a solid one, and drop the
   ambient light field. The frost is decoration; the content must still read. */
@media (prefers-reduced-transparency: reduce) {
  body::before { display: none; }
  .site-header::before { background: var(--paper); }
  .card,
  .empty-state { background: var(--paper-raised); }
  .nav-group__panel { background: var(--paper-raised); }
  @supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .site-header::before,
    .card,
    .empty-state,
    .nav-group__panel {
      -webkit-backdrop-filter: none;
      backdrop-filter: none;
    }
    .site-header[data-scrolled]::before { background: var(--paper); }
    .card,
    .empty-state { background: var(--paper-raised); }
    .nav-group__panel { background: var(--paper-raised); }
  }
}

/* More contrast: firm the softened lines back up, and solidify glass so text
   never sits on a translucent ground. */
@media (prefers-contrast: more) {
  .card,
  .empty-state { border-color: var(--rule-strong); background: var(--paper-raised); }
  .btn--secondary { border-color: var(--rule-strong); }
  .section--ruled { border-block-start-color: var(--rule); }
  .hero__stamp { border-block-start-color: var(--rule); }
  body::before { display: none; }
  @supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .card,
    .empty-state {
      -webkit-backdrop-filter: none;
      backdrop-filter: none;
    }
  }
}
